am 8ad0281: With this, the BackupService onBackup method is called.
Merge commit '8ad028117d4b99883bbc52b29f097b2fb1d9b0c2'
* commit '8ad028117d4b99883bbc52b29f097b2fb1d9b0c2':
With this, the BackupService onBackup method is called.
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index e582fb15..db1deae 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -46,6 +46,8 @@
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.lang.String;
+import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -74,137 +76,16 @@
fullBackup = isFull;
}
}
- private HashSet<BackupRequest> mPendingBackups = new HashSet<BackupRequest>();
+ // Backups that we haven't started yet.
+ private HashMap<ComponentName,BackupRequest> mPendingBackups = new HashMap();
+ // Backups that we have started. These are separate to prevent starvation
+ // if an app keeps re-enqueuing itself.
+ private ArrayList<BackupRequest> mBackupQueue;
private final Object mQueueLock = new Object();
private File mStateDir;
private File mDataDir;
- // ----- Handler that runs the actual backup process asynchronously -----
-
- private class BackupHandler extends Handler implements ServiceConnection {
- private volatile Object mBindSignaller = new Object();
- private volatile boolean mBinding = false;
- private IBackupService mTargetService = null;
-
- public void handleMessage(Message msg) {
-
- switch (msg.what) {
- case MSG_RUN_BACKUP:
- {
- // snapshot the pending-backup set and work on that
- HashSet<BackupRequest> queue;
- synchronized (mQueueLock) {
- queue = mPendingBackups;
- mPendingBackups = new HashSet<BackupRequest>();
- // !!! TODO: start a new backup-queue journal file too
- }
-
- // Walk the set of pending backups, setting up the relevant files and
- // invoking the backup service in each participant
- Intent backupIntent = new Intent(BackupService.SERVICE_ACTION);
- for (BackupRequest request : queue) {
- mBinding = true;
- mTargetService = null;
-
- backupIntent.setClassName(request.service.packageName, request.service.name);
- Log.d(TAG, "binding to " + backupIntent);
- if (mContext.bindService(backupIntent, this, 0)) {
- synchronized (mBindSignaller) {
- while (mTargetService == null && mBinding == true) {
- try {
- mBindSignaller.wait();
- } catch (InterruptedException e) {
- }
- }
- }
- if (mTargetService != null) {
- try {
- Log.d(TAG, "invoking doBackup() on " + backupIntent);
-
- // !!! TODO right now these naming schemes limit applications to
- // one backup service per package
- File savedStateName = new File(mStateDir,
- request.service.packageName);
- File backupDataName = new File(mDataDir,
- request.service.packageName + ".data");
- File newStateName = new File(mStateDir,
- request.service.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
- try {
- mTargetService.doBackup(savedState, backupData, newState);
- } finally {
- savedState.close();
- backupData.close();
- newState.close();
- }
-
- // !!! TODO: Now propagate the newly-backed-up data to the transport
-
- // !!! 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 (FileNotFoundException fnf) {
- Log.d(TAG, "File not found on backup: ");
- fnf.printStackTrace();
- } catch (RemoteException e) {
- Log.d(TAG, "Remote target " + backupIntent
- + " threw during backup:");
- e.printStackTrace();
- } catch (Exception e) {
- Log.w(TAG, "Final exception guard in backup: ");
- e.printStackTrace();
- }
- mContext.unbindService(this);
- }
- } else {
- Log.d(TAG, "Unable to bind to " + backupIntent);
- }
- }
- }
- break;
- }
- }
-
- public void onServiceConnected(ComponentName name, IBinder service) {
- synchronized (mBindSignaller) {
- mTargetService = IBackupService.Stub.asInterface(service);
- mBinding = false;
- mBindSignaller.notifyAll();
- }
- }
-
- public void onServiceDisconnected(ComponentName name) {
- synchronized (mBindSignaller) {
- mTargetService = null;
- mBinding = false;
- mBindSignaller.notifyAll();
- }
- }
- }
-
public BackupManagerService(Context context) {
mContext = context;
mPackageManager = context.getPackageManager();
@@ -268,6 +149,140 @@
}
};
+ // ----- Run the actual backup process asynchronously -----
+
+ private class BackupHandler extends Handler implements ServiceConnection {
+ public void handleMessage(Message msg) {
+
+ switch (msg.what) {
+ case MSG_RUN_BACKUP:
+ // snapshot the pending-backup set and work on that
+ synchronized (mQueueLock) {
+ mBackupQueue = new ArrayList();
+ for (BackupRequest b: mPendingBackups.values()) {
+ mBackupQueue.add(b);
+ }
+ mPendingBackups = new HashMap<ComponentName,BackupRequest>();
+ // !!! TODO: start a new backup-queue journal file too
+ // WARNING: If we crash after this line, anything in mPendingBackups will
+ // be lost. FIX THIS.
+ }
+ startOneService();
+ 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) {
+ // TODO: handle backup being interrupted
+ }
+ }
+
+ void startOneService() {
+ // Loop until we find someone to start or the queue empties out.
+ Intent intent = new Intent(BackupService.SERVICE_ACTION);
+ while (true) {
+ BackupRequest request;
+ synchronized (mQueueLock) {
+ int queueSize = mBackupQueue.size();
+ if (queueSize == 0) {
+ mBackupQueue = null;
+ // TODO: Anything else to do here?
+ return;
+ }
+ request = mBackupQueue.get(0);
+ // Take it off the queue when we're done.
+ }
+
+ intent.setClassName(request.service.packageName, request.service.name);
+ Log.d(TAG, "binding to " + intent);
+ try {
+ if (mContext.bindService(intent, mBackupHandler, Context.BIND_AUTO_CREATE)) {
+ Log.d(TAG, "awaiting service object for " + intent);
+ // success
+ return;
+ }
+ } catch (SecurityException ex) {
+ // Try for the next one.
+ Log.d(TAG, "error in bind", ex);
+ }
+ }
+ }
+
+ void processOneBackup(ComponentName name, IBackupService bs) {
+ try {
+ Log.d(TAG, "processOneBackup doBackup() on " + name);
+
+ BackupRequest request;
+ synchronized (mQueueLock) {
+ request = mBackupQueue.get(0);
+ }
+
+ // !!! TODO right now these naming schemes limit applications to
+ // one backup service per package
+ File savedStateName = new File(mStateDir, request.service.packageName);
+ File backupDataName = new File(mDataDir, request.service.packageName + ".data");
+ File newStateName = new File(mStateDir, request.service.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
+ try {
+ // TODO: Make this oneway
+ bs.doBackup(savedState, backupData, newState);
+ } finally {
+ if (savedState != null) {
+ savedState.close();
+ }
+ backupData.close();
+ newState.close();
+ }
+
+ // !!! TODO: Now propagate the newly-backed-up data to the transport
+
+ // !!! 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 (FileNotFoundException fnf) {
+ Log.d(TAG, "File not found on backup: ");
+ fnf.printStackTrace();
+ } catch (RemoteException e) {
+ Log.d(TAG, "Remote target " + name + " threw during backup:");
+ e.printStackTrace();
+ } catch (Exception e) {
+ Log.w(TAG, "Final exception guard in backup: ");
+ e.printStackTrace();
+ }
+ synchronized (mQueueLock) {
+ mBackupQueue.remove(0);
+ }
+ mContext.unbindService(mBackupHandler);
+ }
+
// Add the backup services in the given package to our set of known backup participants.
// If 'packageName' is null, adds all backup services in the system.
void addPackageParticipantsLocked(String packageName) {
@@ -305,7 +320,8 @@
removePackageParticipantsLockedInner(packageName, services);
}
- private void removePackageParticipantsLockedInner(String packageName, List<ResolveInfo> services) {
+ private void removePackageParticipantsLockedInner(String packageName,
+ List<ResolveInfo> services) {
for (ResolveInfo ri : services) {
if (packageName == null || ri.serviceInfo.packageName.equals(packageName)) {
int uid = ri.serviceInfo.applicationInfo.uid;
@@ -353,10 +369,11 @@
// validate the caller-supplied package name against the known set of
// packages associated with this uid
if (service.packageName.equals(packageName)) {
- // add the caller to the set of pending backups
- if (mPendingBackups.add(new BackupRequest(service, false))) {
- // !!! TODO: write to the pending-backup journal file in case of crash
- }
+ // Add the caller to the set of pending backups. If there is
+ // one already there, then overwrite it, but no harm done.
+ mPendingBackups.put(new ComponentName(service.packageName, service.name),
+ new BackupRequest(service, true));
+ // !!! TODO: write to the pending-backup journal file in case of crash
}
}
@@ -381,7 +398,8 @@
HashSet<ServiceInfo> servicesAtUid = mBackupParticipants.get(uid);
for (ServiceInfo service: servicesAtUid) {
if (service.packageName.equals(packageName)) {
- mPendingBackups.add(new BackupRequest(service, true));
+ mPendingBackups.put(new ComponentName(service.packageName, service.name),
+ new BackupRequest(service, true));
}
}
}
diff --git a/tests/backup/src/com/android/backuptest/BackupTestService.java b/tests/backup/src/com/android/backuptest/BackupTestService.java
index 99f3e55..c58c98b 100644
--- a/tests/backup/src/com/android/backuptest/BackupTestService.java
+++ b/tests/backup/src/com/android/backuptest/BackupTestService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.example.android.apis.app;
+package com.android.backuptest;
import android.backup.BackupService;
import android.os.ParcelFileDescriptor;