Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6ef58a15 authored by Christopher Tate's avatar Christopher Tate
Browse files

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.
parent 3a608f82
Loading
Loading
Loading
Loading
+55 −0
Original line number Diff line number Diff line
@@ -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");
+18 −0
Original line number Diff line number Diff line
@@ -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.
     *
     * <p>Callers must hold the android.permission.BACKUP permission to use this method.
     */
    void setBackupEnabled(boolean isEnabled);

    /**
     * Report whether the backup mechanism is currently enabled.
     *
     * <p>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
+54 −14
Original line number Diff line number Diff line
@@ -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<HashSet<ApplicationInfo>> mBackupParticipants
    private final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
        = new SparseArray<HashSet<ApplicationInfo>>();
    // 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<String,IBackupTransport> mTransports
            = new HashMap<String,IBackupTransport>();
    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) {
        // 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<String> known = new ArrayList<String>();
@@ -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<ApplicationInfo> 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);
            }
        }
    }