summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.xml10
-rw-r--r--core/java/android/backup/IBackupManager.aidl6
-rw-r--r--core/java/android/content/pm/ApplicationInfo.java2
-rw-r--r--core/java/android/content/pm/IPackageManager.aidl2
-rw-r--r--services/java/com/android/server/BackupManagerService.java120
-rw-r--r--services/java/com/android/server/PackageManagerService.java142
-rw-r--r--tests/backup/Android.mk3
7 files changed, 221 insertions, 64 deletions
diff --git a/api/current.xml b/api/current.xml
index 86043df..e123602 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -42639,6 +42639,16 @@
visibility="public"
>
</field>
+<field name="backupAgentName"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="className"
type="java.lang.String"
transient="false"
diff --git a/core/java/android/backup/IBackupManager.aidl b/core/java/android/backup/IBackupManager.aidl
index bf6c79f..d94b066 100644
--- a/core/java/android/backup/IBackupManager.aidl
+++ b/core/java/android/backup/IBackupManager.aidl
@@ -62,6 +62,12 @@ interface IBackupManager {
void agentDisconnected(String packageName);
/**
+ * Notify the Backup Manager Service that an application being installed will
+ * need a data-restore pass. This method is only invoked by the Package Manager.
+ */
+ void restoreAtInstall(String packageName, int token);
+
+ /**
* Enable/disable the backup service entirely. When disabled, no backup
* or restore operations will take place. Data-changed notifications will
* still be observed and collected, however, so that changes made while the
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 123d9b7..2e405c1 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -68,8 +68,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable {
* 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;
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index 7a02a98..f793a00 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -154,6 +154,8 @@ interface IPackageManager {
void installPackage(in Uri packageURI, IPackageInstallObserver observer, int flags,
in String installerPackageName);
+ void finishPackageInstall(int token);
+
/**
* Delete a package.
*
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index ea0fc65..f79a02a 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -17,6 +17,7 @@
package com.android.server;
import android.app.ActivityManagerNative;
+import android.app.ActivityThread;
import android.app.AlarmManager;
import android.app.IActivityManager;
import android.app.IApplicationThread;
@@ -34,6 +35,7 @@ import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
+import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
@@ -113,6 +115,7 @@ class BackupManagerService extends IBackupManager.Stub {
private Context mContext;
private PackageManager mPackageManager;
+ IPackageManager mPackageManagerBinder;
private IActivityManager mActivityManager;
private PowerManager mPowerManager;
private AlarmManager mAlarmManager;
@@ -179,13 +182,15 @@ class BackupManagerService extends IBackupManager.Stub {
public IRestoreObserver observer;
public long token;
public PackageInfo pkgInfo;
+ public int pmToken; // in post-install restore, the PM's token for this transaction
RestoreParams(IBackupTransport _transport, IRestoreObserver _obs,
- long _token, PackageInfo _pkg) {
+ long _token, PackageInfo _pkg, int _pmToken) {
transport = _transport;
observer = _obs;
token = _token;
pkgInfo = _pkg;
+ pmToken = _pmToken;
}
RestoreParams(IBackupTransport _transport, IRestoreObserver _obs, long _token) {
@@ -193,6 +198,7 @@ class BackupManagerService extends IBackupManager.Stub {
observer = _obs;
token = _token;
pkgInfo = null;
+ pmToken = 0;
}
}
@@ -302,7 +308,7 @@ class BackupManagerService extends IBackupManager.Stub {
RestoreParams params = (RestoreParams)msg.obj;
Log.d(TAG, "MSG_RUN_RESTORE observer=" + params.observer);
(new PerformRestoreTask(params.transport, params.observer,
- params.token, params.pkgInfo)).run();
+ params.token, params.pkgInfo, params.pmToken)).run();
break;
}
@@ -349,6 +355,7 @@ class BackupManagerService extends IBackupManager.Stub {
public BackupManagerService(Context context) {
mContext = context;
mPackageManager = context.getPackageManager();
+ mPackageManagerBinder = ActivityThread.getPackageManager();
mActivityManager = ActivityManagerNative.getDefault();
mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
@@ -1115,6 +1122,18 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
+ // Get the restore-set token for the best-available restore set for this package:
+ // the active set if possible, else the ancestral one. Returns zero if none available.
+ long getAvailableRestoreToken(String packageName) {
+ long token = mAncestralToken;
+ synchronized (mQueueLock) {
+ if (mEverStoredApps.contains(packageName)) {
+ token = mCurrentToken;
+ }
+ }
+ return token;
+ }
+
// -----
// Utility methods used by the asynchronous-with-timeout backup/restore operations
boolean waitUntilOperationComplete(int token) {
@@ -1132,12 +1151,14 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
mBackupHandler.removeMessages(MSG_TIMEOUT);
- if (DEBUG) Log.v(TAG, "operation " + token + " complete: finalState=" + finalState);
+ if (DEBUG) Log.v(TAG, "operation " + Integer.toHexString(token)
+ + " complete: finalState=" + finalState);
return finalState == OP_ACKNOWLEDGED;
}
void prepareOperationTimeout(int token, long interval) {
- if (DEBUG) Log.v(TAG, "starting timeout: token=" + token + " interval=" + interval);
+ if (DEBUG) Log.v(TAG, "starting timeout: token=" + Integer.toHexString(token)
+ + " interval=" + interval);
mCurrentOperations.put(token, OP_PENDING);
Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0);
mBackupHandler.sendMessageDelayed(msg, interval);
@@ -1476,6 +1497,7 @@ class BackupManagerService extends IBackupManager.Stub {
private long mToken;
private PackageInfo mTargetPackage;
private File mStateDir;
+ private int mPmToken;
class RestoreRequest {
public PackageInfo app;
@@ -1488,11 +1510,12 @@ class BackupManagerService extends IBackupManager.Stub {
}
PerformRestoreTask(IBackupTransport transport, IRestoreObserver observer,
- long restoreSetToken, PackageInfo targetPackage) {
+ long restoreSetToken, PackageInfo targetPackage, int pmToken) {
mTransport = transport;
mObserver = observer;
mToken = restoreSetToken;
mTargetPackage = targetPackage;
+ mPmToken = pmToken;
try {
mStateDir = new File(mBaseStateDir, transport.transportDirName());
@@ -1505,25 +1528,7 @@ class BackupManagerService extends IBackupManager.Stub {
long startRealtime = SystemClock.elapsedRealtime();
if (DEBUG) Log.v(TAG, "Beginning restore process mTransport=" + mTransport
+ " mObserver=" + mObserver + " mToken=" + Long.toHexString(mToken)
- + " mTargetPackage=" + mTargetPackage);
- /**
- * Restore sequence:
- *
- * 1. get the restore set description for our identity
- * 2. for each app in the restore set:
- * 2.a. if it's restorable on this device, add it to the restore queue
- * 3. for each app in the restore queue:
- * 3.a. clear the app data
- * 3.b. get the restore data for the app from the transport
- * 3.c. launch the backup agent for the app
- * 3.d. agent.doRestore() with the data from the server
- * 3.e. unbind the agent [and kill the app?]
- * 4. shut down the transport
- *
- * On errors, we try our best to recover and move on to the next
- * application, but if necessary we abort the whole operation --
- * the user is waiting, after all.
- */
+ + " mTargetPackage=" + mTargetPackage + " mPmToken=" + mPmToken);
PackageManagerBackupAgent pmAgent = null;
int error = -1; // assume error
@@ -1609,6 +1614,7 @@ class BackupManagerService extends IBackupManager.Stub {
EventLog.writeEvent(EventLogTags.RESTORE_TRANSPORT_FAILURE);
return;
} else if (packageName.equals("")) {
+ if (DEBUG) Log.v(TAG, "No next package, finishing restore");
break;
}
@@ -1733,6 +1739,15 @@ class BackupManagerService extends IBackupManager.Stub {
writeRestoreTokens();
}
+ // We must under all circumstances tell the Package Manager to
+ // proceed with install notifications if it's waiting for us.
+ if (mPmToken > 0) {
+ if (DEBUG) Log.v(TAG, "finishing PM token " + mPmToken);
+ try {
+ mPackageManagerBinder.finishPackageInstall(mPmToken);
+ } catch (RemoteException e) { /* can't happen */ }
+ }
+
// done; we can finally release the wakelock
mWakelock.release();
}
@@ -2169,7 +2184,7 @@ class BackupManagerService extends IBackupManager.Stub {
public String getCurrentTransport() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getCurrentTransport");
- Log.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
+ if (DEBUG) Log.v(TAG, "... getCurrentTransport() returning " + mCurrentTransport);
return mCurrentTransport;
}
@@ -2248,6 +2263,45 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
+ // An application being installed will need a restore pass, then the Package Manager
+ // will need to be told when the restore is finished.
+ public void restoreAtInstall(String packageName, int token) {
+ if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+ Log.w(TAG, "Non-system process uid=" + Binder.getCallingUid()
+ + " attemping install-time restore");
+ return;
+ }
+
+ long restoreSet = getAvailableRestoreToken(packageName);
+ if (DEBUG) Log.v(TAG, "restoreAtInstall pkg=" + packageName
+ + " token=" + Integer.toHexString(token));
+
+ if (restoreSet != 0) {
+ // okay, we're going to attempt a restore of this package from this restore set.
+ // The eventual message back into the Package Manager to run the post-install
+ // steps for 'token' will be issued from the restore handling code.
+
+ // We can use a synthetic PackageInfo here because:
+ // 1. We know it's valid, since the Package Manager supplied the name
+ // 2. Only the packageName field will be used by the restore code
+ PackageInfo pkg = new PackageInfo();
+ pkg.packageName = packageName;
+
+ mWakelock.acquire();
+ Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+ msg.obj = new RestoreParams(getTransport(mCurrentTransport), null,
+ restoreSet, pkg, token);
+ mBackupHandler.sendMessage(msg);
+ } else {
+ // No way to attempt a restore; just tell the Package Manager to proceed
+ // with the post-install handling for this package.
+ if (DEBUG) Log.v(TAG, "No restore set -- skipping restore");
+ try {
+ mPackageManagerBinder.finishPackageInstall(token);
+ } catch (RemoteException e) { /* can't happen */ }
+ }
+ }
+
// Hand off a restore session
public IRestoreSession beginRestoreSession(String transport) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "beginRestoreSession");
@@ -2266,7 +2320,7 @@ class BackupManagerService extends IBackupManager.Stub {
// completed the given outstanding asynchronous backup/restore operation.
public void opComplete(int token) {
synchronized (mCurrentOpLock) {
- if (DEBUG) Log.v(TAG, "opComplete: " + token);
+ if (DEBUG) Log.v(TAG, "opComplete: " + Integer.toHexString(token));
mCurrentOperations.put(token, OP_ACKNOWLEDGED);
mCurrentOpLock.notifyAll();
}
@@ -2289,6 +2343,7 @@ class BackupManagerService extends IBackupManager.Stub {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
"getAvailableRestoreSets");
+ long oldId = Binder.clearCallingIdentity();
try {
if (mRestoreTransport == null) {
Log.w(TAG, "Null transport getting restore sets");
@@ -2302,6 +2357,8 @@ class BackupManagerService extends IBackupManager.Stub {
} catch (Exception e) {
Log.e(TAG, "Error in getAvailableRestoreSets", e);
return null;
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
}
}
@@ -2360,12 +2417,7 @@ class BackupManagerService extends IBackupManager.Stub {
// So far so good; we're allowed to try to restore this package. Now
// check whether there is data for it in the current dataset, falling back
// to the ancestral dataset if not.
- long token = mAncestralToken;
- synchronized (mQueueLock) {
- if (mEverStoredApps.contains(packageName)) {
- token = mCurrentToken;
- }
- }
+ long token = getAvailableRestoreToken(packageName);
// If we didn't come up with a place to look -- no ancestral dataset and
// the app has never been backed up from this device -- there's nothing
@@ -2378,7 +2430,7 @@ class BackupManagerService extends IBackupManager.Stub {
long oldId = Binder.clearCallingIdentity();
mWakelock.acquire();
Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
- msg.obj = new RestoreParams(mRestoreTransport, observer, token, app);
+ msg.obj = new RestoreParams(mRestoreTransport, observer, token, app, 0);
mBackupHandler.sendMessage(msg);
Binder.restoreCallingIdentity(oldId);
return 0;
@@ -2391,12 +2443,14 @@ class BackupManagerService extends IBackupManager.Stub {
if (DEBUG) Log.d(TAG, "endRestoreSession");
synchronized (this) {
+ long oldId = Binder.clearCallingIdentity();
try {
if (mRestoreTransport != null) mRestoreTransport.finishRestore();
} catch (Exception e) {
Log.e(TAG, "Error in finishRestore", e);
} finally {
mRestoreTransport = null;
+ Binder.restoreCallingIdentity(oldId);
}
}
diff --git a/services/java/com/android/server/PackageManagerService.java b/services/java/com/android/server/PackageManagerService.java
index 252f2a6..90a606d 100644
--- a/services/java/com/android/server/PackageManagerService.java
+++ b/services/java/com/android/server/PackageManagerService.java
@@ -29,6 +29,7 @@ import org.xmlpull.v1.XmlSerializer;
import android.app.ActivityManagerNative;
import android.app.IActivityManager;
+import android.backup.IBackupManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -123,6 +124,7 @@ class PackageManagerService extends IPackageManager.Stub {
private static final boolean DEBUG_SETTINGS = false;
private static final boolean DEBUG_PREFERRED = false;
private static final boolean DEBUG_UPGRADE = false;
+ private static final boolean DEBUG_INSTALL = false;
private static final boolean MULTIPLE_APPLICATION_UIDS = true;
private static final int RADIO_UID = Process.PHONE_UID;
@@ -310,6 +312,7 @@ class PackageManagerService extends IPackageManager.Stub {
static final int MCS_UNBIND = 6;
static final int START_CLEANING_PACKAGE = 7;
static final int FIND_INSTALL_LOC = 8;
+ static final int POST_INSTALL = 9;
// Delay time in millisecs
static final int BROADCAST_DELAY = 10 * 1000;
private ServiceConnection mDefContainerConn = new ServiceConnection() {
@@ -324,6 +327,20 @@ class PackageManagerService extends IPackageManager.Stub {
}
};
+ // Recordkeeping of restore-after-install operations that are currently in flight
+ // between the Package Manager and the Backup Manager
+ class PostInstallData {
+ public InstallArgs args;
+ public PackageInstalledInfo res;
+
+ PostInstallData(InstallArgs _a, PackageInstalledInfo _r) {
+ args = _a;
+ res = _r;
+ }
+ };
+ final SparseArray<PostInstallData> mRunningInstalls = new SparseArray<PostInstallData>();
+ int mNextInstallToken = 1; // nonzero; will be wrapped back to 1 when ++ overflows
+
class PackageHandler extends Handler {
final ArrayList<HandlerParams> mPendingInstalls =
new ArrayList<HandlerParams>();
@@ -422,6 +439,51 @@ class PackageManagerService extends IPackageManager.Stub {
}
startCleaningPackages();
} break;
+ case POST_INSTALL: {
+ if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
+ PostInstallData data = mRunningInstalls.get(msg.arg1);
+ mRunningInstalls.delete(msg.arg1);
+
+ if (data != null) {
+ InstallArgs args = data.args;
+ PackageInstalledInfo res = data.res;
+
+ if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
+ res.removedInfo.sendBroadcast(false, true);
+ Bundle extras = new Bundle(1);
+ extras.putInt(Intent.EXTRA_UID, res.uid);
+ final boolean update = res.removedInfo.removedPackage != null;
+ if (update) {
+ extras.putBoolean(Intent.EXTRA_REPLACING, true);
+ }
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
+ res.pkg.applicationInfo.packageName,
+ extras);
+ if (update) {
+ sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
+ res.pkg.applicationInfo.packageName,
+ extras);
+ }
+ if (res.removedInfo.args != null) {
+ // Remove the replaced package's older resources safely now
+ synchronized (mInstallLock) {
+ res.removedInfo.args.doPostDeleteLI(true);
+ }
+ }
+ }
+ Runtime.getRuntime().gc();
+
+ if (args.observer != null) {
+ try {
+ args.observer.packageInstalled(res.name, res.returnCode);
+ } catch (RemoteException e) {
+ Log.i(TAG, "Observer no longer exists.");
+ }
+ }
+ } else {
+ Log.e(TAG, "Bogus post-install token " + msg.arg1);
+ }
+ } break;
}
}
}
@@ -4253,6 +4315,12 @@ class PackageManagerService extends IPackageManager.Stub {
mHandler.sendMessage(msg);
}
+ public void finishPackageInstall(int token) {
+ if (DEBUG_INSTALL) Log.v(TAG, "BM finishing package install for " + token);
+ Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
+ mHandler.sendMessage(msg);
+ }
+
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
// Queue up an async operation since the package installation may take a little while.
mHandler.post(new Runnable() {
@@ -4271,38 +4339,54 @@ class PackageManagerService extends IPackageManager.Stub {
}
args.doPostInstall(res.returnCode);
}
- // There appears to be a subtle deadlock condition if the sendPackageBroadcast
- // call appears in the synchronized block above.
- if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
- res.removedInfo.sendBroadcast(false, true);
- Bundle extras = new Bundle(1);
- extras.putInt(Intent.EXTRA_UID, res.uid);
- final boolean update = res.removedInfo.removedPackage != null;
- if (update) {
- extras.putBoolean(Intent.EXTRA_REPLACING, true);
- }
- sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
- res.pkg.applicationInfo.packageName,
- extras);
- if (update) {
- sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
- res.pkg.applicationInfo.packageName,
- extras);
- }
- if (res.removedInfo.args != null) {
- // Remove the replaced package's older resources safely now
- synchronized (mInstallLock) {
- res.removedInfo.args.doPostDeleteLI(true);
+
+ // A restore should be performed at this point if (a) the install
+ // succeeded, (b) the operation is not an update, and (c) the new
+ // package has a backupAgent defined.
+ final boolean update = res.removedInfo.removedPackage != null;
+ boolean doRestore = (!update && res.pkg.applicationInfo.backupAgentName != null);
+
+ // Set up the post-install work request bookkeeping. This will be used
+ // and cleaned up by the post-install event handling regardless of whether
+ // there's a restore pass performed. Token values are >= 1.
+ int token;
+ if (mNextInstallToken < 0) mNextInstallToken = 1;
+ token = mNextInstallToken++;
+
+ PostInstallData data = new PostInstallData(args, res);
+ mRunningInstalls.put(token, data);
+ if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);
+
+ if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
+ // Pass responsibility to the Backup Manager. It will perform a
+ // restore if appropriate, then pass responsibility back to the
+ // Package Manager to run the post-install observer callbacks
+ // and broadcasts.
+ IBackupManager bm = IBackupManager.Stub.asInterface(
+ ServiceManager.getService(Context.BACKUP_SERVICE));
+ if (bm != null) {
+ if (DEBUG_INSTALL) Log.v(TAG, "token " + token
+ + " to BM for possible restore");
+ try {
+ bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
+ } catch (RemoteException e) {
+ // can't happen; the backup manager is local
+ } catch (Exception e) {
+ Log.e(TAG, "Exception trying to enqueue restore", e);
+ doRestore = false;
}
+ } else {
+ Log.e(TAG, "Backup Manager not found!");
+ doRestore = false;
}
}
- Runtime.getRuntime().gc();
- if (args.observer != null) {
- try {
- args.observer.packageInstalled(res.name, res.returnCode);
- } catch (RemoteException e) {
- Log.i(TAG, "Observer no longer exists.");
- }
+
+ if (!doRestore) {
+ // No restore possible, or the Backup Manager was mysteriously not
+ // available -- just fire the post-install work request directly.
+ if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);
+ Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
+ mHandler.sendMessage(msg);
}
}
});
diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk
index 0813c35..498ec40 100644
--- a/tests/backup/Android.mk
+++ b/tests/backup/Android.mk
@@ -23,6 +23,7 @@ LOCAL_SRC_FILES := \
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := backup_helper_test
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
LOCAL_SHARED_LIBRARIES := libutils
include $(BUILD_EXECUTABLE)
@@ -33,6 +34,8 @@ include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_PACKAGE_NAME := BackupTest