summaryrefslogtreecommitdiffstats
path: root/services/java/com/android/server/BackupManagerService.java
diff options
context:
space:
mode:
Diffstat (limited to 'services/java/com/android/server/BackupManagerService.java')
-rw-r--r--services/java/com/android/server/BackupManagerService.java496
1 files changed, 471 insertions, 25 deletions
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 6e76331..a334dbb 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -23,10 +23,14 @@ import android.app.IActivityManager;
import android.app.IApplicationThread;
import android.app.IBackupAgent;
import android.app.PendingIntent;
+import android.app.backup.BackupDataOutput;
+import android.app.backup.FullBackup;
import android.app.backup.RestoreSet;
import android.app.backup.IBackupManager;
+import android.app.backup.IFullBackupRestoreObserver;
import android.app.backup.IRestoreObserver;
import android.app.backup.IRestoreSession;
+import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
@@ -60,6 +64,9 @@ import android.util.EventLog;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseIntArray;
+import android.util.StringBuilderPrinter;
+
+import libcore.io.Libcore;
import com.android.internal.backup.BackupConstants;
import com.android.internal.backup.IBackupTransport;
@@ -81,10 +88,15 @@ import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
class BackupManagerService extends IBackupManager.Stub {
private static final String TAG = "BackupManagerService";
- private static final boolean DEBUG = false;
+ private static final boolean DEBUG = true;
+
+ // Name and current contents version of the full-backup manifest file
+ static final String BACKUP_MANIFEST_FILENAME = "_manifest";
+ static final int BACKUP_MANIFEST_VERSION = 1;
// How often we perform a backup pass. Privileged external callers can
// trigger an immediate pass.
@@ -108,14 +120,20 @@ class BackupManagerService extends IBackupManager.Stub {
private static final int MSG_RUN_GET_RESTORE_SETS = 6;
private static final int MSG_TIMEOUT = 7;
private static final int MSG_RESTORE_TIMEOUT = 8;
+ private static final int MSG_FULL_CONFIRMATION_TIMEOUT = 9;
+ private static final int MSG_RUN_FULL_RESTORE = 10;
// Timeout interval for deciding that a bind or clear-data has taken too long
static final long TIMEOUT_INTERVAL = 10 * 1000;
// Timeout intervals for agent backup & restore operations
static final long TIMEOUT_BACKUP_INTERVAL = 30 * 1000;
+ static final long TIMEOUT_FULL_BACKUP_INTERVAL = 5 * 60 * 1000;
static final long TIMEOUT_RESTORE_INTERVAL = 60 * 1000;
+ // User confirmation timeout for a full backup/restore operation
+ static final long TIMEOUT_FULL_CONFIRMATION = 30 * 1000;
+
private Context mContext;
private PackageManager mPackageManager;
IPackageManager mPackageManagerBinder;
@@ -138,15 +156,13 @@ class BackupManagerService extends IBackupManager.Stub {
// set of backup services that have pending changes
class BackupRequest {
public ApplicationInfo appInfo;
- public boolean fullBackup;
- BackupRequest(ApplicationInfo app, boolean isFull) {
+ BackupRequest(ApplicationInfo app) {
appInfo = app;
- fullBackup = isFull;
}
public String toString() {
- return "BackupRequest{app=" + appInfo + " full=" + fullBackup + "}";
+ return "BackupRequest{app=" + appInfo + "}";
}
}
// Backups that we haven't started yet. Keys are package names.
@@ -232,6 +248,38 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
+ class FullParams {
+ public ParcelFileDescriptor fd;
+ public final AtomicBoolean latch;
+ public IFullBackupRestoreObserver observer;
+
+ FullParams() {
+ latch = new AtomicBoolean(false);
+ }
+ }
+
+ class FullBackupParams extends FullParams {
+ public boolean includeApks;
+ public boolean includeShared;
+ public boolean allApps;
+ public String[] packages;
+
+ FullBackupParams(ParcelFileDescriptor output, boolean saveApks, boolean saveShared,
+ boolean doAllApps, String[] pkgList) {
+ fd = output;
+ includeApks = saveApks;
+ includeShared = saveShared;
+ allApps = doAllApps;
+ packages = pkgList;
+ }
+ }
+
+ class FullRestoreParams extends FullParams {
+ FullRestoreParams(ParcelFileDescriptor input) {
+ fd = input;
+ }
+ }
+
// Bookkeeping of in-flight operations for timeout etc. purposes. The operation
// token is the index of the entry in the pending-operations list.
static final int OP_PENDING = 0;
@@ -242,6 +290,8 @@ class BackupManagerService extends IBackupManager.Stub {
final Object mCurrentOpLock = new Object();
final Random mTokenGenerator = new Random();
+ final SparseArray<FullParams> mFullConfirmations = new SparseArray<FullParams>();
+
// Where we keep our journal files and other bookkeeping
File mBaseStateDir;
File mDataDir;
@@ -264,6 +314,17 @@ class BackupManagerService extends IBackupManager.Stub {
static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
HashSet<String> mPendingInits = new HashSet<String>(); // transport names
+ // Utility: build a new random integer token
+ int generateToken() {
+ int token;
+ do {
+ synchronized (mTokenGenerator) {
+ token = mTokenGenerator.nextInt();
+ }
+ } while (token < 0);
+ return token;
+ }
+
// ----- Asynchronous backup/restore handler thread -----
private class BackupHandler extends Handler {
@@ -321,7 +382,13 @@ class BackupManagerService extends IBackupManager.Stub {
}
case MSG_RUN_FULL_BACKUP:
+ {
+ FullBackupParams params = (FullBackupParams)msg.obj;
+ (new PerformFullBackupTask(params.fd, params.observer, params.includeApks,
+ params.includeShared, params.allApps, params.packages,
+ params.latch)).run();
break;
+ }
case MSG_RUN_RESTORE:
{
@@ -416,6 +483,34 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
}
+
+ case MSG_FULL_CONFIRMATION_TIMEOUT:
+ {
+ synchronized (mFullConfirmations) {
+ FullParams params = mFullConfirmations.get(msg.arg1);
+ if (params != null) {
+ Slog.i(TAG, "Full backup/restore timed out waiting for user confirmation");
+
+ // Release the waiter; timeout == completion
+ signalFullBackupRestoreCompletion(params);
+
+ // Remove the token from the set
+ mFullConfirmations.delete(msg.arg1);
+
+ // Report a timeout to the observer, if any
+ if (params.observer != null) {
+ try {
+ params.observer.onTimeout();
+ } catch (RemoteException e) {
+ /* don't care if the app has gone away */
+ }
+ }
+ } else {
+ Slog.d(TAG, "couldn't find params for token " + msg.arg1);
+ }
+ }
+ break;
+ }
}
}
}
@@ -1253,9 +1348,11 @@ class BackupManagerService extends IBackupManager.Stub {
void prepareOperationTimeout(int token, long interval) {
if (DEBUG) Slog.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);
+ synchronized (mCurrentOpLock) {
+ mCurrentOperations.put(token, OP_PENDING);
+ Message msg = mBackupHandler.obtainMessage(MSG_TIMEOUT, token, 0);
+ mBackupHandler.sendMessageDelayed(msg, interval);
+ }
}
// ----- Back up a set of applications via a worker thread -----
@@ -1313,7 +1410,7 @@ class BackupManagerService extends IBackupManager.Stub {
if (status == BackupConstants.TRANSPORT_OK) {
PackageManagerBackupAgent pmAgent = new PackageManagerBackupAgent(
mPackageManager, allAgentPackages());
- BackupRequest pmRequest = new BackupRequest(new ApplicationInfo(), false);
+ BackupRequest pmRequest = new BackupRequest(new ApplicationInfo());
pmRequest.appInfo.packageName = PACKAGE_MANAGER_SENTINEL;
status = processOneBackup(pmRequest,
IBackupAgent.Stub.asInterface(pmAgent.onBind()), mTransport);
@@ -1407,12 +1504,10 @@ class BackupManagerService extends IBackupManager.Stub {
}
IBackupAgent agent = null;
- int mode = (request.fullBackup)
- ? IApplicationThread.BACKUP_MODE_FULL
- : IApplicationThread.BACKUP_MODE_INCREMENTAL;
try {
mWakelock.setWorkSource(new WorkSource(request.appInfo.uid));
- agent = bindToAgentSynchronous(request.appInfo, mode);
+ agent = bindToAgentSynchronous(request.appInfo,
+ IApplicationThread.BACKUP_MODE_INCREMENTAL);
if (agent != null) {
int result = processOneBackup(request, agent, transport);
if (result != BackupConstants.TRANSPORT_OK) return result;
@@ -1446,7 +1541,7 @@ class BackupManagerService extends IBackupManager.Stub {
ParcelFileDescriptor newState = null;
PackageInfo packInfo;
- int token = mTokenGenerator.nextInt();
+ final int token = generateToken();
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
@@ -1461,12 +1556,11 @@ class BackupManagerService extends IBackupManager.Stub {
}
// In a full backup, we pass a null ParcelFileDescriptor as
- // the saved-state "file"
- if (!request.fullBackup) {
- savedState = ParcelFileDescriptor.open(savedStateName,
- ParcelFileDescriptor.MODE_READ_ONLY |
- ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary
- }
+ // the saved-state "file". This is by definition an incremental,
+ // so we build a saved state file to pass.
+ savedState = ParcelFileDescriptor.open(savedStateName,
+ ParcelFileDescriptor.MODE_READ_ONLY |
+ ParcelFileDescriptor.MODE_CREATE); // Make an empty file if necessary
backupData = ParcelFileDescriptor.open(backupDataName,
ParcelFileDescriptor.MODE_READ_WRITE |
@@ -1480,7 +1574,8 @@ class BackupManagerService extends IBackupManager.Stub {
// Initiate the target's backup pass
prepareOperationTimeout(token, TIMEOUT_BACKUP_INTERVAL);
- agent.doBackup(savedState, backupData, newState, token, mBackupManagerBinder);
+ agent.doBackup(savedState, backupData, newState, false,
+ token, mBackupManagerBinder);
boolean success = waitUntilOperationComplete(token);
if (!success) {
@@ -1552,6 +1647,224 @@ class BackupManagerService extends IBackupManager.Stub {
}
+ // ----- Full backup to a file/socket -----
+
+ class PerformFullBackupTask implements Runnable {
+ ParcelFileDescriptor mOutputFile;
+ IFullBackupRestoreObserver mObserver;
+ boolean mIncludeApks;
+ boolean mIncludeShared;
+ boolean mAllApps;
+ String[] mPackages;
+ AtomicBoolean mLatchObject;
+ File mFilesDir;
+ File mManifestFile;
+
+ PerformFullBackupTask(ParcelFileDescriptor fd, IFullBackupRestoreObserver observer,
+ boolean includeApks, boolean includeShared,
+ boolean doAllApps, String[] packages, AtomicBoolean latch) {
+ mOutputFile = fd;
+ mObserver = observer;
+ mIncludeApks = includeApks;
+ mIncludeShared = includeShared;
+ mAllApps = doAllApps;
+ mPackages = packages;
+ mLatchObject = latch;
+
+ mFilesDir = new File("/data/system");
+ mManifestFile = new File(mFilesDir, BACKUP_MANIFEST_FILENAME);
+ }
+
+ @Override
+ public void run() {
+ final List<PackageInfo> packagesToBackup;
+
+ sendStartBackup();
+
+ // doAllApps supersedes the package set if any
+ if (mAllApps) {
+ packagesToBackup = mPackageManager.getInstalledPackages(
+ PackageManager.GET_SIGNATURES);
+ } else {
+ packagesToBackup = new ArrayList<PackageInfo>();
+ for (String pkgName : mPackages) {
+ try {
+ packagesToBackup.add(mPackageManager.getPackageInfo(pkgName,
+ PackageManager.GET_SIGNATURES));
+ } catch (NameNotFoundException e) {
+ Slog.w(TAG, "Unknown package " + pkgName + ", skipping");
+ }
+ }
+ }
+
+ // Now back up the app data via the agent mechanism
+ PackageInfo pkg = null;
+ try {
+ int N = packagesToBackup.size();
+ for (int i = 0; i < N; i++) {
+ pkg = packagesToBackup.get(i);
+
+ Slog.d(TAG, "Binding to full backup agent : " + pkg.packageName);
+
+ IBackupAgent agent = bindToAgentSynchronous(pkg.applicationInfo,
+ IApplicationThread.BACKUP_MODE_FULL);
+ if (agent != null) {
+ try {
+ ApplicationInfo app = mPackageManager.getApplicationInfo(
+ pkg.packageName, 0);
+ boolean sendApk = mIncludeApks
+ && ((app.flags & ApplicationInfo.FLAG_FORWARD_LOCK) == 0)
+ && ((app.flags & ApplicationInfo.FLAG_SYSTEM) == 0 ||
+ (app.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+
+ sendOnBackupPackage(pkg.packageName);
+
+ {
+ BackupDataOutput output = new BackupDataOutput(
+ mOutputFile.getFileDescriptor());
+
+ if (DEBUG) Slog.d(TAG, "Writing manifest for " + pkg.packageName);
+ writeAppManifest(pkg, mManifestFile, sendApk);
+ FullBackup.backupToTar(pkg.packageName, null, null,
+ mFilesDir.getAbsolutePath(),
+ mManifestFile.getAbsolutePath(),
+ output);
+ }
+
+ if (DEBUG) Slog.d(TAG, "Calling doBackup()");
+ final int token = generateToken();
+ prepareOperationTimeout(token, TIMEOUT_FULL_BACKUP_INTERVAL);
+ agent.doBackup(null, mOutputFile, null, sendApk,
+ token, mBackupManagerBinder);
+ boolean success = waitUntilOperationComplete(token);
+ if (!success) {
+ Slog.d(TAG, "Full backup failed on package " + pkg.packageName);
+ } else {
+ if (DEBUG) Slog.d(TAG, "Full backup success: " + pkg.packageName);
+ }
+ } catch (NameNotFoundException e) {
+ Slog.e(TAG, "Package exists but not app info; skipping: "
+ + pkg.packageName);
+ } catch (IOException e) {
+ Slog.e(TAG, "Error backing up " + pkg.packageName, e);
+ }
+ } else {
+ Slog.w(TAG, "Unable to bind to full agent for " + pkg.packageName);
+ }
+ tearDown(pkg);
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "App died during full backup");
+ } finally {
+ if (pkg != null) {
+ tearDown(pkg);
+ }
+ try {
+ mOutputFile.close();
+ } catch (IOException e) {
+ /* nothing we can do about this */
+ }
+ synchronized (mCurrentOpLock) {
+ mCurrentOperations.clear();
+ }
+ synchronized (mLatchObject) {
+ mLatchObject.set(true);
+ mLatchObject.notifyAll();
+ }
+ sendEndBackup();
+ mWakelock.release();
+ if (DEBUG) Slog.d(TAG, "Full backup pass complete.");
+ }
+ }
+
+ private void writeAppManifest(PackageInfo pkg, File manifestFile, boolean withApk)
+ throws IOException {
+ // Manifest format. All data are strings ending in LF:
+ // BACKUP_MANIFEST_VERSION, currently 1
+ //
+ // Version 1:
+ // package name
+ // package's versionCode
+ // boolean: "1" if archive includes .apk, "0" otherwise
+ // number of signatures == N
+ // N*: signature byte array in ascii format per Signature.toCharsString()
+ StringBuilder builder = new StringBuilder(4096);
+ StringBuilderPrinter printer = new StringBuilderPrinter(builder);
+
+ printer.println(Integer.toString(BACKUP_MANIFEST_VERSION));
+ printer.println(pkg.packageName);
+ printer.println(Integer.toString(pkg.versionCode));
+ printer.println(withApk ? "1" : "0");
+ if (pkg.signatures == null) {
+ printer.println("0");
+ } else {
+ printer.println(Integer.toString(pkg.signatures.length));
+ for (Signature sig : pkg.signatures) {
+ printer.println(sig.toCharsString());
+ }
+ }
+
+ FileOutputStream outstream = new FileOutputStream(manifestFile);
+ Libcore.os.ftruncate(outstream.getFD(), 0);
+ outstream.write(builder.toString().getBytes());
+ outstream.close();
+ }
+
+ private void tearDown(PackageInfo pkg) {
+ final ApplicationInfo app = pkg.applicationInfo;
+ try {
+ // unbind and tidy up even on timeout or failure, just in case
+ mActivityManager.unbindBackupAgent(app);
+
+ // The agent was running with a stub Application object, so shut it down
+ if (app.uid != Process.SYSTEM_UID) {
+ if (DEBUG) Slog.d(TAG, "Backup complete, killing host process");
+ mActivityManager.killApplicationProcess(app.processName, app.uid);
+ } else {
+ if (DEBUG) Slog.d(TAG, "Not killing after restore: " + app.processName);
+ }
+ } catch (RemoteException e) {
+ Slog.d(TAG, "Lost app trying to shut down");
+ }
+ }
+
+ // wrappers for observer use
+ void sendStartBackup() {
+ if (mObserver != null) {
+ try {
+ mObserver.onStartBackup();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "full backup observer went away: startBackup");
+ mObserver = null;
+ }
+ }
+ }
+
+ void sendOnBackupPackage(String name) {
+ if (mObserver != null) {
+ try {
+ // TODO: use a more user-friendly name string
+ mObserver.onBackupPackage(name);
+ } catch (RemoteException e) {
+ Slog.w(TAG, "full backup observer went away: backupPackage");
+ mObserver = null;
+ }
+ }
+ }
+
+ void sendEndBackup() {
+ if (mObserver != null) {
+ try {
+ mObserver.onEndBackup();
+ } catch (RemoteException e) {
+ Slog.w(TAG, "full backup observer went away: endBackup");
+ mObserver = null;
+ }
+ }
+ }
+ }
+
+
// ----- Restore handling -----
private boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
@@ -1893,7 +2206,7 @@ class BackupManagerService extends IBackupManager.Stub {
ParcelFileDescriptor backupData = null;
ParcelFileDescriptor newState = null;
- int token = mTokenGenerator.nextInt();
+ final int token = generateToken();
try {
// Run the transport's restore pass
backupData = ParcelFileDescriptor.open(backupDataName,
@@ -1957,7 +2270,9 @@ class BackupManagerService extends IBackupManager.Stub {
try { if (backupData != null) backupData.close(); } catch (IOException e) {}
try { if (newState != null) newState.close(); } catch (IOException e) {}
backupData = newState = null;
- mCurrentOperations.delete(token);
+ synchronized (mCurrentOperations) {
+ mCurrentOperations.delete(token);
+ }
// If we know a priori that we'll need to perform a full post-restore backup
// pass, clear the new state file data. This means we're discarding work that
@@ -2092,7 +2407,7 @@ class BackupManagerService extends IBackupManager.Stub {
if (app.packageName.equals(packageName)) {
// Add the caller to the set of pending backups. If there is
// one already there, then overwrite it, but no harm done.
- BackupRequest req = new BackupRequest(app, false);
+ BackupRequest req = new BackupRequest(app);
if (mPendingBackups.put(app.packageName, req) == null) {
// Journal this request in case of crash. The put()
// operation returned null when this package was not already
@@ -2239,10 +2554,141 @@ class BackupManagerService extends IBackupManager.Stub {
}
}
+ // Run a *full* backup pass for the given package, writing the resulting data stream
+ // to the supplied file descriptor. This method is synchronous and does not return
+ // to the caller until the backup has been completed.
+ public void fullBackup(ParcelFileDescriptor fd, boolean includeApks, boolean includeShared,
+ boolean doAllApps, String[] pkgList) {
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
+
+ // Validate
+ if (!doAllApps) {
+ if (!includeShared) {
+ // If we're backing up shared data (sdcard or equivalent), then we can run
+ // without any supplied app names. Otherwise, we'd be doing no work, so
+ // report the error.
+ if (pkgList == null || pkgList.length == 0) {
+ throw new IllegalArgumentException(
+ "Backup requested but neither shared nor any apps named");
+ }
+ }
+ }
+
+ if (DEBUG) Slog.v(TAG, "Requesting full backup: apks=" + includeApks
+ + " shared=" + includeShared + " all=" + doAllApps
+ + " pkgs=" + pkgList);
+
+ long oldId = Binder.clearCallingIdentity();
+ try {
+ FullBackupParams params = new FullBackupParams(fd, includeApks, includeShared,
+ doAllApps, pkgList);
+ final int token = generateToken();
+ synchronized (mFullConfirmations) {
+ mFullConfirmations.put(token, params);
+ }
+
+ // start up the confirmation UI, making sure the screen lights up
+ if (DEBUG) Slog.d(TAG, "Starting confirmation UI, token=" + token);
+ try {
+ Intent confIntent = new Intent(FullBackup.FULL_BACKUP_INTENT_ACTION);
+ confIntent.setClassName("com.android.backupconfirm",
+ "com.android.backupconfirm.BackupRestoreConfirmation");
+ confIntent.putExtra(FullBackup.CONF_TOKEN_INTENT_EXTRA, token);
+ confIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(confIntent);
+ } catch (ActivityNotFoundException e) {
+ Slog.e(TAG, "Unable to launch full backup confirmation", e);
+ mFullConfirmations.delete(token);
+ return;
+ }
+ mPowerManager.userActivity(SystemClock.uptimeMillis(), false);
+
+ // start the confirmation countdown
+ if (DEBUG) Slog.d(TAG, "Posting conf timeout msg after "
+ + TIMEOUT_FULL_CONFIRMATION + " millis");
+ Message msg = mBackupHandler.obtainMessage(MSG_FULL_CONFIRMATION_TIMEOUT,
+ token, 0, params);
+ mBackupHandler.sendMessageDelayed(msg, TIMEOUT_FULL_CONFIRMATION);
+
+ // wait for the backup to be performed
+ if (DEBUG) Slog.d(TAG, "Waiting for full backup completion...");
+ waitForCompletion(params);
+ if (DEBUG) Slog.d(TAG, "...Full backup operation complete!");
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ try {
+ fd.close();
+ } catch (IOException e) {
+ // just eat it
+ }
+ }
+ }
+
+ void waitForCompletion(FullParams params) {
+ synchronized (params.latch) {
+ while (params.latch.get() == false) {
+ try {
+ params.latch.wait();
+ } catch (InterruptedException e) { /* never interrupted */ }
+ }
+ }
+ }
+
+ void signalFullBackupRestoreCompletion(FullParams params) {
+ synchronized (params.latch) {
+ params.latch.set(true);
+ params.latch.notifyAll();
+ }
+ }
+
+ // Confirm that the previously-requested full backup/restore operation can proceed. This
+ // is used to require a user-facing disclosure about the operation.
+ public void acknowledgeFullBackupOrRestore(int token, boolean allow,
+ IFullBackupRestoreObserver observer) {
+ if (DEBUG) Slog.d(TAG, "acknowledgeFullBackupOrRestore : token=" + token
+ + " allow=" + allow);
+
+ // TODO: possibly require not just this signature-only permission, but even
+ // require that the specific designated confirmation-UI app uid is the caller?
+ mContext.enforceCallingPermission(android.Manifest.permission.BACKUP, "fullBackup");
+
+ long oldId = Binder.clearCallingIdentity();
+ try {
+
+ FullParams params;
+ synchronized (mFullConfirmations) {
+ params = mFullConfirmations.get(token);
+ if (params != null) {
+ mBackupHandler.removeMessages(MSG_FULL_CONFIRMATION_TIMEOUT, params);
+ mFullConfirmations.delete(token);
+
+ if (allow) {
+ params.observer = observer;
+ final int verb = params instanceof FullBackupParams
+ ? MSG_RUN_FULL_BACKUP
+ : MSG_RUN_FULL_RESTORE;
+
+ mWakelock.acquire();
+ Message msg = mBackupHandler.obtainMessage(verb, params);
+ mBackupHandler.sendMessage(msg);
+ } else {
+ Slog.w(TAG, "User rejected full backup/restore operation");
+ // indicate completion without having actually transferred any data
+ signalFullBackupRestoreCompletion(params);
+ }
+ } else {
+ Slog.w(TAG, "Attempted to ack full backup/restore with invalid token");
+ }
+ }
+ } finally {
+ Binder.restoreCallingIdentity(oldId);
+ }
+ }
+
// Enable/disable the backup service
public void setBackupEnabled(boolean enable) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP,
- "setBackupEnabled");
+ "setBackupEnabled");
Slog.i(TAG, "Backup enabled => " + enable);