Merge change 1089 into donut
* changes: Add a Backup Manager interface to request a full backup
This commit is contained in:
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user