Retool the backup process to use a new 'BackupAgent' class
Backups will be handled by launching the application in a special mode under which no activities or services will be started, only the BackupAgent subclass named in the app's android:backupAgent manifest property. This takes the place of the BackupService class used earlier during development. In the cases of *full* backup or restore, an application that does not supply its own BackupAgent will be launched in a restricted manner; in particular, it will be using the default Application class rather than any manifest-declared one. This ensures that the app is not running any code that may try to manipulate its data while the backup system reads/writes its data set.
This commit is contained in:
@ -70,6 +70,7 @@ LOCAL_SRC_FILES += \
|
|||||||
core/java/android/app/IActivityPendingResult.aidl \
|
core/java/android/app/IActivityPendingResult.aidl \
|
||||||
core/java/android/app/IActivityWatcher.aidl \
|
core/java/android/app/IActivityWatcher.aidl \
|
||||||
core/java/android/app/IAlarmManager.aidl \
|
core/java/android/app/IAlarmManager.aidl \
|
||||||
|
core/java/android/app/IBackupAgent.aidl \
|
||||||
core/java/android/app/IInstrumentationWatcher.aidl \
|
core/java/android/app/IInstrumentationWatcher.aidl \
|
||||||
core/java/android/app/IIntentReceiver.aidl \
|
core/java/android/app/IIntentReceiver.aidl \
|
||||||
core/java/android/app/IIntentSender.aidl \
|
core/java/android/app/IIntentSender.aidl \
|
||||||
@ -82,7 +83,6 @@ LOCAL_SRC_FILES += \
|
|||||||
core/java/android/app/IWallpaperService.aidl \
|
core/java/android/app/IWallpaperService.aidl \
|
||||||
core/java/android/app/IWallpaperServiceCallback.aidl \
|
core/java/android/app/IWallpaperServiceCallback.aidl \
|
||||||
core/java/android/backup/IBackupManager.aidl \
|
core/java/android/backup/IBackupManager.aidl \
|
||||||
core/java/android/backup/IBackupService.aidl \
|
|
||||||
core/java/android/bluetooth/IBluetoothA2dp.aidl \
|
core/java/android/bluetooth/IBluetoothA2dp.aidl \
|
||||||
core/java/android/bluetooth/IBluetoothDevice.aidl \
|
core/java/android/bluetooth/IBluetoothDevice.aidl \
|
||||||
core/java/android/bluetooth/IBluetoothDeviceCallback.aidl \
|
core/java/android/bluetooth/IBluetoothDeviceCallback.aidl \
|
||||||
|
@ -2077,6 +2077,17 @@
|
|||||||
visibility="public"
|
visibility="public"
|
||||||
>
|
>
|
||||||
</field>
|
</field>
|
||||||
|
<field name="allowBackup"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="16843393"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
<field name="allowClearUserData"
|
<field name="allowClearUserData"
|
||||||
type="int"
|
type="int"
|
||||||
transient="false"
|
transient="false"
|
||||||
@ -2308,6 +2319,17 @@
|
|||||||
visibility="public"
|
visibility="public"
|
||||||
>
|
>
|
||||||
</field>
|
</field>
|
||||||
|
<field name="backupAgent"
|
||||||
|
type="int"
|
||||||
|
transient="false"
|
||||||
|
volatile="false"
|
||||||
|
value="16843392"
|
||||||
|
static="true"
|
||||||
|
final="true"
|
||||||
|
deprecated="not deprecated"
|
||||||
|
visibility="public"
|
||||||
|
>
|
||||||
|
</field>
|
||||||
<field name="baselineAlignBottom"
|
<field name="baselineAlignBottom"
|
||||||
type="int"
|
type="int"
|
||||||
transient="false"
|
transient="false"
|
||||||
@ -3507,28 +3529,6 @@
|
|||||||
visibility="public"
|
visibility="public"
|
||||||
>
|
>
|
||||||
</field>
|
</field>
|
||||||
<field name="donut_resource_pad31"
|
|
||||||
type="int"
|
|
||||||
transient="false"
|
|
||||||
volatile="false"
|
|
||||||
value="16843393"
|
|
||||||
static="true"
|
|
||||||
final="true"
|
|
||||||
deprecated="not deprecated"
|
|
||||||
visibility="public"
|
|
||||||
>
|
|
||||||
</field>
|
|
||||||
<field name="donut_resource_pad32"
|
|
||||||
type="int"
|
|
||||||
transient="false"
|
|
||||||
volatile="false"
|
|
||||||
value="16843392"
|
|
||||||
static="true"
|
|
||||||
final="true"
|
|
||||||
deprecated="not deprecated"
|
|
||||||
visibility="public"
|
|
||||||
>
|
|
||||||
</field>
|
|
||||||
<field name="donut_resource_pad4"
|
<field name="donut_resource_pad4"
|
||||||
type="int"
|
type="int"
|
||||||
transient="false"
|
transient="false"
|
||||||
|
@ -20,6 +20,7 @@ import android.content.ComponentName;
|
|||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.ConfigurationInfo;
|
import android.content.pm.ConfigurationInfo;
|
||||||
import android.content.pm.IPackageDataObserver;
|
import android.content.pm.IPackageDataObserver;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
@ -1021,6 +1022,33 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM
|
|||||||
reply.writeStrongBinder(binder);
|
reply.writeStrongBinder(binder);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case START_BACKUP_AGENT_TRANSACTION: {
|
||||||
|
data.enforceInterface(IActivityManager.descriptor);
|
||||||
|
ApplicationInfo info = ApplicationInfo.CREATOR.createFromParcel(data);
|
||||||
|
int backupRestoreMode = data.readInt();
|
||||||
|
boolean success = bindBackupAgent(info, backupRestoreMode);
|
||||||
|
reply.writeNoException();
|
||||||
|
reply.writeInt(success ? 1 : 0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case BACKUP_AGENT_CREATED_TRANSACTION: {
|
||||||
|
data.enforceInterface(IActivityManager.descriptor);
|
||||||
|
String packageName = data.readString();
|
||||||
|
IBinder agent = data.readStrongBinder();
|
||||||
|
backupAgentCreated(packageName, agent);
|
||||||
|
reply.writeNoException();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
case UNBIND_BACKUP_AGENT_TRANSACTION: {
|
||||||
|
data.enforceInterface(IActivityManager.descriptor);
|
||||||
|
ApplicationInfo info = ApplicationInfo.CREATOR.createFromParcel(data);
|
||||||
|
unbindBackupAgent(info);
|
||||||
|
reply.writeNoException();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.onTransact(code, data, reply, flags);
|
return super.onTransact(code, data, reply, flags);
|
||||||
@ -1681,6 +1709,43 @@ class ActivityManagerProxy implements IActivityManager
|
|||||||
return binder;
|
return binder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean bindBackupAgent(ApplicationInfo app, int backupRestoreMode)
|
||||||
|
throws RemoteException {
|
||||||
|
Parcel data = Parcel.obtain();
|
||||||
|
Parcel reply = Parcel.obtain();
|
||||||
|
data.writeInterfaceToken(IActivityManager.descriptor);
|
||||||
|
app.writeToParcel(data, 0);
|
||||||
|
data.writeInt(backupRestoreMode);
|
||||||
|
mRemote.transact(START_BACKUP_AGENT_TRANSACTION, data, reply, 0);
|
||||||
|
reply.readException();
|
||||||
|
boolean success = reply.readInt() != 0;
|
||||||
|
reply.recycle();
|
||||||
|
data.recycle();
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException {
|
||||||
|
Parcel data = Parcel.obtain();
|
||||||
|
Parcel reply = Parcel.obtain();
|
||||||
|
data.writeInterfaceToken(IActivityManager.descriptor);
|
||||||
|
data.writeString(packageName);
|
||||||
|
data.writeStrongBinder(agent);
|
||||||
|
mRemote.transact(BACKUP_AGENT_CREATED_TRANSACTION, data, reply, 0);
|
||||||
|
reply.recycle();
|
||||||
|
data.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unbindBackupAgent(ApplicationInfo app) throws RemoteException {
|
||||||
|
Parcel data = Parcel.obtain();
|
||||||
|
Parcel reply = Parcel.obtain();
|
||||||
|
data.writeInterfaceToken(IActivityManager.descriptor);
|
||||||
|
app.writeToParcel(data, 0);
|
||||||
|
mRemote.transact(UNBIND_BACKUP_AGENT_TRANSACTION, data, reply, 0);
|
||||||
|
reply.readException();
|
||||||
|
reply.recycle();
|
||||||
|
data.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
public boolean startInstrumentation(ComponentName className, String profileFile,
|
public boolean startInstrumentation(ComponentName className, String profileFile,
|
||||||
int flags, Bundle arguments, IInstrumentationWatcher watcher)
|
int flags, Bundle arguments, IInstrumentationWatcher watcher)
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
|
@ -115,6 +115,7 @@ public final class ActivityThread {
|
|||||||
private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
|
private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
|
||||||
private static final boolean DEBUG_BROADCAST = false;
|
private static final boolean DEBUG_BROADCAST = false;
|
||||||
private static final boolean DEBUG_RESULTS = false;
|
private static final boolean DEBUG_RESULTS = false;
|
||||||
|
private static final boolean DEBUG_BACKUP = true;
|
||||||
private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
|
private static final long MIN_TIME_BETWEEN_GCS = 5*1000;
|
||||||
private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
|
private static final Pattern PATTERN_SEMICOLON = Pattern.compile(";");
|
||||||
private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
|
private static final int SQLITE_MEM_RELEASED_EVENT_LOG_TAG = 75003;
|
||||||
@ -499,7 +500,7 @@ public final class ActivityThread {
|
|||||||
return mResources;
|
return mResources;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Application makeApplication() {
|
public Application makeApplication(boolean forceDefaultAppClass) {
|
||||||
if (mApplication != null) {
|
if (mApplication != null) {
|
||||||
return mApplication;
|
return mApplication;
|
||||||
}
|
}
|
||||||
@ -507,7 +508,7 @@ public final class ActivityThread {
|
|||||||
Application app = null;
|
Application app = null;
|
||||||
|
|
||||||
String appClass = mApplicationInfo.className;
|
String appClass = mApplicationInfo.className;
|
||||||
if (appClass == null) {
|
if (forceDefaultAppClass || (appClass == null)) {
|
||||||
appClass = "android.app.Application";
|
appClass = "android.app.Application";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1199,6 +1200,16 @@ public final class ActivityThread {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class CreateBackupAgentData {
|
||||||
|
ApplicationInfo appInfo;
|
||||||
|
int backupMode;
|
||||||
|
public String toString() {
|
||||||
|
return "CreateBackupAgentData{appInfo=" + appInfo
|
||||||
|
+ " backupAgent=" + appInfo.backupAgentName
|
||||||
|
+ " mode=" + backupMode + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final class CreateServiceData {
|
private static final class CreateServiceData {
|
||||||
IBinder token;
|
IBinder token;
|
||||||
ServiceInfo info;
|
ServiceInfo info;
|
||||||
@ -1239,6 +1250,7 @@ public final class ActivityThread {
|
|||||||
Bundle instrumentationArgs;
|
Bundle instrumentationArgs;
|
||||||
IInstrumentationWatcher instrumentationWatcher;
|
IInstrumentationWatcher instrumentationWatcher;
|
||||||
int debugMode;
|
int debugMode;
|
||||||
|
boolean restrictedBackupMode;
|
||||||
Configuration config;
|
Configuration config;
|
||||||
boolean handlingProfiling;
|
boolean handlingProfiling;
|
||||||
public String toString() {
|
public String toString() {
|
||||||
@ -1374,6 +1386,21 @@ public final class ActivityThread {
|
|||||||
queueOrSendMessage(H.RECEIVER, r);
|
queueOrSendMessage(H.RECEIVER, r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final void scheduleCreateBackupAgent(ApplicationInfo app, int backupMode) {
|
||||||
|
CreateBackupAgentData d = new CreateBackupAgentData();
|
||||||
|
d.appInfo = app;
|
||||||
|
d.backupMode = backupMode;
|
||||||
|
|
||||||
|
queueOrSendMessage(H.CREATE_BACKUP_AGENT, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void scheduleDestroyBackupAgent(ApplicationInfo app) {
|
||||||
|
CreateBackupAgentData d = new CreateBackupAgentData();
|
||||||
|
d.appInfo = app;
|
||||||
|
|
||||||
|
queueOrSendMessage(H.DESTROY_BACKUP_AGENT, d);
|
||||||
|
}
|
||||||
|
|
||||||
public final void scheduleCreateService(IBinder token,
|
public final void scheduleCreateService(IBinder token,
|
||||||
ServiceInfo info) {
|
ServiceInfo info) {
|
||||||
CreateServiceData s = new CreateServiceData();
|
CreateServiceData s = new CreateServiceData();
|
||||||
@ -1419,7 +1446,7 @@ public final class ActivityThread {
|
|||||||
ApplicationInfo appInfo, List<ProviderInfo> providers,
|
ApplicationInfo appInfo, List<ProviderInfo> providers,
|
||||||
ComponentName instrumentationName, String profileFile,
|
ComponentName instrumentationName, String profileFile,
|
||||||
Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
|
Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
|
||||||
int debugMode, Configuration config,
|
int debugMode, boolean isRestrictedBackupMode, Configuration config,
|
||||||
Map<String, IBinder> services) {
|
Map<String, IBinder> services) {
|
||||||
Process.setArgV0(processName);
|
Process.setArgV0(processName);
|
||||||
|
|
||||||
@ -1437,6 +1464,7 @@ public final class ActivityThread {
|
|||||||
data.instrumentationArgs = instrumentationArgs;
|
data.instrumentationArgs = instrumentationArgs;
|
||||||
data.instrumentationWatcher = instrumentationWatcher;
|
data.instrumentationWatcher = instrumentationWatcher;
|
||||||
data.debugMode = debugMode;
|
data.debugMode = debugMode;
|
||||||
|
data.restrictedBackupMode = isRestrictedBackupMode;
|
||||||
data.config = config;
|
data.config = config;
|
||||||
queueOrSendMessage(H.BIND_APPLICATION, data);
|
queueOrSendMessage(H.BIND_APPLICATION, data);
|
||||||
}
|
}
|
||||||
@ -1718,6 +1746,8 @@ public final class ActivityThread {
|
|||||||
public static final int ACTIVITY_CONFIGURATION_CHANGED = 125;
|
public static final int ACTIVITY_CONFIGURATION_CHANGED = 125;
|
||||||
public static final int RELAUNCH_ACTIVITY = 126;
|
public static final int RELAUNCH_ACTIVITY = 126;
|
||||||
public static final int PROFILER_CONTROL = 127;
|
public static final int PROFILER_CONTROL = 127;
|
||||||
|
public static final int CREATE_BACKUP_AGENT = 128;
|
||||||
|
public static final int DESTROY_BACKUP_AGENT = 129;
|
||||||
String codeToString(int code) {
|
String codeToString(int code) {
|
||||||
if (localLOGV) {
|
if (localLOGV) {
|
||||||
switch (code) {
|
switch (code) {
|
||||||
@ -1749,6 +1779,8 @@ public final class ActivityThread {
|
|||||||
case ACTIVITY_CONFIGURATION_CHANGED: return "ACTIVITY_CONFIGURATION_CHANGED";
|
case ACTIVITY_CONFIGURATION_CHANGED: return "ACTIVITY_CONFIGURATION_CHANGED";
|
||||||
case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
|
case RELAUNCH_ACTIVITY: return "RELAUNCH_ACTIVITY";
|
||||||
case PROFILER_CONTROL: return "PROFILER_CONTROL";
|
case PROFILER_CONTROL: return "PROFILER_CONTROL";
|
||||||
|
case CREATE_BACKUP_AGENT: return "CREATE_BACKUP_AGENT";
|
||||||
|
case DESTROY_BACKUP_AGENT: return "DESTROY_BACKUP_AGENT";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return "(unknown)";
|
return "(unknown)";
|
||||||
@ -1851,6 +1883,12 @@ public final class ActivityThread {
|
|||||||
case PROFILER_CONTROL:
|
case PROFILER_CONTROL:
|
||||||
handleProfilerControl(msg.arg1 != 0, (String)msg.obj);
|
handleProfilerControl(msg.arg1 != 0, (String)msg.obj);
|
||||||
break;
|
break;
|
||||||
|
case CREATE_BACKUP_AGENT:
|
||||||
|
handleCreateBackupAgent((CreateBackupAgentData)msg.obj);
|
||||||
|
break;
|
||||||
|
case DESTROY_BACKUP_AGENT:
|
||||||
|
handleDestroyBackupAgent((CreateBackupAgentData)msg.obj);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1908,6 +1946,8 @@ public final class ActivityThread {
|
|||||||
Application mInitialApplication;
|
Application mInitialApplication;
|
||||||
final ArrayList<Application> mAllApplications
|
final ArrayList<Application> mAllApplications
|
||||||
= new ArrayList<Application>();
|
= new ArrayList<Application>();
|
||||||
|
// set of instantiated backup agents, keyed by package name
|
||||||
|
final HashMap<String, BackupAgent> mBackupAgents = new HashMap<String, BackupAgent>();
|
||||||
static final ThreadLocal sThreadLocal = new ThreadLocal();
|
static final ThreadLocal sThreadLocal = new ThreadLocal();
|
||||||
Instrumentation mInstrumentation;
|
Instrumentation mInstrumentation;
|
||||||
String mInstrumentationAppDir = null;
|
String mInstrumentationAppDir = null;
|
||||||
@ -2269,7 +2309,7 @@ public final class ActivityThread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Application app = r.packageInfo.makeApplication();
|
Application app = r.packageInfo.makeApplication(false);
|
||||||
|
|
||||||
if (localLOGV) Log.v(TAG, "Performing launch of " + r);
|
if (localLOGV) Log.v(TAG, "Performing launch of " + r);
|
||||||
if (localLOGV) Log.v(
|
if (localLOGV) Log.v(
|
||||||
@ -2464,7 +2504,7 @@ public final class ActivityThread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Application app = packageInfo.makeApplication();
|
Application app = packageInfo.makeApplication(false);
|
||||||
|
|
||||||
if (localLOGV) Log.v(
|
if (localLOGV) Log.v(
|
||||||
TAG, "Performing receive of " + data.intent
|
TAG, "Performing receive of " + data.intent
|
||||||
@ -2507,6 +2547,85 @@ public final class ActivityThread {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Instantiate a BackupAgent and tell it that it's alive
|
||||||
|
private final void handleCreateBackupAgent(CreateBackupAgentData data) {
|
||||||
|
if (DEBUG_BACKUP) Log.v(TAG, "handleCreateBackupAgent: " + data);
|
||||||
|
|
||||||
|
// no longer idle; we have backup work to do
|
||||||
|
unscheduleGcIdler();
|
||||||
|
|
||||||
|
// instantiate the BackupAgent class named in the manifest
|
||||||
|
PackageInfo packageInfo = getPackageInfoNoCheck(data.appInfo);
|
||||||
|
String packageName = packageInfo.mPackageName;
|
||||||
|
if (mBackupAgents.get(packageName) != null) {
|
||||||
|
Log.d(TAG, "BackupAgent " + " for " + packageName
|
||||||
|
+ " already exists");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BackupAgent agent = null;
|
||||||
|
String classname = data.appInfo.backupAgentName;
|
||||||
|
if (classname == null) {
|
||||||
|
if (data.backupMode == IApplicationThread.BACKUP_MODE_INCREMENTAL) {
|
||||||
|
Log.e(TAG, "Attempted incremental backup but no defined agent for "
|
||||||
|
+ packageName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
classname = "android.app.FullBackupAgent";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
java.lang.ClassLoader cl = packageInfo.getClassLoader();
|
||||||
|
agent = (BackupAgent) cl.loadClass(data.appInfo.backupAgentName).newInstance();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Unable to instantiate backup agent "
|
||||||
|
+ data.appInfo.backupAgentName + ": " + e.toString(), e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up the agent's context
|
||||||
|
try {
|
||||||
|
if (DEBUG_BACKUP) Log.v(TAG, "Initializing BackupAgent "
|
||||||
|
+ data.appInfo.backupAgentName);
|
||||||
|
|
||||||
|
ApplicationContext context = new ApplicationContext();
|
||||||
|
context.init(packageInfo, null, this);
|
||||||
|
context.setOuterContext(agent);
|
||||||
|
agent.attach(context);
|
||||||
|
agent.onCreate();
|
||||||
|
|
||||||
|
// tell the OS that we're live now
|
||||||
|
IBinder binder = agent.onBind();
|
||||||
|
try {
|
||||||
|
ActivityManagerNative.getDefault().backupAgentCreated(packageName, binder);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
// nothing to do.
|
||||||
|
}
|
||||||
|
mBackupAgents.put(packageName, agent);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Unable to create BackupAgent "
|
||||||
|
+ data.appInfo.backupAgentName + ": " + e.toString(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tear down a BackupAgent
|
||||||
|
private final void handleDestroyBackupAgent(CreateBackupAgentData data) {
|
||||||
|
if (DEBUG_BACKUP) Log.v(TAG, "handleDestroyBackupAgent: " + data);
|
||||||
|
|
||||||
|
PackageInfo packageInfo = getPackageInfoNoCheck(data.appInfo);
|
||||||
|
String packageName = packageInfo.mPackageName;
|
||||||
|
BackupAgent agent = mBackupAgents.get(packageName);
|
||||||
|
if (agent != null) {
|
||||||
|
try {
|
||||||
|
agent.onDestroy();
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "Exception thrown in onDestroy by backup agent of " + data.appInfo);
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
mBackupAgents.remove(packageName);
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "Attempt to destroy unknown backup agent " + data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final void handleCreateService(CreateServiceData data) {
|
private final void handleCreateService(CreateServiceData data) {
|
||||||
// If we are getting ready to gc after going to the background, well
|
// If we are getting ready to gc after going to the background, well
|
||||||
// we are back active so skip it.
|
// we are back active so skip it.
|
||||||
@ -2532,7 +2651,7 @@ public final class ActivityThread {
|
|||||||
ApplicationContext context = new ApplicationContext();
|
ApplicationContext context = new ApplicationContext();
|
||||||
context.init(packageInfo, null, this);
|
context.init(packageInfo, null, this);
|
||||||
|
|
||||||
Application app = packageInfo.makeApplication();
|
Application app = packageInfo.makeApplication(false);
|
||||||
context.setOuterContext(service);
|
context.setOuterContext(service);
|
||||||
service.attach(context, this, data.info.name, data.token, app,
|
service.attach(context, this, data.info.name, data.token, app,
|
||||||
ActivityManagerNative.getDefault());
|
ActivityManagerNative.getDefault());
|
||||||
@ -3694,7 +3813,9 @@ public final class ActivityThread {
|
|||||||
mInstrumentation = new Instrumentation();
|
mInstrumentation = new Instrumentation();
|
||||||
}
|
}
|
||||||
|
|
||||||
Application app = data.info.makeApplication();
|
// If the app is being launched for full backup or restore, bring it up in
|
||||||
|
// a restricted environment with the base application class.
|
||||||
|
Application app = data.info.makeApplication(data.restrictedBackupMode);
|
||||||
mInitialApplication = app;
|
mInitialApplication = app;
|
||||||
|
|
||||||
List<ProviderInfo> providers = data.providers;
|
List<ProviderInfo> providers = data.providers;
|
||||||
|
@ -230,11 +230,13 @@ public abstract class ApplicationThreadNative extends Binder
|
|||||||
IBinder binder = data.readStrongBinder();
|
IBinder binder = data.readStrongBinder();
|
||||||
IInstrumentationWatcher testWatcher = IInstrumentationWatcher.Stub.asInterface(binder);
|
IInstrumentationWatcher testWatcher = IInstrumentationWatcher.Stub.asInterface(binder);
|
||||||
int testMode = data.readInt();
|
int testMode = data.readInt();
|
||||||
|
boolean restrictedBackupMode = (data.readInt() != 0);
|
||||||
Configuration config = Configuration.CREATOR.createFromParcel(data);
|
Configuration config = Configuration.CREATOR.createFromParcel(data);
|
||||||
HashMap<String, IBinder> services = data.readHashMap(null);
|
HashMap<String, IBinder> services = data.readHashMap(null);
|
||||||
bindApplication(packageName, info,
|
bindApplication(packageName, info,
|
||||||
providers, testName, profileName,
|
providers, testName, profileName,
|
||||||
testArgs, testWatcher, testMode, config, services);
|
testArgs, testWatcher, testMode, restrictedBackupMode,
|
||||||
|
config, services);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,6 +341,15 @@ public abstract class ApplicationThreadNative extends Binder
|
|||||||
setSchedulingGroup(group);
|
setSchedulingGroup(group);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION:
|
||||||
|
{
|
||||||
|
data.enforceInterface(IApplicationThread.descriptor);
|
||||||
|
ApplicationInfo appInfo = ApplicationInfo.CREATOR.createFromParcel(data);
|
||||||
|
int backupMode = data.readInt();
|
||||||
|
scheduleCreateBackupAgent(appInfo, backupMode);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.onTransact(code, data, reply, flags);
|
return super.onTransact(code, data, reply, flags);
|
||||||
@ -492,6 +503,24 @@ class ApplicationThreadProxy implements IApplicationThread {
|
|||||||
data.recycle();
|
data.recycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public final void scheduleCreateBackupAgent(ApplicationInfo app, int backupMode)
|
||||||
|
throws RemoteException {
|
||||||
|
Parcel data = Parcel.obtain();
|
||||||
|
data.writeInterfaceToken(IApplicationThread.descriptor);
|
||||||
|
app.writeToParcel(data, 0);
|
||||||
|
data.writeInt(backupMode);
|
||||||
|
mRemote.transact(SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION, data, null, 0);
|
||||||
|
data.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void scheduleDestroyBackupAgent(ApplicationInfo app) throws RemoteException {
|
||||||
|
Parcel data = Parcel.obtain();
|
||||||
|
data.writeInterfaceToken(IApplicationThread.descriptor);
|
||||||
|
app.writeToParcel(data, 0);
|
||||||
|
mRemote.transact(SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION, data, null, 0);
|
||||||
|
data.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
public final void scheduleCreateService(IBinder token, ServiceInfo info)
|
public final void scheduleCreateService(IBinder token, ServiceInfo info)
|
||||||
throws RemoteException {
|
throws RemoteException {
|
||||||
Parcel data = Parcel.obtain();
|
Parcel data = Parcel.obtain();
|
||||||
@ -551,7 +580,8 @@ class ApplicationThreadProxy implements IApplicationThread {
|
|||||||
public final void bindApplication(String packageName, ApplicationInfo info,
|
public final void bindApplication(String packageName, ApplicationInfo info,
|
||||||
List<ProviderInfo> providers, ComponentName testName,
|
List<ProviderInfo> providers, ComponentName testName,
|
||||||
String profileName, Bundle testArgs, IInstrumentationWatcher testWatcher, int debugMode,
|
String profileName, Bundle testArgs, IInstrumentationWatcher testWatcher, int debugMode,
|
||||||
Configuration config, Map<String, IBinder> services) throws RemoteException {
|
boolean restrictedBackupMode, Configuration config,
|
||||||
|
Map<String, IBinder> services) throws RemoteException {
|
||||||
Parcel data = Parcel.obtain();
|
Parcel data = Parcel.obtain();
|
||||||
data.writeInterfaceToken(IApplicationThread.descriptor);
|
data.writeInterfaceToken(IApplicationThread.descriptor);
|
||||||
data.writeString(packageName);
|
data.writeString(packageName);
|
||||||
@ -567,6 +597,7 @@ class ApplicationThreadProxy implements IApplicationThread {
|
|||||||
data.writeBundle(testArgs);
|
data.writeBundle(testArgs);
|
||||||
data.writeStrongInterface(testWatcher);
|
data.writeStrongInterface(testWatcher);
|
||||||
data.writeInt(debugMode);
|
data.writeInt(debugMode);
|
||||||
|
data.writeInt(restrictedBackupMode ? 1 : 0);
|
||||||
config.writeToParcel(data, 0);
|
config.writeToParcel(data, 0);
|
||||||
data.writeMap(services);
|
data.writeMap(services);
|
||||||
mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null,
|
mRemote.transact(BIND_APPLICATION_TRANSACTION, data, null,
|
||||||
|
@ -14,13 +14,12 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package android.backup;
|
package android.app;
|
||||||
|
|
||||||
import android.annotation.SdkConstant;
|
import android.app.IBackupAgent;
|
||||||
import android.annotation.SdkConstant.SdkConstantType;
|
import android.backup.BackupDataOutput;
|
||||||
import android.app.Service;
|
import android.content.Context;
|
||||||
import android.backup.IBackupService;
|
import android.content.ContextWrapper;
|
||||||
import android.content.Intent;
|
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
@ -30,31 +29,18 @@ import android.util.Log;
|
|||||||
* This is the central interface between an application and Android's
|
* This is the central interface between an application and Android's
|
||||||
* settings backup mechanism.
|
* settings backup mechanism.
|
||||||
*
|
*
|
||||||
* In order to use the backup service, your application must implement a
|
|
||||||
* subclass of BackupService, and declare an intent filter
|
|
||||||
* in the application manifest specifying that your BackupService subclass
|
|
||||||
* handles the {@link BackupService#SERVICE_ACTION} intent action. For example:
|
|
||||||
*
|
|
||||||
* <pre class="prettyprint">
|
|
||||||
* <!-- Use the class "MyBackupService" to perform backups for my app -->
|
|
||||||
* <service android:name=".MyBackupService">
|
|
||||||
* <intent-filter>
|
|
||||||
* <action android:name="android.backup.BackupService.SERVICE" />
|
|
||||||
* </intent-filter>
|
|
||||||
* </service></pre>
|
|
||||||
*
|
|
||||||
* @hide pending API solidification
|
* @hide pending API solidification
|
||||||
*/
|
*/
|
||||||
|
public abstract class BackupAgent extends ContextWrapper {
|
||||||
|
public BackupAgent() {
|
||||||
|
super(null);
|
||||||
|
}
|
||||||
|
|
||||||
public abstract class BackupService extends Service {
|
public void onCreate() {
|
||||||
/**
|
}
|
||||||
* Service Action: Participate in the backup infrastructure. Applications
|
|
||||||
* that wish to use the Android backup mechanism must provide an exported
|
public void onDestroy() {
|
||||||
* subclass of BackupService and give it an {@link android.content.IntentFilter
|
}
|
||||||
* IntentFilter} that accepts this action.
|
|
||||||
*/
|
|
||||||
@SdkConstant(SdkConstantType.SERVICE_ACTION)
|
|
||||||
public static final String SERVICE_ACTION = "android.backup.BackupService.SERVICE";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The application is being asked to write any data changed since the
|
* The application is being asked to write any data changed since the
|
||||||
@ -91,7 +77,8 @@ public abstract class BackupService extends Service {
|
|||||||
* file. The application should record the final backup state
|
* file. The application should record the final backup state
|
||||||
* here after restoring its data from dataFd.
|
* here after restoring its data from dataFd.
|
||||||
*/
|
*/
|
||||||
public abstract void onRestore(ParcelFileDescriptor /* TODO: BackupDataInput */ data, ParcelFileDescriptor newState);
|
public abstract void onRestore(ParcelFileDescriptor /* TODO: BackupDataInput */ data,
|
||||||
|
ParcelFileDescriptor newState);
|
||||||
|
|
||||||
|
|
||||||
// ----- Core implementation -----
|
// ----- Core implementation -----
|
||||||
@ -100,29 +87,33 @@ public abstract class BackupService extends Service {
|
|||||||
* Returns the private interface called by the backup system. Applications will
|
* Returns the private interface called by the backup system. Applications will
|
||||||
* not typically override this.
|
* not typically override this.
|
||||||
*/
|
*/
|
||||||
public IBinder onBind(Intent intent) {
|
public IBinder onBind() {
|
||||||
if (intent.getAction().equals(SERVICE_ACTION)) {
|
|
||||||
return mBinder;
|
return mBinder;
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final IBinder mBinder = new BackupServiceBinder().asBinder();
|
private final IBinder mBinder = new BackupServiceBinder().asBinder();
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public void attach(Context context) {
|
||||||
|
attachBaseContext(context);
|
||||||
|
}
|
||||||
|
|
||||||
// ----- IBackupService binder interface -----
|
// ----- IBackupService binder interface -----
|
||||||
private class BackupServiceBinder extends IBackupService.Stub {
|
private class BackupServiceBinder extends IBackupAgent.Stub {
|
||||||
|
private static final String TAG = "BackupServiceBinder";
|
||||||
|
|
||||||
public void doBackup(ParcelFileDescriptor oldState,
|
public void doBackup(ParcelFileDescriptor oldState,
|
||||||
ParcelFileDescriptor data,
|
ParcelFileDescriptor data,
|
||||||
ParcelFileDescriptor newState) throws RemoteException {
|
ParcelFileDescriptor newState) throws RemoteException {
|
||||||
// !!! TODO - real implementation; for now just invoke the callbacks directly
|
// !!! TODO - real implementation; for now just invoke the callbacks directly
|
||||||
Log.v("BackupServiceBinder", "doBackup() invoked");
|
Log.v(TAG, "doBackup() invoked");
|
||||||
BackupDataOutput output = new BackupDataOutput(BackupService.this,
|
BackupDataOutput output = new BackupDataOutput(BackupAgent.this,
|
||||||
data.getFileDescriptor());
|
data.getFileDescriptor());
|
||||||
try {
|
try {
|
||||||
BackupService.this.onBackup(oldState, output, newState);
|
BackupAgent.this.onBackup(oldState, output, newState);
|
||||||
} catch (RuntimeException ex) {
|
} catch (RuntimeException ex) {
|
||||||
Log.d("BackupService", "onBackup ("
|
Log.d("BackupAgent", "onBackup ("
|
||||||
+ BackupService.this.getClass().getName() + ") threw", ex);
|
+ BackupAgent.this.getClass().getName() + ") threw", ex);
|
||||||
throw ex;
|
throw ex;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,8 +121,8 @@ public abstract class BackupService extends Service {
|
|||||||
public void doRestore(ParcelFileDescriptor data,
|
public void doRestore(ParcelFileDescriptor data,
|
||||||
ParcelFileDescriptor newState) throws RemoteException {
|
ParcelFileDescriptor newState) throws RemoteException {
|
||||||
// !!! TODO - real implementation; for now just invoke the callbacks directly
|
// !!! TODO - real implementation; for now just invoke the callbacks directly
|
||||||
Log.v("BackupServiceBinder", "doRestore() invoked");
|
Log.v(TAG, "doRestore() invoked");
|
||||||
BackupService.this.onRestore(data, newState);
|
BackupAgent.this.onRestore(data, newState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -21,6 +21,7 @@ import android.content.ContentProviderNative;
|
|||||||
import android.content.IContentProvider;
|
import android.content.IContentProvider;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.ConfigurationInfo;
|
import android.content.pm.ConfigurationInfo;
|
||||||
import android.content.pm.IPackageDataObserver;
|
import android.content.pm.IPackageDataObserver;
|
||||||
import android.content.pm.ProviderInfo;
|
import android.content.pm.ProviderInfo;
|
||||||
@ -149,6 +150,11 @@ public interface IActivityManager extends IInterface {
|
|||||||
public void serviceDoneExecuting(IBinder token) throws RemoteException;
|
public void serviceDoneExecuting(IBinder token) throws RemoteException;
|
||||||
public IBinder peekService(Intent service, String resolvedType) throws RemoteException;
|
public IBinder peekService(Intent service, String resolvedType) throws RemoteException;
|
||||||
|
|
||||||
|
public boolean bindBackupAgent(ApplicationInfo appInfo, int backupRestoreMode)
|
||||||
|
throws RemoteException;
|
||||||
|
public void backupAgentCreated(String packageName, IBinder agent) throws RemoteException;
|
||||||
|
public void unbindBackupAgent(ApplicationInfo appInfo) throws RemoteException;
|
||||||
|
|
||||||
public boolean startInstrumentation(ComponentName className, String profileFile,
|
public boolean startInstrumentation(ComponentName className, String profileFile,
|
||||||
int flags, Bundle arguments, IInstrumentationWatcher watcher)
|
int flags, Bundle arguments, IInstrumentationWatcher watcher)
|
||||||
throws RemoteException;
|
throws RemoteException;
|
||||||
@ -397,4 +403,7 @@ public interface IActivityManager extends IInterface {
|
|||||||
int SHUTDOWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+86;
|
int SHUTDOWN_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+86;
|
||||||
int STOP_APP_SWITCHES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+87;
|
int STOP_APP_SWITCHES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+87;
|
||||||
int RESUME_APP_SWITCHES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+88;
|
int RESUME_APP_SWITCHES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+88;
|
||||||
|
int START_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+89;
|
||||||
|
int BACKUP_AGENT_CREATED_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+90;
|
||||||
|
int UNBIND_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+91;
|
||||||
}
|
}
|
||||||
|
@ -59,6 +59,11 @@ public interface IApplicationThread extends IInterface {
|
|||||||
int configChanges) throws RemoteException;
|
int configChanges) throws RemoteException;
|
||||||
void scheduleReceiver(Intent intent, ActivityInfo info, int resultCode,
|
void scheduleReceiver(Intent intent, ActivityInfo info, int resultCode,
|
||||||
String data, Bundle extras, boolean sync) throws RemoteException;
|
String data, Bundle extras, boolean sync) throws RemoteException;
|
||||||
|
static final int BACKUP_MODE_INCREMENTAL = 0;
|
||||||
|
static final int BACKUP_MODE_FULL = 1;
|
||||||
|
static final int BACKUP_MODE_RESTORE = 2;
|
||||||
|
void scheduleCreateBackupAgent(ApplicationInfo app, int backupMode) throws RemoteException;
|
||||||
|
void scheduleDestroyBackupAgent(ApplicationInfo app) throws RemoteException;
|
||||||
void scheduleCreateService(IBinder token, ServiceInfo info) throws RemoteException;
|
void scheduleCreateService(IBinder token, ServiceInfo info) throws RemoteException;
|
||||||
void scheduleBindService(IBinder token,
|
void scheduleBindService(IBinder token,
|
||||||
Intent intent, boolean rebind) throws RemoteException;
|
Intent intent, boolean rebind) throws RemoteException;
|
||||||
@ -71,8 +76,8 @@ public interface IApplicationThread extends IInterface {
|
|||||||
static final int DEBUG_WAIT = 2;
|
static final int DEBUG_WAIT = 2;
|
||||||
void bindApplication(String packageName, ApplicationInfo info, List<ProviderInfo> providers,
|
void bindApplication(String packageName, ApplicationInfo info, List<ProviderInfo> providers,
|
||||||
ComponentName testName, String profileName, Bundle testArguments,
|
ComponentName testName, String profileName, Bundle testArguments,
|
||||||
IInstrumentationWatcher testWatcher, int debugMode, Configuration config, Map<String,
|
IInstrumentationWatcher testWatcher, int debugMode, boolean restrictedBackupMode,
|
||||||
IBinder> services) throws RemoteException;
|
Configuration config, Map<String, IBinder> services) throws RemoteException;
|
||||||
void scheduleExit() throws RemoteException;
|
void scheduleExit() throws RemoteException;
|
||||||
void requestThumbnail(IBinder token) throws RemoteException;
|
void requestThumbnail(IBinder token) throws RemoteException;
|
||||||
void scheduleConfigurationChanged(Configuration config) throws RemoteException;
|
void scheduleConfigurationChanged(Configuration config) throws RemoteException;
|
||||||
@ -119,4 +124,6 @@ public interface IApplicationThread extends IInterface {
|
|||||||
int REQUEST_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+26;
|
int REQUEST_PSS_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+26;
|
||||||
int PROFILER_CONTROL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+27;
|
int PROFILER_CONTROL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+27;
|
||||||
int SET_SCHEDULING_GROUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28;
|
int SET_SCHEDULING_GROUP_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+28;
|
||||||
|
int SCHEDULE_CREATE_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+29;
|
||||||
|
int SCHEDULE_DESTROY_BACKUP_AGENT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+30;
|
||||||
}
|
}
|
||||||
|
@ -14,18 +14,18 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package android.backup;
|
package android.app;
|
||||||
|
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Interface presented by applications being asked to participate in the
|
* Interface presented by applications being asked to participate in the
|
||||||
* backup & restore mechanism. End user code does not typically implement
|
* backup & restore mechanism. End user code does not typically implement
|
||||||
* this interface; they subclass BackupService instead.
|
* this interface; they subclass BackupAgent instead.
|
||||||
*
|
*
|
||||||
* {@hide}
|
* {@hide}
|
||||||
*/
|
*/
|
||||||
interface IBackupService {
|
interface IBackupAgent {
|
||||||
/**
|
/**
|
||||||
* Request that the app perform an incremental backup.
|
* Request that the app perform an incremental backup.
|
||||||
*
|
*
|
@ -33,9 +33,23 @@ interface IBackupManager {
|
|||||||
*/
|
*/
|
||||||
oneway void dataChanged(String packageName);
|
oneway void dataChanged(String packageName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the Backup Manager Service that an agent has become available. This
|
||||||
|
* method is only invoked by the Activity Manager.
|
||||||
|
* !!! TODO: permission
|
||||||
|
*/
|
||||||
|
oneway void agentConnected(String packageName, IBinder agent);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the Backup Manager Service that an agent has unexpectedly gone away.
|
||||||
|
* This method is only invoked by the Activity Manager.
|
||||||
|
* !!! TODO: permission
|
||||||
|
*/
|
||||||
|
oneway void agentDisconnected(String packageName);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Schedule a full backup of the given package.
|
* Schedule a full backup of the given package.
|
||||||
* !!! TODO: protect with a signature-or-system permission?
|
* !!! TODO: permission
|
||||||
*/
|
*/
|
||||||
oneway void scheduleFullBackup(String packageName);
|
oneway void scheduleFullBackup(String packageName);
|
||||||
}
|
}
|
||||||
|
@ -58,10 +58,21 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
|
|||||||
* Class implementing the Application's manage space
|
* Class implementing the Application's manage space
|
||||||
* functionality. From the "manageSpaceActivity"
|
* functionality. From the "manageSpaceActivity"
|
||||||
* attribute. This is an optional attribute and will be null if
|
* attribute. This is an optional attribute and will be null if
|
||||||
* application's dont specify it in their manifest
|
* applications don't specify it in their manifest
|
||||||
*/
|
*/
|
||||||
public String manageSpaceActivityName;
|
public String manageSpaceActivityName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class implementing the Application's backup functionality. From
|
||||||
|
* the "backupAgent" attribute. This is an optional attribute and
|
||||||
|
* will be null if the application does not specify it in its manifest.
|
||||||
|
*
|
||||||
|
* <p>If android:allowBackup is set to false, this attribute is ignored.
|
||||||
|
*
|
||||||
|
* {@hide}
|
||||||
|
*/
|
||||||
|
public String backupAgentName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value for {@link #flags}: if set, this application is installed in the
|
* Value for {@link #flags}: if set, this application is installed in the
|
||||||
* device's system image.
|
* device's system image.
|
||||||
@ -93,7 +104,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
|
|||||||
public static final int FLAG_PERSISTENT = 1<<3;
|
public static final int FLAG_PERSISTENT = 1<<3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Value for {@link #flags}: set to true iif this application holds the
|
* Value for {@link #flags}: set to true if this application holds the
|
||||||
* {@link android.Manifest.permission#FACTORY_TEST} permission and the
|
* {@link android.Manifest.permission#FACTORY_TEST} permission and the
|
||||||
* device is running in factory test mode.
|
* device is running in factory test mode.
|
||||||
*/
|
*/
|
||||||
@ -125,6 +136,14 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
|
|||||||
*/
|
*/
|
||||||
public static final int FLAG_TEST_ONLY = 1<<8;
|
public static final int FLAG_TEST_ONLY = 1<<8;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Value for {@link #flags}: this is false if the application has set
|
||||||
|
* its android:allowBackup to false, true otherwise.
|
||||||
|
*
|
||||||
|
* {@hide}
|
||||||
|
*/
|
||||||
|
public static final int FLAG_ALLOW_BACKUP = 1<<10;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flags associated with the application. Any combination of
|
* Flags associated with the application. Any combination of
|
||||||
* {@link #FLAG_SYSTEM}, {@link #FLAG_DEBUGGABLE}, {@link #FLAG_HAS_CODE},
|
* {@link #FLAG_SYSTEM}, {@link #FLAG_DEBUGGABLE}, {@link #FLAG_HAS_CODE},
|
||||||
@ -285,6 +304,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
|
|||||||
dest.writeInt(targetSdkVersion);
|
dest.writeInt(targetSdkVersion);
|
||||||
dest.writeInt(enabled ? 1 : 0);
|
dest.writeInt(enabled ? 1 : 0);
|
||||||
dest.writeString(manageSpaceActivityName);
|
dest.writeString(manageSpaceActivityName);
|
||||||
|
dest.writeString(backupAgentName);
|
||||||
dest.writeInt(descriptionRes);
|
dest.writeInt(descriptionRes);
|
||||||
dest.writeIntArray(supportsDensities);
|
dest.writeIntArray(supportsDensities);
|
||||||
}
|
}
|
||||||
@ -315,6 +335,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
|
|||||||
targetSdkVersion = source.readInt();
|
targetSdkVersion = source.readInt();
|
||||||
enabled = source.readInt() != 0;
|
enabled = source.readInt() != 0;
|
||||||
manageSpaceActivityName = source.readString();
|
manageSpaceActivityName = source.readString();
|
||||||
|
backupAgentName = source.readString();
|
||||||
descriptionRes = source.readInt();
|
descriptionRes = source.readInt();
|
||||||
supportsDensities = source.createIntArray();
|
supportsDensities = source.createIntArray();
|
||||||
}
|
}
|
||||||
|
@ -1176,6 +1176,19 @@ public class PackageParser {
|
|||||||
outError);
|
outError);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean allowBackup = sa.getBoolean(
|
||||||
|
com.android.internal.R.styleable.AndroidManifestApplication_allowBackup, true);
|
||||||
|
if (allowBackup) {
|
||||||
|
ai.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
|
||||||
|
String backupAgent = sa.getNonResourceString(
|
||||||
|
com.android.internal.R.styleable.AndroidManifestApplication_backupAgent);
|
||||||
|
if (backupAgent != null) {
|
||||||
|
ai.backupAgentName = buildClassName(pkgName, backupAgent, outError);
|
||||||
|
Log.v(TAG, "android:backupAgent = " + ai.backupAgentName
|
||||||
|
+ " from " + pkgName + "+" + backupAgent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TypedValue v = sa.peekValue(
|
TypedValue v = sa.peekValue(
|
||||||
com.android.internal.R.styleable.AndroidManifestApplication_label);
|
com.android.internal.R.styleable.AndroidManifestApplication_label);
|
||||||
if (v != null && (ai.labelRes=v.resourceId) == 0) {
|
if (v != null && (ai.labelRes=v.resourceId) == 0) {
|
||||||
|
@ -972,6 +972,13 @@
|
|||||||
android:description="@string/permdesc_batteryStats"
|
android:description="@string/permdesc_batteryStats"
|
||||||
android:protectionLevel="normal" />
|
android:protectionLevel="normal" />
|
||||||
|
|
||||||
|
<!-- Allows an application to control the backup and restore process
|
||||||
|
@hide pending API council -->
|
||||||
|
<permission android:name="android.permission.BACKUP"
|
||||||
|
android:label="@string/permlab_backup"
|
||||||
|
android:description="@string/permdesc_backup"
|
||||||
|
android:protectionLevel="signature" />
|
||||||
|
|
||||||
<!-- Allows an application to tell the AppWidget service which application
|
<!-- Allows an application to tell the AppWidget service which application
|
||||||
can access AppWidget's data. The normal user flow is that a user
|
can access AppWidget's data. The normal user flow is that a user
|
||||||
picks an AppWidget to go into a particular host, thereby giving that
|
picks an AppWidget to go into a particular host, thereby giving that
|
||||||
|
@ -570,6 +570,15 @@
|
|||||||
<!-- Application's requirement for five way navigation -->
|
<!-- Application's requirement for five way navigation -->
|
||||||
<attr name="reqFiveWayNav" format="boolean" />
|
<attr name="reqFiveWayNav" format="boolean" />
|
||||||
|
|
||||||
|
<!-- The name of the class implementing <code>BackupAgent</code> to manage
|
||||||
|
backup and restore of the application's settings to external storage. -->
|
||||||
|
<attr name="backupAgent" format="string" />
|
||||||
|
|
||||||
|
<!-- Whether the application allows its data to be backed up at all. This
|
||||||
|
attribute defaults to 'true': unless the application opts out, the
|
||||||
|
user will be able to back up its data to desktop storage. -->
|
||||||
|
<attr name="allowBackup" format="boolean" />
|
||||||
|
|
||||||
<!-- The <code>manifest</code> tag is the root of an
|
<!-- The <code>manifest</code> tag is the root of an
|
||||||
<code>AndroidManifest.xml</code> file,
|
<code>AndroidManifest.xml</code> file,
|
||||||
describing the contents of an Android package (.apk) file. One
|
describing the contents of an Android package (.apk) file. One
|
||||||
@ -643,6 +652,8 @@
|
|||||||
<attr name="manageSpaceActivity" />
|
<attr name="manageSpaceActivity" />
|
||||||
<attr name="allowClearUserData" />
|
<attr name="allowClearUserData" />
|
||||||
<attr name="testOnly" />
|
<attr name="testOnly" />
|
||||||
|
<attr name="backupAgent" />
|
||||||
|
<attr name="allowBackup" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
<!-- The <code>permission</code> tag declares a security permission that can be
|
<!-- The <code>permission</code> tag declares a security permission that can be
|
||||||
|
@ -1113,6 +1113,8 @@
|
|||||||
<public type="attr" name="gestureStrokeAngleThreshold" />
|
<public type="attr" name="gestureStrokeAngleThreshold" />
|
||||||
<public type="attr" name="eventsInterceptionEnabled" />
|
<public type="attr" name="eventsInterceptionEnabled" />
|
||||||
<public type="attr" name="fadeEnabled" />
|
<public type="attr" name="fadeEnabled" />
|
||||||
|
<public type="attr" name="backupAgent" />
|
||||||
|
<public type="attr" name="allowBackup" />
|
||||||
|
|
||||||
<public-padding type="attr" name="donut_resource_pad" end="0x0101029f" />
|
<public-padding type="attr" name="donut_resource_pad" end="0x0101029f" />
|
||||||
|
|
||||||
|
@ -539,6 +539,11 @@
|
|||||||
<string name="permdesc_batteryStats">Allows the modification of
|
<string name="permdesc_batteryStats">Allows the modification of
|
||||||
collected battery statistics. Not for use by normal applications.</string>
|
collected battery statistics. Not for use by normal applications.</string>
|
||||||
|
|
||||||
|
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
|
||||||
|
<string name="permlab_backup">control system backup and restore</string>
|
||||||
|
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
|
||||||
|
<string name="permdesc_backup">Allows the application to control the system's backup and restore mechanism. Not for use by normal applications.</string>
|
||||||
|
|
||||||
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
|
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
|
||||||
<string name="permlab_internalSystemWindow">display unauthorized windows</string>
|
<string name="permlab_internalSystemWindow">display unauthorized windows</string>
|
||||||
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
|
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
|
||||||
|
@ -16,17 +16,16 @@
|
|||||||
|
|
||||||
package com.android.server;
|
package com.android.server;
|
||||||
|
|
||||||
import android.backup.BackupService;
|
import android.app.ActivityManagerNative;
|
||||||
import android.backup.IBackupService;
|
import android.app.IActivityManager;
|
||||||
|
import android.app.IApplicationThread;
|
||||||
|
import android.app.IBackupAgent;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.IntentFilter;
|
import android.content.IntentFilter;
|
||||||
import android.content.ServiceConnection;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ResolveInfo;
|
|
||||||
import android.content.pm.ServiceInfo;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Binder;
|
import android.os.Binder;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
@ -49,6 +48,7 @@ import java.lang.String;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
class BackupManagerService extends IBackupManager.Stub {
|
class BackupManagerService extends IBackupManager.Stub {
|
||||||
@ -62,22 +62,28 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
|
|
||||||
private Context mContext;
|
private Context mContext;
|
||||||
private PackageManager mPackageManager;
|
private PackageManager mPackageManager;
|
||||||
|
private final IActivityManager mActivityManager;
|
||||||
private final BackupHandler mBackupHandler = new BackupHandler();
|
private final BackupHandler mBackupHandler = new BackupHandler();
|
||||||
// map UIDs to the set of backup client services within that UID's app set
|
// map UIDs to the set of backup client services within that UID's app set
|
||||||
private SparseArray<HashSet<ServiceInfo>> mBackupParticipants
|
private SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
|
||||||
= new SparseArray<HashSet<ServiceInfo>>();
|
= new SparseArray<HashSet<ApplicationInfo>>();
|
||||||
// set of backup services that have pending changes
|
// set of backup services that have pending changes
|
||||||
private class BackupRequest {
|
private class BackupRequest {
|
||||||
public ServiceInfo service;
|
public ApplicationInfo appInfo;
|
||||||
public boolean fullBackup;
|
public boolean fullBackup;
|
||||||
|
|
||||||
BackupRequest(ServiceInfo svc, boolean isFull) {
|
BackupRequest(ApplicationInfo app, boolean isFull) {
|
||||||
service = svc;
|
appInfo = app;
|
||||||
fullBackup = isFull;
|
fullBackup = isFull;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
return "BackupRequest{app=" + appInfo + " full=" + fullBackup + "}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Backups that we haven't started yet.
|
// Backups that we haven't started yet.
|
||||||
private HashMap<ComponentName,BackupRequest> mPendingBackups = new HashMap();
|
private HashMap<ApplicationInfo,BackupRequest> mPendingBackups
|
||||||
|
= new HashMap<ApplicationInfo,BackupRequest>();
|
||||||
// Backups that we have started. These are separate to prevent starvation
|
// Backups that we have started. These are separate to prevent starvation
|
||||||
// if an app keeps re-enqueuing itself.
|
// if an app keeps re-enqueuing itself.
|
||||||
private ArrayList<BackupRequest> mBackupQueue;
|
private ArrayList<BackupRequest> mBackupQueue;
|
||||||
@ -89,6 +95,7 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
public BackupManagerService(Context context) {
|
public BackupManagerService(Context context) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mPackageManager = context.getPackageManager();
|
mPackageManager = context.getPackageManager();
|
||||||
|
mActivityManager = ActivityManagerNative.getDefault();
|
||||||
|
|
||||||
// Set up our bookkeeping
|
// Set up our bookkeeping
|
||||||
mStateDir = new File(Environment.getDataDirectory(), "backup");
|
mStateDir = new File(Environment.getDataDirectory(), "backup");
|
||||||
@ -151,7 +158,7 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
|
|
||||||
// ----- Run the actual backup process asynchronously -----
|
// ----- Run the actual backup process asynchronously -----
|
||||||
|
|
||||||
private class BackupHandler extends Handler implements ServiceConnection {
|
private class BackupHandler extends Handler {
|
||||||
public void handleMessage(Message msg) {
|
public void handleMessage(Message msg) {
|
||||||
|
|
||||||
switch (msg.what) {
|
switch (msg.what) {
|
||||||
@ -163,31 +170,20 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
for (BackupRequest b: mPendingBackups.values()) {
|
for (BackupRequest b: mPendingBackups.values()) {
|
||||||
mBackupQueue.add(b);
|
mBackupQueue.add(b);
|
||||||
}
|
}
|
||||||
mPendingBackups = new HashMap<ComponentName,BackupRequest>();
|
mPendingBackups = new HashMap<ApplicationInfo,BackupRequest>();
|
||||||
}
|
}
|
||||||
// !!! TODO: start a new backup-queue journal file too
|
// !!! TODO: start a new backup-queue journal file too
|
||||||
// WARNING: If we crash after this line, anything in mPendingBackups will
|
// WARNING: If we crash after this line, anything in mPendingBackups will
|
||||||
// be lost. FIX THIS.
|
// be lost. FIX THIS.
|
||||||
}
|
}
|
||||||
startOneService();
|
startOneAgent();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
|
||||||
Log.d(TAG, "onServiceConnected name=" + name + " service=" + service);
|
|
||||||
IBackupService bs = IBackupService.Stub.asInterface(service);
|
|
||||||
processOneBackup(name, bs);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onServiceDisconnected(ComponentName name) {
|
void startOneAgent() {
|
||||||
// TODO: handle backup being interrupted
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void startOneService() {
|
|
||||||
// Loop until we find someone to start or the queue empties out.
|
// Loop until we find someone to start or the queue empties out.
|
||||||
Intent intent = new Intent(BackupService.SERVICE_ACTION);
|
|
||||||
while (true) {
|
while (true) {
|
||||||
BackupRequest request;
|
BackupRequest request;
|
||||||
synchronized (mQueueLock) {
|
synchronized (mQueueLock) {
|
||||||
@ -205,14 +201,19 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
// Take it off the queue when we're done.
|
// Take it off the queue when we're done.
|
||||||
}
|
}
|
||||||
|
|
||||||
intent.setClassName(request.service.packageName, request.service.name);
|
Log.d(TAG, "starting agent for " + request);
|
||||||
Log.d(TAG, "binding to " + intent);
|
// !!! TODO: need to handle the restore case?
|
||||||
|
int mode = (request.fullBackup)
|
||||||
|
? IApplicationThread.BACKUP_MODE_FULL
|
||||||
|
: IApplicationThread.BACKUP_MODE_INCREMENTAL;
|
||||||
try {
|
try {
|
||||||
if (mContext.bindService(intent, mBackupHandler, Context.BIND_AUTO_CREATE)) {
|
if (mActivityManager.bindBackupAgent(request.appInfo, mode)) {
|
||||||
Log.d(TAG, "awaiting service object for " + intent);
|
Log.d(TAG, "awaiting agent for " + request);
|
||||||
// success
|
// success
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
// can't happen; activity manager is local
|
||||||
} catch (SecurityException ex) {
|
} catch (SecurityException ex) {
|
||||||
// Try for the next one.
|
// Try for the next one.
|
||||||
Log.d(TAG, "error in bind", ex);
|
Log.d(TAG, "error in bind", ex);
|
||||||
@ -220,9 +221,8 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void processOneBackup(ComponentName name, IBackupService bs) {
|
void processOneBackup(String packageName, IBackupAgent bs) {
|
||||||
try {
|
Log.d(TAG, "processOneBackup doBackup() on " + packageName);
|
||||||
Log.d(TAG, "processOneBackup doBackup() on " + name);
|
|
||||||
|
|
||||||
BackupRequest request;
|
BackupRequest request;
|
||||||
synchronized (mQueueLock) {
|
synchronized (mQueueLock) {
|
||||||
@ -232,11 +232,12 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
request = mBackupQueue.get(0);
|
request = mBackupQueue.get(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
// !!! TODO right now these naming schemes limit applications to
|
// !!! TODO right now these naming schemes limit applications to
|
||||||
// one backup service per package
|
// one backup service per package
|
||||||
File savedStateName = new File(mStateDir, request.service.packageName);
|
File savedStateName = new File(mStateDir, request.appInfo.packageName);
|
||||||
File backupDataName = new File(mDataDir, request.service.packageName + ".data");
|
File backupDataName = new File(mDataDir, request.appInfo.packageName + ".data");
|
||||||
File newStateName = new File(mStateDir, request.service.packageName + ".new");
|
File newStateName = new File(mStateDir, request.appInfo.packageName + ".new");
|
||||||
|
|
||||||
// In a full backup, we pass a null ParcelFileDescriptor as
|
// In a full backup, we pass a null ParcelFileDescriptor as
|
||||||
// the saved-state "file"
|
// the saved-state "file"
|
||||||
@ -280,7 +281,7 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
Log.d(TAG, "File not found on backup: ");
|
Log.d(TAG, "File not found on backup: ");
|
||||||
fnf.printStackTrace();
|
fnf.printStackTrace();
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
Log.d(TAG, "Remote target " + name + " threw during backup:");
|
Log.d(TAG, "Remote target " + packageName + " threw during backup:");
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.w(TAG, "Final exception guard in backup: ");
|
Log.w(TAG, "Final exception guard in backup: ");
|
||||||
@ -289,37 +290,45 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
synchronized (mQueueLock) {
|
synchronized (mQueueLock) {
|
||||||
mBackupQueue.remove(0);
|
mBackupQueue.remove(0);
|
||||||
}
|
}
|
||||||
mContext.unbindService(mBackupHandler);
|
|
||||||
|
if (request != null) {
|
||||||
|
try {
|
||||||
|
mActivityManager.unbindBackupAgent(request.appInfo);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
// can't happen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// start the next one
|
// start the next one
|
||||||
startOneService();
|
startOneAgent();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the backup services in the given package to our set of known backup participants.
|
// Add the backup agents in the given package to our set of known backup participants.
|
||||||
// If 'packageName' is null, adds all backup services in the system.
|
// If 'packageName' is null, adds all backup agents in the whole system.
|
||||||
void addPackageParticipantsLocked(String packageName) {
|
void addPackageParticipantsLocked(String packageName) {
|
||||||
List<ResolveInfo> services = mPackageManager.queryIntentServices(
|
// Look for apps that define the android:backupAgent attribute
|
||||||
new Intent(BackupService.SERVICE_ACTION), 0);
|
List<ApplicationInfo> targetApps = allAgentApps();
|
||||||
addPackageParticipantsLockedInner(packageName, services);
|
addPackageParticipantsLockedInner(packageName, targetApps);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPackageParticipantsLockedInner(String packageName, List<ResolveInfo> services) {
|
private void addPackageParticipantsLockedInner(String packageName,
|
||||||
for (ResolveInfo ri : services) {
|
List<ApplicationInfo> targetApps) {
|
||||||
if (packageName == null || ri.serviceInfo.packageName.equals(packageName)) {
|
if (DEBUG) {
|
||||||
int uid = ri.serviceInfo.applicationInfo.uid;
|
Log.v(TAG, "Adding " + targetApps.size() + " backup participants:");
|
||||||
HashSet<ServiceInfo> set = mBackupParticipants.get(uid);
|
for (ApplicationInfo a : targetApps) {
|
||||||
|
Log.v(TAG, " " + a + " agent=" + a.backupAgentName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ApplicationInfo app : targetApps) {
|
||||||
|
if (packageName == null || app.packageName.equals(packageName)) {
|
||||||
|
int uid = app.uid;
|
||||||
|
HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
|
||||||
if (set == null) {
|
if (set == null) {
|
||||||
set = new HashSet<ServiceInfo>();
|
set = new HashSet<ApplicationInfo>();
|
||||||
mBackupParticipants.put(uid, set);
|
mBackupParticipants.put(uid, set);
|
||||||
}
|
}
|
||||||
if (DEBUG) {
|
set.add(app);
|
||||||
Log.v(TAG, "Adding " + services.size() + " backup participants:");
|
|
||||||
for (ResolveInfo svc : services) {
|
|
||||||
Log.v(TAG, " " + svc + " : " + svc.filter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set.add(ri.serviceInfo);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -327,19 +336,30 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
// Remove the given package's backup services from our known active set. If
|
// Remove the given package's backup services from our known active set. If
|
||||||
// 'packageName' is null, *all* backup services will be removed.
|
// 'packageName' is null, *all* backup services will be removed.
|
||||||
void removePackageParticipantsLocked(String packageName) {
|
void removePackageParticipantsLocked(String packageName) {
|
||||||
List<ResolveInfo> services = mPackageManager.queryIntentServices(
|
List<ApplicationInfo> allApps = null;
|
||||||
new Intent(BackupService.SERVICE_ACTION), 0);
|
if (packageName != null) {
|
||||||
removePackageParticipantsLockedInner(packageName, services);
|
allApps = new ArrayList<ApplicationInfo>();
|
||||||
|
try {
|
||||||
|
ApplicationInfo app = mPackageManager.getApplicationInfo(packageName, 0);
|
||||||
|
allApps.add(app);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// just skip it
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// all apps with agents
|
||||||
|
allApps = allAgentApps();
|
||||||
|
}
|
||||||
|
removePackageParticipantsLockedInner(packageName, allApps);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removePackageParticipantsLockedInner(String packageName,
|
private void removePackageParticipantsLockedInner(String packageName,
|
||||||
List<ResolveInfo> services) {
|
List<ApplicationInfo> agents) {
|
||||||
for (ResolveInfo ri : services) {
|
for (ApplicationInfo app : agents) {
|
||||||
if (packageName == null || ri.serviceInfo.packageName.equals(packageName)) {
|
if (packageName == null || app.packageName.equals(packageName)) {
|
||||||
int uid = ri.serviceInfo.applicationInfo.uid;
|
int uid = app.uid;
|
||||||
HashSet<ServiceInfo> set = mBackupParticipants.get(uid);
|
HashSet<ApplicationInfo> set = mBackupParticipants.get(uid);
|
||||||
if (set != null) {
|
if (set != null) {
|
||||||
set.remove(ri.serviceInfo);
|
set.remove(app);
|
||||||
if (set.size() == 0) {
|
if (set.size() == 0) {
|
||||||
mBackupParticipants.put(uid, null);
|
mBackupParticipants.put(uid, null);
|
||||||
}
|
}
|
||||||
@ -348,6 +368,21 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the set of all applications that define an android:backupAgent attribute
|
||||||
|
private List<ApplicationInfo> allAgentApps() {
|
||||||
|
List<ApplicationInfo> allApps = mPackageManager.getInstalledApplications(0);
|
||||||
|
int N = allApps.size();
|
||||||
|
if (N > 0) {
|
||||||
|
for (int a = N-1; a >= 0; a--) {
|
||||||
|
ApplicationInfo app = allApps.get(a);
|
||||||
|
if (app.backupAgentName == null) {
|
||||||
|
allApps.remove(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return allApps;
|
||||||
|
}
|
||||||
|
|
||||||
// Reset the given package's known backup participants. Unlike add/remove, the update
|
// Reset the given package's known backup participants. Unlike add/remove, the update
|
||||||
// action cannot be passed a null package name.
|
// action cannot be passed a null package name.
|
||||||
void updatePackageParticipantsLocked(String packageName) {
|
void updatePackageParticipantsLocked(String packageName) {
|
||||||
@ -357,10 +392,9 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// brute force but small code size
|
// brute force but small code size
|
||||||
List<ResolveInfo> services = mPackageManager.queryIntentServices(
|
List<ApplicationInfo> allApps = allAgentApps();
|
||||||
new Intent(BackupService.SERVICE_ACTION), 0);
|
removePackageParticipantsLockedInner(packageName, allApps);
|
||||||
removePackageParticipantsLockedInner(packageName, services);
|
addPackageParticipantsLockedInner(packageName, allApps);
|
||||||
addPackageParticipantsLockedInner(packageName, services);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----- IBackupManager binder interface -----
|
// ----- IBackupManager binder interface -----
|
||||||
@ -372,24 +406,29 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
|
|
||||||
Log.d(TAG, "dataChanged packageName=" + packageName);
|
Log.d(TAG, "dataChanged packageName=" + packageName);
|
||||||
|
|
||||||
HashSet<ServiceInfo> targets = mBackupParticipants.get(Binder.getCallingUid());
|
HashSet<ApplicationInfo> targets = mBackupParticipants.get(Binder.getCallingUid());
|
||||||
Log.d(TAG, "targets=" + targets);
|
|
||||||
if (targets != null) {
|
if (targets != null) {
|
||||||
synchronized (mQueueLock) {
|
synchronized (mQueueLock) {
|
||||||
// Note that this client has made data changes that need to be backed up
|
// Note that this client has made data changes that need to be backed up
|
||||||
for (ServiceInfo service : targets) {
|
for (ApplicationInfo app : targets) {
|
||||||
// validate the caller-supplied package name against the known set of
|
// validate the caller-supplied package name against the known set of
|
||||||
// packages associated with this uid
|
// packages associated with this uid
|
||||||
if (service.packageName.equals(packageName)) {
|
if (app.packageName.equals(packageName)) {
|
||||||
// Add the caller to the set of pending backups. If there is
|
// Add the caller to the set of pending backups. If there is
|
||||||
// one already there, then overwrite it, but no harm done.
|
// one already there, then overwrite it, but no harm done.
|
||||||
mPendingBackups.put(new ComponentName(service.packageName, service.name),
|
BackupRequest req = new BackupRequest(app, false);
|
||||||
new BackupRequest(service, true));
|
mPendingBackups.put(app, req);
|
||||||
// !!! TODO: write to the pending-backup journal file in case of crash
|
// !!! TODO: write to the pending-backup journal file in case of crash
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(TAG, "Scheduling backup for " + mPendingBackups.size() + " participants");
|
if (DEBUG) {
|
||||||
|
int numKeys = mPendingBackups.size();
|
||||||
|
Log.d(TAG, "Scheduling backup for " + numKeys + " participants:");
|
||||||
|
for (BackupRequest b : mPendingBackups.values()) {
|
||||||
|
Log.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Schedule a backup pass in a few minutes. As backup-eligible data
|
// Schedule a backup pass in a few minutes. As backup-eligible data
|
||||||
// keeps changing, continue to defer the backup pass until things
|
// keeps changing, continue to defer the backup pass until things
|
||||||
// settle down, to avoid extra overhead.
|
// settle down, to avoid extra overhead.
|
||||||
@ -402,22 +441,35 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
// that uid or package itself.
|
// that uid or package itself.
|
||||||
public void scheduleFullBackup(String packageName) throws RemoteException {
|
public void scheduleFullBackup(String packageName) throws RemoteException {
|
||||||
// !!! TODO: protect with a signature-or-system permission?
|
// !!! TODO: protect with a signature-or-system permission?
|
||||||
HashSet<ServiceInfo> targets = new HashSet<ServiceInfo>();
|
|
||||||
synchronized (mQueueLock) {
|
synchronized (mQueueLock) {
|
||||||
int numKeys = mBackupParticipants.size();
|
int numKeys = mBackupParticipants.size();
|
||||||
for (int index = 0; index < numKeys; index++) {
|
for (int index = 0; index < numKeys; index++) {
|
||||||
int uid = mBackupParticipants.keyAt(index);
|
int uid = mBackupParticipants.keyAt(index);
|
||||||
HashSet<ServiceInfo> servicesAtUid = mBackupParticipants.get(uid);
|
HashSet<ApplicationInfo> servicesAtUid = mBackupParticipants.get(uid);
|
||||||
for (ServiceInfo service: servicesAtUid) {
|
for (ApplicationInfo app: servicesAtUid) {
|
||||||
if (service.packageName.equals(packageName)) {
|
if (app.packageName.equals(packageName)) {
|
||||||
mPendingBackups.put(new ComponentName(service.packageName, service.name),
|
mPendingBackups.put(app, new BackupRequest(app, true));
|
||||||
new BackupRequest(service, true));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Callback: a requested backup agent has been instantiated
|
||||||
|
public void agentConnected(String packageName, IBinder agentBinder) {
|
||||||
|
Log.d(TAG, "agentConnected pkg=" + packageName + " agent=" + agentBinder);
|
||||||
|
IBackupAgent bs = IBackupAgent.Stub.asInterface(agentBinder);
|
||||||
|
processOneBackup(packageName, bs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback: a backup agent has failed to come up, or has unexpectedly quit.
|
||||||
|
// If the agent failed to come up in the first place, the agentBinder argument
|
||||||
|
// will be null.
|
||||||
|
public void agentDisconnected(String packageName) {
|
||||||
|
// TODO: handle backup being interrupted
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||||
@ -428,12 +480,18 @@ class BackupManagerService extends IBackupManager.Stub {
|
|||||||
int uid = mBackupParticipants.keyAt(i);
|
int uid = mBackupParticipants.keyAt(i);
|
||||||
pw.print(" uid: ");
|
pw.print(" uid: ");
|
||||||
pw.println(uid);
|
pw.println(uid);
|
||||||
HashSet<ServiceInfo> services = mBackupParticipants.valueAt(i);
|
HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
|
||||||
for (ServiceInfo s: services) {
|
for (ApplicationInfo app: participants) {
|
||||||
pw.print(" ");
|
pw.print(" ");
|
||||||
pw.println(s.toString());
|
pw.println(app.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pw.println("Pending:");
|
||||||
|
Iterator<BackupRequest> br = mPendingBackups.values().iterator();
|
||||||
|
while (br.hasNext()) {
|
||||||
|
pw.print(" ");
|
||||||
|
pw.println(br);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ import android.app.IThumbnailReceiver;
|
|||||||
import android.app.Instrumentation;
|
import android.app.Instrumentation;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.app.ResultInfo;
|
import android.app.ResultInfo;
|
||||||
|
import android.backup.IBackupManager;
|
||||||
import android.content.ActivityNotFoundException;
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.ContentResolver;
|
import android.content.ContentResolver;
|
||||||
@ -131,6 +132,7 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
static final boolean DEBUG_PROCESSES = localLOGV || false;
|
static final boolean DEBUG_PROCESSES = localLOGV || false;
|
||||||
static final boolean DEBUG_USER_LEAVING = localLOGV || false;
|
static final boolean DEBUG_USER_LEAVING = localLOGV || false;
|
||||||
static final boolean DEBUG_RESULTS = localLOGV || false;
|
static final boolean DEBUG_RESULTS = localLOGV || false;
|
||||||
|
static final boolean DEBUG_BACKUP = localLOGV || true;
|
||||||
static final boolean VALIDATE_TOKENS = false;
|
static final boolean VALIDATE_TOKENS = false;
|
||||||
static final boolean SHOW_ACTIVITY_START_TIME = true;
|
static final boolean SHOW_ACTIVITY_START_TIME = true;
|
||||||
|
|
||||||
@ -632,6 +634,12 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
final ArrayList<ServiceRecord> mStoppingServices
|
final ArrayList<ServiceRecord> mStoppingServices
|
||||||
= new ArrayList<ServiceRecord>();
|
= new ArrayList<ServiceRecord>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backup/restore process management
|
||||||
|
*/
|
||||||
|
String mBackupAppName = null;
|
||||||
|
BackupRecord mBackupTarget = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of PendingThumbnailsRecord objects of clients who are still
|
* List of PendingThumbnailsRecord objects of clients who are still
|
||||||
* waiting to receive all of the thumbnails for a task.
|
* waiting to receive all of the thumbnails for a task.
|
||||||
@ -4669,6 +4677,16 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
mPendingBroadcast = null;
|
mPendingBroadcast = null;
|
||||||
scheduleBroadcastsLocked();
|
scheduleBroadcastsLocked();
|
||||||
}
|
}
|
||||||
|
if (mBackupTarget != null && mBackupTarget.app.pid == pid) {
|
||||||
|
Log.w(TAG, "Unattached app died before backup, skipping");
|
||||||
|
try {
|
||||||
|
IBackupManager bm = IBackupManager.Stub.asInterface(
|
||||||
|
ServiceManager.getService(Context.BACKUP_SERVICE));
|
||||||
|
bm.agentDisconnected(app.info.packageName);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
// Can't happen; the backup manager is local
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "Spurious process start timeout - pid not known for " + app);
|
Log.w(TAG, "Spurious process start timeout - pid not known for " + app);
|
||||||
}
|
}
|
||||||
@ -4757,11 +4775,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
mWaitForDebugger = mOrigWaitForDebugger;
|
mWaitForDebugger = mOrigWaitForDebugger;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// If the app is being launched for restore or full backup, set it up specially
|
||||||
|
boolean isRestrictedBackupMode = false;
|
||||||
|
if (mBackupTarget != null && mBackupAppName.equals(processName)) {
|
||||||
|
isRestrictedBackupMode = (mBackupTarget.backupMode == BackupRecord.RESTORE)
|
||||||
|
|| (mBackupTarget.backupMode == BackupRecord.BACKUP_FULL);
|
||||||
|
}
|
||||||
thread.bindApplication(processName, app.instrumentationInfo != null
|
thread.bindApplication(processName, app.instrumentationInfo != null
|
||||||
? app.instrumentationInfo : app.info, providers,
|
? app.instrumentationInfo : app.info, providers,
|
||||||
app.instrumentationClass, app.instrumentationProfileFile,
|
app.instrumentationClass, app.instrumentationProfileFile,
|
||||||
app.instrumentationArguments, app.instrumentationWatcher, testMode,
|
app.instrumentationArguments, app.instrumentationWatcher, testMode,
|
||||||
mConfiguration, getCommonServicesLocked());
|
isRestrictedBackupMode, mConfiguration, getCommonServicesLocked());
|
||||||
updateLRUListLocked(app, false);
|
updateLRUListLocked(app, false);
|
||||||
app.lastRequestedGc = SystemClock.uptimeMillis();
|
app.lastRequestedGc = SystemClock.uptimeMillis();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -4842,6 +4866,17 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check whether the next backup agent is in this process...
|
||||||
|
if (!badApp && mBackupTarget != null && mBackupTarget.appInfo.uid == app.info.uid) {
|
||||||
|
if (DEBUG_BACKUP) Log.v(TAG, "New app is backup target, launching agent for " + app);
|
||||||
|
try {
|
||||||
|
thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, mBackupTarget.backupMode);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "Exception scheduling backup agent creation: ");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (badApp) {
|
if (badApp) {
|
||||||
// todo: Also need to kill application to deal with all
|
// todo: Also need to kill application to deal with all
|
||||||
// kinds of exceptions.
|
// kinds of exceptions.
|
||||||
@ -9118,6 +9153,18 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
app.receivers.clear();
|
app.receivers.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If the app is undergoing backup, tell the backup manager about it
|
||||||
|
if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) {
|
||||||
|
if (DEBUG_BACKUP) Log.d(TAG, "App " + mBackupTarget.appInfo + " died during backup");
|
||||||
|
try {
|
||||||
|
IBackupManager bm = IBackupManager.Stub.asInterface(
|
||||||
|
ServiceManager.getService(Context.BACKUP_SERVICE));
|
||||||
|
bm.agentDisconnected(app.info.packageName);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
// can't happen; backup manager is local
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If the caller is restarting this app, then leave it in its
|
// If the caller is restarting this app, then leave it in its
|
||||||
// current lists and let the caller take care of it.
|
// current lists and let the caller take care of it.
|
||||||
if (restarting) {
|
if (restarting) {
|
||||||
@ -10233,6 +10280,105 @@ public final class ActivityManagerService extends ActivityManagerNative implemen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// BACKUP AND RESTORE
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
// Cause the target app to be launched if necessary and its backup agent
|
||||||
|
// instantiated. The backup agent will invoke backupAgentCreated() on the
|
||||||
|
// activity manager to announce its creation.
|
||||||
|
public boolean bindBackupAgent(ApplicationInfo app, int backupMode) {
|
||||||
|
if (DEBUG_BACKUP) Log.v(TAG, "startBackupAgent: app=" + app + " mode=" + backupMode);
|
||||||
|
enforceCallingPermission("android.permission.BACKUP", "startBackupAgent");
|
||||||
|
|
||||||
|
synchronized(this) {
|
||||||
|
// !!! TODO: currently no check here that we're already bound
|
||||||
|
BatteryStatsImpl.Uid.Pkg.Serv ss = null;
|
||||||
|
BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
|
||||||
|
synchronized (stats) {
|
||||||
|
ss = stats.getServiceStatsLocked(app.uid, app.packageName, app.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
BackupRecord r = new BackupRecord(ss, app, backupMode);
|
||||||
|
ComponentName hostingName = new ComponentName(app.packageName, app.backupAgentName);
|
||||||
|
// startProcessLocked() returns existing proc's record if it's already running
|
||||||
|
ProcessRecord proc = startProcessLocked(app.processName, app,
|
||||||
|
false, 0, "backup", hostingName);
|
||||||
|
if (proc == null) {
|
||||||
|
Log.e(TAG, "Unable to start backup agent process " + r);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
r.app = proc;
|
||||||
|
mBackupTarget = r;
|
||||||
|
mBackupAppName = app.packageName;
|
||||||
|
|
||||||
|
// If the process is already attached, schedule the creation of the backup agent now.
|
||||||
|
// If it is not yet live, this will be done when it attaches to the framework.
|
||||||
|
if (proc.thread != null) {
|
||||||
|
if (DEBUG_BACKUP) Log.v(TAG, "Agent proc already running: " + proc);
|
||||||
|
try {
|
||||||
|
proc.thread.scheduleCreateBackupAgent(app, backupMode);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
// !!! TODO: notify the backup manager that we crashed, or rely on
|
||||||
|
// death notices, or...?
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (DEBUG_BACKUP) Log.v(TAG, "Agent proc not running, waiting for attach");
|
||||||
|
}
|
||||||
|
// Invariants: at this point, the target app process exists and the application
|
||||||
|
// is either already running or in the process of coming up. mBackupTarget and
|
||||||
|
// mBackupAppName describe the app, so that when it binds back to the AM we
|
||||||
|
// know that it's scheduled for a backup-agent operation.
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A backup agent has just come up
|
||||||
|
public void backupAgentCreated(String agentPackageName, IBinder agent) {
|
||||||
|
if (DEBUG_BACKUP) Log.v(TAG, "backupAgentCreated: " + agentPackageName
|
||||||
|
+ " = " + agent);
|
||||||
|
|
||||||
|
synchronized(this) {
|
||||||
|
if (!agentPackageName.equals(mBackupAppName)) {
|
||||||
|
Log.e(TAG, "Backup agent created for " + agentPackageName + " but not requested!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
IBackupManager bm = IBackupManager.Stub.asInterface(
|
||||||
|
ServiceManager.getService(Context.BACKUP_SERVICE));
|
||||||
|
bm.agentConnected(agentPackageName, agent);
|
||||||
|
} catch (RemoteException e) {
|
||||||
|
// can't happen; the backup manager service is local
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.w(TAG, "Exception trying to deliver BackupAgent binding: ");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// done with this agent
|
||||||
|
public void unbindBackupAgent(ApplicationInfo appInfo) {
|
||||||
|
if (DEBUG_BACKUP) Log.v(TAG, "unbindBackupAgent: " + appInfo);
|
||||||
|
|
||||||
|
synchronized(this) {
|
||||||
|
if (!mBackupAppName.equals(appInfo.packageName)) {
|
||||||
|
Log.e(TAG, "Unbind of " + appInfo + " but is not the current backup target");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
mBackupTarget.app.thread.scheduleDestroyBackupAgent(appInfo);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "Exception when unbinding backup agent:");
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
mBackupTarget = null;
|
||||||
|
mBackupAppName = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
// =========================================================
|
// =========================================================
|
||||||
// BROADCASTS
|
// BROADCASTS
|
||||||
// =========================================================
|
// =========================================================
|
||||||
|
57
services/java/com/android/server/am/BackupRecord.java
Normal file
57
services/java/com/android/server/am/BackupRecord.java
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2009 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.server.am;
|
||||||
|
|
||||||
|
import com.android.internal.os.BatteryStatsImpl;
|
||||||
|
|
||||||
|
import android.content.pm.ApplicationInfo;
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
class BackupRecord {
|
||||||
|
// backup/restore modes
|
||||||
|
public static final int BACKUP_NORMAL = 0;
|
||||||
|
public static final int BACKUP_FULL = 1;
|
||||||
|
public static final int RESTORE = 2;
|
||||||
|
|
||||||
|
final BatteryStatsImpl.Uid.Pkg.Serv stats;
|
||||||
|
String stringName; // cached toString() output
|
||||||
|
final ApplicationInfo appInfo; // information about BackupAgent's app
|
||||||
|
final int backupMode; // full backup / incremental / restore
|
||||||
|
ProcessRecord app; // where this agent is running or null
|
||||||
|
|
||||||
|
// ----- Implementation -----
|
||||||
|
|
||||||
|
BackupRecord(BatteryStatsImpl.Uid.Pkg.Serv _agentStats, ApplicationInfo _appInfo,
|
||||||
|
int _backupMode) {
|
||||||
|
stats = _agentStats;
|
||||||
|
appInfo = _appInfo;
|
||||||
|
backupMode = _backupMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
if (stringName != null) {
|
||||||
|
return stringName;
|
||||||
|
}
|
||||||
|
StringBuilder sb = new StringBuilder(128);
|
||||||
|
sb.append("BackupRecord{")
|
||||||
|
.append(Integer.toHexString(System.identityHashCode(this)))
|
||||||
|
.append(' ').append(appInfo.packageName)
|
||||||
|
.append(' ').append(appInfo.name)
|
||||||
|
.append(' ').append(appInfo.backupAgentName).append('}');
|
||||||
|
return stringName = sb.toString();
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.android.backuptest">
|
package="com.android.backuptest">
|
||||||
<application>
|
<application android:backupAgent="BackupTestAgent">
|
||||||
<activity android:name="BackupTestActivity" android:label="_BackupTest">
|
<activity android:name="BackupTestActivity" android:label="_BackupTest">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
@ -8,10 +8,5 @@
|
|||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<service android:name="BackupTestService">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.backup.BackupService.SERVICE" />
|
|
||||||
</intent-filter>
|
|
||||||
</service>
|
|
||||||
</application>
|
</application>
|
||||||
</manifest>
|
</manifest>
|
||||||
|
@ -16,15 +16,15 @@
|
|||||||
|
|
||||||
package com.android.backuptest;
|
package com.android.backuptest;
|
||||||
|
|
||||||
import android.backup.BackupService;
|
import android.app.BackupAgent;
|
||||||
import android.backup.BackupDataOutput;
|
import android.backup.BackupDataOutput;
|
||||||
import android.backup.FileBackupHelper;
|
import android.backup.FileBackupHelper;
|
||||||
import android.os.ParcelFileDescriptor;
|
import android.os.ParcelFileDescriptor;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
public class BackupTestService extends BackupService
|
public class BackupTestAgent extends BackupAgent
|
||||||
{
|
{
|
||||||
static final String TAG = "BackupTestService";
|
static final String TAG = "BackupTestAgent";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
|
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,
|
Reference in New Issue
Block a user