From 6ef58a1509b9d3348a33ca5686917796c2759aa5 Mon Sep 17 00:00:00 2001 From: Christopher Tate Date: Mon, 29 Jun 2009 14:56:28 -0700 Subject: [PATCH] Implement persistent enable/disable of the backup manager Backup & restore is still enabled by default, but with the expectation that it will be enabled during the course of the Setup Wizard or some other privileged entity that has notified the user about the ramifications. While disabled, data-changed notices will still be collected, but no backup pass will be scheduled. When the backup manager is later enabled, any pending data-changed notices will then be processed and the apps invoked for backup. --- .../src/com/android/commands/bmgr/Bmgr.java | 55 +++++++++++++++ core/java/android/backup/IBackupManager.aidl | 18 +++++ .../android/server/BackupManagerService.java | 68 +++++++++++++++---- 3 files changed, 127 insertions(+), 14 deletions(-) diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index c3ddd2014e8c7..c90b8622b8334 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -62,6 +62,16 @@ public final class Bmgr { String op = args[0]; mNextArg = 1; + if ("enabled".equals(op)) { + doEnabled(); + return; + } + + if ("enable".equals(op)) { + doEnable(); + return; + } + if ("run".equals(op)) { doRun(); return; @@ -91,6 +101,41 @@ public final class Bmgr { showUsage(); } + private String enableToString(boolean enabled) { + return enabled ? "enabled" : "disabled"; + } + + private void doEnabled() { + try { + boolean isEnabled = mBmgr.isBackupEnabled(); + System.out.println("Backup Manager currently " + + enableToString(isEnabled)); + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + + private void doEnable() { + String arg = nextArg(); + if (arg == null) { + showUsage(); + return; + } + + try { + boolean enable = Boolean.parseBoolean(arg); + mBmgr.setBackupEnabled(enable); + System.out.println("Backup Manager now " + enableToString(enable)); + } catch (NumberFormatException e) { + showUsage(); + return; + } catch (RemoteException e) { + System.err.println(e.toString()); + System.err.println(BMGR_NOT_RUNNING_ERR); + } + } + private void doRun() { try { mBmgr.backupNow(); @@ -291,6 +336,8 @@ public final class Bmgr { private static void showUsage() { System.err.println("usage: bmgr [backup|restore|list|transport|run]"); System.err.println(" bmgr backup PACKAGE"); + System.err.println(" bmgr enable BOOL"); + System.err.println(" bmgr enabled"); System.err.println(" bmgr list transports"); System.err.println(" bmgr list sets"); System.err.println(" bmgr transport WHICH"); @@ -301,6 +348,14 @@ public final class Bmgr { System.err.println("Note that the backup pass will effectively be a no-op if the package"); System.err.println("does not actually have changed data to store."); System.err.println(""); + System.err.println("The 'enable' command enables or disables the entire backup mechanism."); + System.err.println("If the argument is 'true' it will be enabled, otherwise it will be"); + System.err.println("disabled. When disabled, neither backup or restore operations will"); + System.err.println("be performed."); + System.err.println(""); + System.err.println("The 'enabled' command reports the current enabled/disabled state of"); + System.err.println("the backup mechanism."); + System.err.println(""); System.err.println("The 'list transports' command reports the names of the backup transports"); System.err.println("currently available on the device. These names can be passed as arguments"); System.err.println("to the 'transport' command. The currently selected transport is indicated"); diff --git a/core/java/android/backup/IBackupManager.aidl b/core/java/android/backup/IBackupManager.aidl index 39e160baa4cd4..1f1176252590f 100644 --- a/core/java/android/backup/IBackupManager.aidl +++ b/core/java/android/backup/IBackupManager.aidl @@ -47,6 +47,24 @@ interface IBackupManager { */ void agentDisconnected(String packageName); + /** + * Enable/disable the backup service entirely. When disabled, no backup + * or restore operations will take place. Data-changed notifications will + * still be observed and collected, however, so that changes made while the + * mechanism was disabled will still be backed up properly if it is enabled + * at some point in the future. + * + *

Callers must hold the android.permission.BACKUP permission to use this method. + */ + void setBackupEnabled(boolean isEnabled); + + /** + * Report whether the backup mechanism is currently enabled. + * + *

Callers must hold the android.permission.BACKUP permission to use this method. + */ + boolean isBackupEnabled(); + /** * Schedule an immediate backup attempt for all pending updates. This is * primarily intended for transports to use when they detect a suitable diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index 2b9ac4da28d26..c67f0b503431b 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -74,6 +74,10 @@ class BackupManagerService extends IBackupManager.Stub { private static final String TAG = "BackupManagerService"; private static final boolean DEBUG = true; + // Persistent properties + private static final String BACKUP_TRANSPORT_PROPERTY = "persist.service.bkup.trans"; + private static final String BACKUP_ENABLED_PROPERTY = "persist.service.bkup.enabled"; + // Default time to wait after data changes before we back up the data private static final long COLLECTION_INTERVAL = 3 * 60 * 1000; @@ -86,10 +90,11 @@ class BackupManagerService extends IBackupManager.Stub { private Context mContext; private PackageManager mPackageManager; - private final IActivityManager mActivityManager; + private IActivityManager mActivityManager; + private boolean mEnabled; // access to this is synchronized on 'this' private final BackupHandler mBackupHandler = new BackupHandler(); // map UIDs to the set of backup client services within that UID's app set - private SparseArray> mBackupParticipants + private final SparseArray> mBackupParticipants = new SparseArray>(); // set of backup services that have pending changes private class BackupRequest { @@ -128,7 +133,6 @@ class BackupManagerService extends IBackupManager.Stub { private volatile boolean mClearingData; // Transport bookkeeping - static private final String BACKUP_TRANSPORT_PROPERTY = "persist.service.bkup.trans"; private final HashMap mTransports = new HashMap(); private String mCurrentTransport; @@ -160,6 +164,9 @@ class BackupManagerService extends IBackupManager.Stub { mActivityManager = ActivityManagerNative.getDefault(); // Set up our bookkeeping + // !!! STOPSHIP: make this disabled by default so that we then gate on + // setupwizard or other opt-out UI + mEnabled = SystemProperties.getBoolean(BACKUP_ENABLED_PROPERTY, true); mBaseStateDir = new File(Environment.getDataDirectory(), "backup"); mDataDir = Environment.getDownloadCacheDirectory(); @@ -489,8 +496,15 @@ class BackupManagerService extends IBackupManager.Stub { // The queue lock should be held when scheduling a backup pass private void scheduleBackupPassLocked(long timeFromNowMillis) { - mBackupHandler.removeMessages(MSG_RUN_BACKUP); - mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, timeFromNowMillis); + // We only schedule backups when we're actually enabled + synchronized (this) { + if (mEnabled) { + mBackupHandler.removeMessages(MSG_RUN_BACKUP); + mBackupHandler.sendEmptyMessageDelayed(MSG_RUN_BACKUP, timeFromNowMillis); + } else if (DEBUG) { + Log.v(TAG, "Disabled, so not scheduling backup pass"); + } + } } // Return the given transport @@ -1087,7 +1101,7 @@ class BackupManagerService extends IBackupManager.Stub { if (DEBUG) { int numKeys = mPendingBackups.size(); - Log.d(TAG, "Scheduling backup for " + numKeys + " participants:"); + Log.d(TAG, "Now awaiting backup for " + numKeys + " participants:"); for (BackupRequest b : mPendingBackups.values()) { Log.d(TAG, " + " + b + " agent=" + b.appInfo.backupAgentName); } @@ -1117,7 +1131,7 @@ class BackupManagerService extends IBackupManager.Stub { // Run a backup pass immediately for any applications that have declared // that they have pending updates. public void backupNow() throws RemoteException { - mContext.enforceCallingPermission("android.permission.BACKUP", "tryBackupNow"); + mContext.enforceCallingPermission("android.permission.BACKUP", "backupNow"); if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass"); synchronized (mQueueLock) { @@ -1125,16 +1139,43 @@ class BackupManagerService extends IBackupManager.Stub { } } + // Enable/disable the backup transport + public void setBackupEnabled(boolean enable) { + mContext.enforceCallingPermission("android.permission.BACKUP", "setBackupEnabled"); + + boolean wasEnabled = mEnabled; + synchronized (this) { + SystemProperties.set(BACKUP_ENABLED_PROPERTY, enable ? "true" : "false"); + mEnabled = enable; + } + + if (enable && !wasEnabled) { + synchronized (mQueueLock) { + if (mPendingBackups.size() > 0) { + // !!! TODO: better policy around timing of the first backup pass + if (DEBUG) Log.v(TAG, "Backup enabled with pending data changes, scheduling"); + this.scheduleBackupPassLocked(COLLECTION_INTERVAL); + } + } + } +} + + // Report whether the backup mechanism is currently enabled + public boolean isBackupEnabled() { + mContext.enforceCallingPermission("android.permission.BACKUP", "isBackupEnabled"); + return mEnabled; // no need to synchronize just to read it + } + // Report the name of the currently active transport public String getCurrentTransport() { - mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport"); + mContext.enforceCallingPermission("android.permission.BACKUP", "getCurrentTransport"); Log.v(TAG, "getCurrentTransport() returning " + mCurrentTransport); return mCurrentTransport; } // Report all known, available backup transports public String[] listAllTransports() { - mContext.enforceCallingPermission("android.permission.BACKUP", "selectBackupTransport"); + mContext.enforceCallingPermission("android.permission.BACKUP", "listAllTransports"); String[] list = null; ArrayList known = new ArrayList(); @@ -1292,7 +1333,8 @@ class BackupManagerService extends IBackupManager.Stub { synchronized (mQueueLock) { pw.println("Available transports:"); for (String t : listAllTransports()) { - pw.println(" " + t); + String pad = (t.equals(mCurrentTransport)) ? " * " : " "; + pw.println(pad + t); } int N = mBackupParticipants.size(); pw.println("Participants:"); @@ -1302,14 +1344,12 @@ class BackupManagerService extends IBackupManager.Stub { pw.println(uid); HashSet participants = mBackupParticipants.valueAt(i); for (ApplicationInfo app: participants) { - pw.print(" "); - pw.println(app.toString()); + pw.println(" " + app.toString()); } } pw.println("Pending: " + mPendingBackups.size()); for (BackupRequest req : mPendingBackups.values()) { - pw.print(" "); - pw.println(req); + pw.println(" " + req); } } }