From c7b31e3c3cd71129557d4bf9e3fbcebb9235aba5 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Wed, 10 Jun 2009 15:49:30 -0700 Subject: [PATCH] The rest of the basic flow for restore Also moved the processOneBackup() implementation into the Thread class that runs the backup sequence. --- .../android/server/BackupManagerService.java | 297 ++++++++++++------ .../server/am/ActivityManagerService.java | 13 +- 2 files changed, 211 insertions(+), 99 deletions(-) diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 42a895c1ac50c..f871496ff4b9d 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -25,6 +25,7 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.ApplicationInfo; +import android.content.pm.IPackageDataObserver; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -53,6 +54,7 @@ import com.android.internal.backup.IBackupTransport; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; +import java.io.IOException; import java.io.PrintWriter; import java.lang.String; import java.util.ArrayList; @@ -70,7 +72,10 @@ class BackupManagerService extends IBackupManager.Stub { private static final int MSG_RUN_BACKUP = 1; private static final int MSG_RUN_FULL_BACKUP = 2; - + + // Timeout interval for deciding that a bind or clear-data has taken too long + static final long TIMEOUT_INTERVAL = 10 * 1000; + private Context mContext; private PackageManager mPackageManager; private final IActivityManager mActivityManager; @@ -108,6 +113,10 @@ class BackupManagerService extends IBackupManager.Stub { private IBackupAgent mConnectedAgent; private volatile boolean mConnecting; + // A similar synchronicity mechanism around clearing apps' data for restore + private final Object mClearDataLock = new Object(); + private volatile boolean mClearingData; + private int mTransportId; private File mStateDir; @@ -207,80 +216,6 @@ class BackupManagerService extends IBackupManager.Stub { } } - void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) { - final String packageName = request.appInfo.packageName; - Log.d(TAG, "processOneBackup doBackup() on " + packageName); - - try { - // Look up the package info & signatures. This is first so that if it - // throws an exception, there's no file setup yet that would need to - // be unraveled. - PackageInfo packInfo = mPackageManager.getPackageInfo(packageName, - PackageManager.GET_SIGNATURES); - - // !!! TODO: get the state file dir from the transport - File savedStateName = new File(mStateDir, packageName); - File backupDataName = new File(mDataDir, packageName + ".data"); - File newStateName = new File(mStateDir, packageName + ".new"); - - // In a full backup, we pass a null ParcelFileDescriptor as - // the saved-state "file" - ParcelFileDescriptor savedState = (request.fullBackup) ? null - : ParcelFileDescriptor.open(savedStateName, - ParcelFileDescriptor.MODE_READ_ONLY | - ParcelFileDescriptor.MODE_CREATE); - - backupDataName.delete(); - ParcelFileDescriptor backupData = - ParcelFileDescriptor.open(backupDataName, - ParcelFileDescriptor.MODE_READ_WRITE | - ParcelFileDescriptor.MODE_CREATE); - - newStateName.delete(); - ParcelFileDescriptor newState = - ParcelFileDescriptor.open(newStateName, - ParcelFileDescriptor.MODE_READ_WRITE | - ParcelFileDescriptor.MODE_CREATE); - - // Run the target's backup pass - boolean success = false; - try { - agent.doBackup(savedState, backupData, newState); - success = true; - } finally { - if (savedState != null) { - savedState.close(); - } - backupData.close(); - newState.close(); - } - - // Now propagate the newly-backed-up data to the transport - if (success) { - if (DEBUG) Log.v(TAG, "doBackup() success; calling transport"); - backupData = - ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY); - int error = transport.performBackup(packInfo, backupData); - - // !!! TODO: After successful transport, delete the now-stale data - // and juggle the files so that next time the new state is passed - //backupDataName.delete(); - newStateName.renameTo(savedStateName); - } - } catch (NameNotFoundException e) { - Log.e(TAG, "Package not found on backup: " + packageName); - } catch (FileNotFoundException fnf) { - Log.w(TAG, "File not found on backup: "); - fnf.printStackTrace(); - } catch (RemoteException e) { - Log.d(TAG, "Remote target " + request.appInfo.packageName + " threw during backup:"); - e.printStackTrace(); - } catch (Exception e) { - Log.w(TAG, "Final exception guard in backup: "); - e.printStackTrace(); - } - } - // Add the backup agents in the given package to our set of known backup participants. // If 'packageName' is null, adds all backup agents in the whole system. void addPackageParticipantsLocked(String packageName) { @@ -424,11 +359,14 @@ class BackupManagerService extends IBackupManager.Stub { Log.d(TAG, "awaiting agent for " + app); // success; wait for the agent to arrive - while (mConnecting && mConnectedAgent == null) { + // only wait 10 seconds for the clear data to happen + long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL; + while (mConnecting && mConnectedAgent == null + && (System.currentTimeMillis() < timeoutMark)) { try { - mAgentConnectLock.wait(10000); + mAgentConnectLock.wait(5000); } catch (InterruptedException e) { - // just retry + // just bail return null; } } @@ -447,6 +385,37 @@ class BackupManagerService extends IBackupManager.Stub { return agent; } + // clear an application's data, blocking until the operation completes or times out + void clearApplicationDataSynchronous(String packageName) { + ClearDataObserver observer = new ClearDataObserver(); + + synchronized(mClearDataLock) { + mClearingData = true; + mPackageManager.clearApplicationUserData(packageName, observer); + + // only wait 10 seconds for the clear data to happen + long timeoutMark = System.currentTimeMillis() + TIMEOUT_INTERVAL; + while (mClearingData && (System.currentTimeMillis() < timeoutMark)) { + try { + mClearDataLock.wait(5000); + } catch (InterruptedException e) { + // won't happen, but still. + mClearingData = false; + } + } + } + } + + class ClearDataObserver extends IPackageDataObserver.Stub { + public void onRemoveCompleted(String packageName, boolean succeeded) + throws android.os.RemoteException { + synchronized(mClearDataLock) { + mClearingData = false; + notifyAll(); + } + } + } + // ----- Back up a set of applications via a worker thread ----- class PerformBackupThread extends Thread { @@ -508,13 +477,88 @@ class BackupManagerService extends IBackupManager.Stub { mActivityManager.unbindBackupAgent(request.appInfo); } catch (SecurityException ex) { // Try for the next one. - Log.d(TAG, "error in bind", ex); + Log.d(TAG, "error in bind/backup", ex); } catch (RemoteException e) { - // can't happen + Log.v(TAG, "bind/backup threw"); + e.printStackTrace(); } } } + + void processOneBackup(BackupRequest request, IBackupAgent agent, IBackupTransport transport) { + final String packageName = request.appInfo.packageName; + Log.d(TAG, "processOneBackup doBackup() on " + packageName); + + try { + // Look up the package info & signatures. This is first so that if it + // throws an exception, there's no file setup yet that would need to + // be unraveled. + PackageInfo packInfo = mPackageManager.getPackageInfo(packageName, + PackageManager.GET_SIGNATURES); + + // !!! TODO: get the state file dir from the transport + File savedStateName = new File(mStateDir, packageName); + File backupDataName = new File(mDataDir, packageName + ".data"); + File newStateName = new File(mStateDir, packageName + ".new"); + + // In a full backup, we pass a null ParcelFileDescriptor as + // the saved-state "file" + ParcelFileDescriptor savedState = (request.fullBackup) ? null + : ParcelFileDescriptor.open(savedStateName, + ParcelFileDescriptor.MODE_READ_ONLY | + ParcelFileDescriptor.MODE_CREATE); + + backupDataName.delete(); + ParcelFileDescriptor backupData = + ParcelFileDescriptor.open(backupDataName, + ParcelFileDescriptor.MODE_READ_WRITE | + ParcelFileDescriptor.MODE_CREATE); + + newStateName.delete(); + ParcelFileDescriptor newState = + ParcelFileDescriptor.open(newStateName, + ParcelFileDescriptor.MODE_READ_WRITE | + ParcelFileDescriptor.MODE_CREATE); + + // Run the target's backup pass + boolean success = false; + try { + agent.doBackup(savedState, backupData, newState); + success = true; + } finally { + if (savedState != null) { + savedState.close(); + } + backupData.close(); + newState.close(); + } + + // Now propagate the newly-backed-up data to the transport + if (success) { + if (DEBUG) Log.v(TAG, "doBackup() success; calling transport"); + backupData = + ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.MODE_READ_ONLY); + int error = transport.performBackup(packInfo, backupData); + + // !!! TODO: After successful transport, delete the now-stale data + // and juggle the files so that next time the new state is passed + //backupDataName.delete(); + newStateName.renameTo(savedStateName); + } + } catch (NameNotFoundException e) { + Log.e(TAG, "Package not found on backup: " + packageName); + } catch (FileNotFoundException fnf) { + Log.w(TAG, "File not found on backup: "); + fnf.printStackTrace(); + } catch (RemoteException e) { + Log.d(TAG, "Remote target " + request.appInfo.packageName + " threw during backup:"); + e.printStackTrace(); + } catch (Exception e) { + Log.w(TAG, "Final exception guard in backup: "); + e.printStackTrace(); + } + } } @@ -524,12 +568,12 @@ class BackupManagerService extends IBackupManager.Stub { // ApplicationInfo struct if it is; null if not. // // !!! TODO: also consider signatures - ApplicationInfo isRestorable(PackageInfo packageInfo) { + PackageInfo isRestorable(PackageInfo packageInfo) { if (packageInfo.packageName != null) { try { - ApplicationInfo app = mPackageManager.getApplicationInfo(packageInfo.packageName, + PackageInfo app = mPackageManager.getPackageInfo(packageInfo.packageName, PackageManager.GET_SIGNATURES); - if ((app.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { + if ((app.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) { return app; } } catch (Exception e) { @@ -541,6 +585,7 @@ class BackupManagerService extends IBackupManager.Stub { class PerformRestoreThread extends Thread { private IBackupTransport mTransport; + private RestoreSet mImage; PerformRestoreThread(IBackupTransport transport) { mTransport = transport; @@ -556,6 +601,7 @@ class BackupManagerService extends IBackupManager.Stub { * 3. for each app in the restore set: * 3.a. if it's restorable on this device, add it to the restore queue * 4. for each app in the restore queue: + * 4.a. clear the app data * 4.b. get the restore data for the app from the transport * 4.c. launch the backup agent for the app * 4.d. agent.doRestore() with the data from the server @@ -577,13 +623,14 @@ class BackupManagerService extends IBackupManager.Stub { RestoreSet[] images = mTransport.getAvailableRestoreSets(); if (images.length > 0) { // !!! for now we always take the first set - RestoreSet image = images[0]; + mImage = images[0]; // build the set of apps we will attempt to restore - PackageInfo[] packages = mTransport.getAppSet(image.token); - HashSet appsToRestore = new HashSet(); + PackageInfo[] packages = mTransport.getAppSet(mImage.token); + HashSet appsToRestore = new HashSet(); for (PackageInfo pkg: packages) { - ApplicationInfo app = isRestorable(pkg); + // get the real PackageManager idea of the package + PackageInfo app = isRestorable(pkg); if (app != null) { appsToRestore.add(app); } @@ -609,19 +656,23 @@ class BackupManagerService extends IBackupManager.Stub { } // restore each app in the queue - void doQueuedRestores(HashSet appsToRestore) { - for (ApplicationInfo app : appsToRestore) { + void doQueuedRestores(HashSet appsToRestore) { + for (PackageInfo app : appsToRestore) { Log.d(TAG, "starting agent for restore of " + app); - IBackupAgent agent = null; try { - agent = bindToAgentSynchronous(app, IApplicationThread.BACKUP_MODE_RESTORE); + // Remove the app's data first + clearApplicationDataSynchronous(app.packageName); + + // Now perform the restore into the clean app + IBackupAgent agent = bindToAgentSynchronous(app.applicationInfo, + IApplicationThread.BACKUP_MODE_RESTORE); if (agent != null) { processOneRestore(app, agent); } // unbind even on timeout, just in case - mActivityManager.unbindBackupAgent(app); + mActivityManager.unbindBackupAgent(app.applicationInfo); } catch (SecurityException ex) { // Try for the next one. Log.d(TAG, "error in bind", ex); @@ -632,9 +683,67 @@ class BackupManagerService extends IBackupManager.Stub { } } - // do the guts of a restore - void processOneRestore(ApplicationInfo app, IBackupAgent agent) { + // Do the guts of a restore of one application, derived from the 'mImage' + // restore set via the 'mTransport' transport. + void processOneRestore(PackageInfo app, IBackupAgent agent) { // !!! TODO: actually run the restore through mTransport + final String packageName = app.packageName; + + // !!! TODO: get the dirs from the transport + File backupDataName = new File(mDataDir, packageName + ".restore"); + backupDataName.delete(); + try { + ParcelFileDescriptor backupData = + ParcelFileDescriptor.open(backupDataName, + ParcelFileDescriptor.MODE_READ_WRITE | + ParcelFileDescriptor.MODE_CREATE); + + // Run the transport's restore pass + // Run the target's backup pass + int err = -1; + try { + err = mTransport.getRestoreData(mImage.token, app, backupData); + } catch (RemoteException e) { + // can't happen + } finally { + backupData.close(); + } + + // Okay, we have the data. Now have the agent do the restore. + File newStateName = new File(mStateDir, packageName + ".new"); + ParcelFileDescriptor newState = + ParcelFileDescriptor.open(newStateName, + ParcelFileDescriptor.MODE_READ_WRITE | + ParcelFileDescriptor.MODE_CREATE); + + backupData = ParcelFileDescriptor.open(backupDataName, + ParcelFileDescriptor.MODE_READ_ONLY); + + boolean success = false; + try { + agent.doRestore(backupData, newState); + success = true; + } catch (Exception e) { + Log.e(TAG, "Restore failed for " + packageName); + e.printStackTrace(); + } finally { + newState.close(); + backupData.close(); + } + + // if everything went okay, remember the recorded state now + if (success) { + File savedStateName = new File(mStateDir, packageName); + newStateName.renameTo(savedStateName); + } + } catch (FileNotFoundException fnfe) { + Log.v(TAG, "Couldn't open file for restore: " + fnfe); + } catch (IOException ioe) { + Log.e(TAG, "Unable to process restore file: " + ioe); + } catch (Exception e) { + Log.e(TAG, "Final exception guard in restore:"); + e.printStackTrace(); + } } } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 14dcfd5ab97c8..6a81178ee0d8a 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -10422,11 +10422,14 @@ public final class ActivityManagerService extends ActivityManagerNative implemen // Not backing this app up any more; reset its OOM adjustment updateOomAdjLocked(proc); - try { - proc.thread.scheduleDestroyBackupAgent(appInfo); - } catch (Exception e) { - Log.e(TAG, "Exception when unbinding backup agent:"); - e.printStackTrace(); + // If the app crashed during backup, 'thread' will be null here + if (proc.thread != null) { + try { + proc.thread.scheduleDestroyBackupAgent(appInfo); + } catch (Exception e) { + Log.e(TAG, "Exception when unbinding backup agent:"); + e.printStackTrace(); + } } } }