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

Commit 73e02529 authored by Christopher Tate's avatar Christopher Tate
Browse files

Ensure that everything gets backed up at least once

We now schedule a backup pass for any new application we see (at boot or
package-install time) for which we have never performed a backup.  The
bookkeeping is a log file with the names of all the packages we've successfully
completed a backup pass on, maintained with synchronous journal-type behavior.

Also, make a bunch of private fields package-scoped to permit cheaper access
from the various worker / binder threads that run under the Backup Manager's
aegis.
parent 2debd56b
Loading
Loading
Loading
Loading
+111 −38
Original line number Diff line number Diff line
@@ -100,18 +100,18 @@ class BackupManagerService extends IBackupManager.Stub {
    private PowerManager mPowerManager;
    private AlarmManager mAlarmManager;

    private boolean mEnabled;   // access to this is synchronized on 'this'
    private boolean mProvisioned;
    private PowerManager.WakeLock mWakelock;
    private final BackupHandler mBackupHandler = new BackupHandler();
    private PendingIntent mRunBackupIntent;
    private BroadcastReceiver mRunBackupReceiver;
    private IntentFilter mRunBackupFilter;
    boolean mEnabled;   // access to this is synchronized on 'this'
    boolean mProvisioned;
    PowerManager.WakeLock mWakelock;
    final BackupHandler mBackupHandler = new BackupHandler();
    PendingIntent mRunBackupIntent;
    BroadcastReceiver mRunBackupReceiver;
    IntentFilter mRunBackupFilter;
    // map UIDs to the set of backup client services within that UID's app set
    private final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
    final SparseArray<HashSet<ApplicationInfo>> mBackupParticipants
        = new SparseArray<HashSet<ApplicationInfo>>();
    // set of backup services that have pending changes
    private class BackupRequest {
    class BackupRequest {
        public ApplicationInfo appInfo;
        public boolean fullBackup;

@@ -125,35 +125,35 @@ class BackupManagerService extends IBackupManager.Stub {
        }
    }
    // Backups that we haven't started yet.
    private HashMap<ApplicationInfo,BackupRequest> mPendingBackups
    HashMap<ApplicationInfo,BackupRequest> mPendingBackups
            = new HashMap<ApplicationInfo,BackupRequest>();

    // Pseudoname that we use for the Package Manager metadata "package"
    private static final String PACKAGE_MANAGER_SENTINEL = "@pm@";
    static final String PACKAGE_MANAGER_SENTINEL = "@pm@";

    // locking around the pending-backup management
    private final Object mQueueLock = new Object();
    final Object mQueueLock = new Object();

    // The thread performing the sequence of queued backups binds to each app's agent
    // in succession.  Bind notifications are asynchronously delivered through the
    // Activity Manager; use this lock object to signal when a requested binding has
    // completed.
    private final Object mAgentConnectLock = new Object();
    private IBackupAgent mConnectedAgent;
    private volatile boolean mConnecting;
    final Object mAgentConnectLock = new Object();
    IBackupAgent mConnectedAgent;
    volatile boolean mConnecting;

    // A similar synchronicity mechanism around clearing apps' data for restore
    private final Object mClearDataLock = new Object();
    private volatile boolean mClearingData;
    final Object mClearDataLock = new Object();
    volatile boolean mClearingData;

    // Transport bookkeeping
    private final HashMap<String,IBackupTransport> mTransports
    final HashMap<String,IBackupTransport> mTransports
            = new HashMap<String,IBackupTransport>();
    private String mCurrentTransport;
    private IBackupTransport mLocalTransport, mGoogleTransport;
    private RestoreSession mActiveRestoreSession;
    String mCurrentTransport;
    IBackupTransport mLocalTransport, mGoogleTransport;
    RestoreSession mActiveRestoreSession;

    private class RestoreParams {
    class RestoreParams {
        public IBackupTransport transport;
        public IRestoreObserver observer;
        public long token;
@@ -165,7 +165,7 @@ class BackupManagerService extends IBackupManager.Stub {
        }
    }

    private class ClearParams {
    class ClearParams {
        public IBackupTransport transport;
        public PackageInfo packageInfo;

@@ -176,11 +176,17 @@ class BackupManagerService extends IBackupManager.Stub {
    }

    // Where we keep our journal files and other bookkeeping
    private File mBaseStateDir;
    private File mDataDir;
    private File mJournalDir;
    private File mJournal;
    private RandomAccessFile mJournalStream;
    File mBaseStateDir;
    File mDataDir;
    File mJournalDir;
    File mJournal;
    RandomAccessFile mJournalStream;

    // Keep a log of all the apps we've ever backed up
    private File mEverStored;
    private RandomAccessFile mEverStoredStream;
    HashSet<String> mEverStoredApps = new HashSet<String>();


    public BackupManagerService(Context context) {
        mContext = context;
@@ -215,6 +221,9 @@ class BackupManagerService extends IBackupManager.Stub {
        mJournalDir.mkdirs();   // creates mBaseStateDir along the way
        makeJournalLocked();    // okay because no other threads are running yet

        // Set up the various sorts of package tracking we do
        initPackageTracking();

        // Build our mapping of uid to backup client services.  This implicitly
        // schedules a backup pass on the Package Manager metadata the first
        // time anything needs to be backed up.
@@ -249,14 +258,6 @@ class BackupManagerService extends IBackupManager.Stub {
        // leftover journal files into the pending backup set
        parseLeftoverJournals();

        // Register for broadcasts about package install, etc., so we can
        // update the provider list.
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        filter.addDataScheme("package");
        mContext.registerReceiver(mBroadcastReceiver, filter);

        // Power management
        mWakelock = mPowerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "backup");

@@ -281,6 +282,39 @@ class BackupManagerService extends IBackupManager.Stub {
        }
    }

    private void initPackageTracking() {
        if (DEBUG) Log.v(TAG, "Initializing package tracking");

        // Keep a log of what apps we've ever backed up
        mEverStored = new File(mBaseStateDir, "processed");
        try {
            mEverStoredStream = new RandomAccessFile(mEverStored, "rwd");

            // parse its existing contents
            mEverStoredStream.seek(0);
            try {
                while (true) {
                    String pkg = mEverStoredStream.readUTF();
                    mEverStoredApps.add(pkg);
                    if (DEBUG) Log.v(TAG, "   + " + pkg);
                }
            } catch (EOFException e) {
                // now we're at EOF
            }
        } catch (IOException e) {
            Log.e(TAG, "Unable to open known-stored file!");
            mEverStoredStream = null;
        }

        // Register for broadcasts about package install, etc., so we can
        // update the provider list.
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_PACKAGE_ADDED);
        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        filter.addDataScheme("package");
        mContext.registerReceiver(mBroadcastReceiver, filter);
    }

    private void makeJournalLocked() {
        try {
            mJournal = File.createTempFile("journal", null, mJournalDir);
@@ -485,6 +519,17 @@ class BackupManagerService extends IBackupManager.Stub {
                    mBackupParticipants.put(uid, set);
                }
                set.add(pkg.applicationInfo);

                // If we've never seen this app before, schedule a backup for it
                if (!mEverStoredApps.contains(pkg.packageName)) {
                    if (DEBUG) Log.i(TAG, "New app " + pkg.packageName
                            + " never backed up; scheduling");
                    try {
                        dataChanged(pkg.packageName);
                    } catch (RemoteException e) {
                        // can't happen; it's a local method call
                    }
                }
            }
        }
    }
@@ -540,7 +585,7 @@ class BackupManagerService extends IBackupManager.Stub {
    }

    // Returns the set of all applications that define an android:backupAgent attribute
    private List<PackageInfo> allAgentPackages() {
    List<PackageInfo> allAgentPackages() {
        // !!! TODO: cache this and regenerate only when necessary
        int flags = PackageManager.GET_SIGNATURES;
        List<PackageInfo> packages = mPackageManager.getInstalledPackages(flags);
@@ -570,6 +615,28 @@ class BackupManagerService extends IBackupManager.Stub {
        addPackageParticipantsLockedInner(packageName, allApps);
    }

    // Called from the backup thread: record that the given app has been successfully
    // backed up at least once
    void logBackupComplete(String packageName) {
        if (mEverStoredStream != null) {
            synchronized (mEverStoredApps) {
                if (mEverStoredApps.add(packageName)) {
                    try {
                        mEverStoredStream.writeUTF(packageName);
                    } catch (IOException e) {
                        Log.e(TAG, "Unable to log backup of " + packageName + ", ceasing log");
                        try {
                            mEverStoredStream.close();
                        } catch (IOException ioe) {
                            // we're dropping it; no need to handle an exception on close here
                        }
                        mEverStoredStream = null;
                    }
                }
            }
        }
    }

    // Return the given transport
    private IBackupTransport getTransport(String transportName) {
        synchronized (mTransports) {
@@ -799,6 +866,7 @@ class BackupManagerService extends IBackupManager.Stub {
                boolean success = false;
                try {
                    agent.doBackup(savedState, backupData, newState);
                    logBackupComplete(packageName);
                    success = true;
                } finally {
                    if (savedState != null) {
@@ -1235,7 +1303,8 @@ class BackupManagerService extends IBackupManager.Stub {
                }
            }
        } else {
            Log.w(TAG, "dataChanged but no participant pkg='" + packageName + "'");
            Log.w(TAG, "dataChanged but no participant pkg='" + packageName + "'"
                    + " uid=" + Binder.getCallingUid());
        }
    }

@@ -1566,6 +1635,10 @@ class BackupManagerService extends IBackupManager.Stub {
                    pw.println("    " + app.toString());
                }
            }
            pw.println("Ever backed up: " + mEverStoredApps.size());
            for (String pkg : mEverStoredApps) {
                pw.println("    " + pkg);
            }
            pw.println("Pending: " + mPendingBackups.size());
            for (BackupRequest req : mPendingBackups.values()) {
                pw.println("    " + req);