Merge change 1089 into donut

* changes:
  Add a Backup Manager interface to request a full backup
This commit is contained in:
Android (Google) Code Review
2009-05-06 12:18:21 -07:00
2 changed files with 59 additions and 16 deletions

View File

@@ -29,7 +29,13 @@ package android.backup;
interface IBackupManager { interface IBackupManager {
/** /**
* Tell the system service that the caller has made changes to its * Tell the system service that the caller has made changes to its
* data, and therefore needs to undergo a backup pass. * data, and therefore needs to undergo an incremental backup pass.
*/ */
oneway void dataChanged(String packageName); oneway void dataChanged(String packageName);
/**
* Schedule a full backup of the given package.
* !!! TODO: protect with a signature-or-system permission?
*/
oneway void scheduleFullBackup(String packageName);
} }

View File

@@ -58,7 +58,16 @@ class BackupManagerService extends IBackupManager.Stub {
private SparseArray<HashSet<ServiceInfo>> mBackupParticipants private SparseArray<HashSet<ServiceInfo>> mBackupParticipants
= new SparseArray<HashSet<ServiceInfo>>(); = new SparseArray<HashSet<ServiceInfo>>();
// set of backup services that have pending changes // set of backup services that have pending changes
private HashSet<ServiceInfo> mPendingBackups = new HashSet<ServiceInfo>(); private class BackupRequest {
public ServiceInfo service;
public boolean fullBackup;
BackupRequest(ServiceInfo svc, boolean isFull) {
service = svc;
fullBackup = isFull;
}
}
private HashSet<BackupRequest> mPendingBackups = new HashSet<BackupRequest>();
private final Object mQueueLock = new Object(); private final Object mQueueLock = new Object();
private File mStateDir; private File mStateDir;
@@ -77,21 +86,21 @@ class BackupManagerService extends IBackupManager.Stub {
case MSG_RUN_BACKUP: case MSG_RUN_BACKUP:
{ {
// snapshot the pending-backup set and work on that // snapshot the pending-backup set and work on that
HashSet<ServiceInfo> queue; HashSet<BackupRequest> queue;
synchronized (mQueueLock) { synchronized (mQueueLock) {
queue = mPendingBackups; queue = mPendingBackups;
mPendingBackups = new HashSet<ServiceInfo>(); mPendingBackups = new HashSet<BackupRequest>();
// !!! TODO: start a new backup-queue journal file too // !!! TODO: start a new backup-queue journal file too
} }
// Walk the set of pending backups, setting up the relevant files and // Walk the set of pending backups, setting up the relevant files and
// invoking the backup service in each participant // invoking the backup service in each participant
Intent backupIntent = new Intent(BackupService.SERVICE_ACTION); Intent backupIntent = new Intent(BackupService.SERVICE_ACTION);
for (ServiceInfo service : queue) { for (BackupRequest request : queue) {
mBinding = true; mBinding = true;
mTargetService = null; mTargetService = null;
backupIntent.setClassName(service.packageName, service.name); backupIntent.setClassName(request.service.packageName, request.service.name);
Log.d(TAG, "binding to " + backupIntent); Log.d(TAG, "binding to " + backupIntent);
if (mContext.bindService(backupIntent, this, 0)) { if (mContext.bindService(backupIntent, this, 0)) {
synchronized (mBindSignaller) { synchronized (mBindSignaller) {
@@ -106,15 +115,25 @@ class BackupManagerService extends IBackupManager.Stub {
try { try {
Log.d(TAG, "invoking doBackup() on " + backupIntent); Log.d(TAG, "invoking doBackup() on " + backupIntent);
File savedStateName = new File(mStateDir, service.packageName); // !!! TODO right now these naming schemes limit applications to
File backupDataName = new File(mDataDir, service.packageName + ".data"); // one backup service per package
File newStateName = new File(mStateDir, service.packageName + ".new"); File savedStateName = new File(mStateDir,
request.service.packageName);
ParcelFileDescriptor savedState = File dummyName = new File(mStateDir, "#####");
ParcelFileDescriptor.open(savedStateName, File backupDataName = new File(mDataDir,
ParcelFileDescriptor.MODE_READ_ONLY | request.service.packageName + ".data");
ParcelFileDescriptor.MODE_CREATE); File newStateName = new File(mStateDir,
request.service.packageName + ".new");
// In a full backup, we pass a file that is never writeable, hence
// is always zero-sized, as a sentinel to the callee that they must
// write all of their data.
ParcelFileDescriptor savedState =
ParcelFileDescriptor.open(
(request.fullBackup) ? savedStateName : dummyName,
ParcelFileDescriptor.MODE_READ_ONLY |
ParcelFileDescriptor.MODE_CREATE);
backupDataName.delete(); backupDataName.delete();
ParcelFileDescriptor backupData = ParcelFileDescriptor backupData =
ParcelFileDescriptor.open(backupDataName, ParcelFileDescriptor.open(backupDataName,
@@ -231,7 +250,7 @@ class BackupManagerService extends IBackupManager.Stub {
// packages associated with this uid // packages associated with this uid
if (service.packageName.equals(packageName)) { if (service.packageName.equals(packageName)) {
// add the caller to the set of pending backups // add the caller to the set of pending backups
if (mPendingBackups.add(service)) { if (mPendingBackups.add(new BackupRequest(service, false))) {
// !!! TODO: write to the pending-backup journal file in case of crash // !!! TODO: write to the pending-backup journal file in case of crash
} }
} }
@@ -240,9 +259,27 @@ class BackupManagerService extends IBackupManager.Stub {
// 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.
mBackupHandler.removeMessages(MSG_RUN_BACKUP);
mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, COLLECTION_INTERVAL); mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, COLLECTION_INTERVAL);
} }
} }
} }
// Schedule a backup pass for a given package, even if the caller is not part of
// that uid or package itself.
public void scheduleFullBackup(String packageName) throws RemoteException {
// !!! TODO: protect with a signature-or-system permission?
HashSet<ServiceInfo> targets = new HashSet<ServiceInfo>();
synchronized (mQueueLock) {
int numKeys = mBackupParticipants.size();
for (int index = 0; index < numKeys; index++) {
int uid = mBackupParticipants.keyAt(index);
HashSet<ServiceInfo> servicesAtUid = mBackupParticipants.get(uid);
for (ServiceInfo service: servicesAtUid) {
if (service.packageName.equals(packageName)) {
mPendingBackups.add(new BackupRequest(service, true));
}
}
}
}
}
} }