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

Commit ab06997e authored by Christopher Tate's avatar Christopher Tate
Browse files

Fixes to full-backup scheduling edge cases

If a scheduled full-data backup was attempted but the device had not yet
run the primary key/value backup pass, the full-data backup scheduler
would wind up in a bad state and potentially never retry until reboot.

We now properly reschedule a future retry, using the key/value
scheduling batch quantum as a backoff to be sure to give it a chance
to run before the next time full data is attempted.

Change-Id: Ic7eb7a7940fe6380f40d04813a46fc336e95815e
parent dc0078b7
Loading
Loading
Loading
Loading
+80 −36
Original line number Diff line number Diff line
@@ -3938,16 +3938,6 @@ public class BackupManagerService {
                    return;
                }

                // Don't proceed unless we have already established package metadata
                // for the current dataset via a key/value backup pass.
                File stateDir = new File(mBaseStateDir, transport.transportDirName());
                File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
                if (pmState.length() <= 0) {
                    Slog.i(TAG, "Full backup requested but dataset not yet initialized "
                            + "via k/v backup pass; ignoring");
                    return;
                }

                // Set up to send data to the transport
                final int N = mPackages.size();
                for (int i = 0; i < N; i++) {
@@ -4296,6 +4286,31 @@ public class BackupManagerService {
        writeFullBackupScheduleAsync();
    }

    private boolean fullBackupAllowable(IBackupTransport transport) {
        if (transport == null) {
            Slog.w(TAG, "Transport not present; full data backup not performed");
            return false;
        }

        // Don't proceed unless we have already established package metadata
        // for the current dataset via a key/value backup pass.
        try {
            File stateDir = new File(mBaseStateDir, transport.transportDirName());
            File pmState = new File(stateDir, PACKAGE_MANAGER_SENTINEL);
            if (pmState.length() <= 0) {
                if (DEBUG) {
                    Slog.i(TAG, "Full backup requested but dataset not yet initialized");
                }
                return false;
            }
        } catch (Exception e) {
            Slog.w(TAG, "Unable to contact transport");
            return false;
        }

        return true;
    }

    /**
     * Conditions are right for a full backup operation, so run one.  The model we use is
     * to perform one app backup per scheduled job execution, and to reschedule the job
@@ -4307,6 +4322,7 @@ public class BackupManagerService {
    boolean beginFullBackup(FullBackupJob scheduledJob) {
        long now = System.currentTimeMillis();
        FullBackupEntry entry = null;
        long latency = MIN_FULL_BACKUP_INTERVAL;

        if (!mEnabled || !mProvisioned) {
            // Backups are globally disabled, so don't proceed.  We also don't reschedule
@@ -4338,17 +4354,41 @@ public class BackupManagerService {
                return false;
            }

            // At this point we know that we have work to do, just not right now.  Any
            // exit without actually running backups will also require that we
            // reschedule the job.
            boolean runBackup = true;

            if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
                if (MORE_DEBUG) {
                    Slog.i(TAG, "Preconditions not met; not running full backup");
                }
                runBackup = false;
                // Typically this means we haven't run a key/value backup yet.  Back off
                // full-backup operations by the key/value job's run interval so that
                // next time we run, we are likely to be able to make progress.
                latency = KeyValueBackupJob.BATCH_INTERVAL;
            }

            if (runBackup) {
                entry = mFullBackupQueue.get(0);
                long timeSinceRun = now - entry.lastBackup;
            if (timeSinceRun < MIN_FULL_BACKUP_INTERVAL) {
                runBackup = (timeSinceRun >= MIN_FULL_BACKUP_INTERVAL);
                if (!runBackup) {
                    // It's too early to back up the next thing in the queue, so bow out
                    if (MORE_DEBUG) {
                        Slog.i(TAG, "Device ready but too early to back up next app");
                    }
                final long latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
                    // Wait until the next app in the queue falls due for a full data backup
                    latency = MIN_FULL_BACKUP_INTERVAL - timeSinceRun;
                }
            }

            if (!runBackup) {
                final long deferTime = latency;     // pin for the closure
                mBackupHandler.post(new Runnable() {
                    @Override public void run() {
                        FullBackupJob.schedule(mContext, latency);
                        FullBackupJob.schedule(mContext, deferTime);
                    }
                });
                return false;
@@ -8489,6 +8529,9 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
            throw new IllegalStateException("Restore supported only for the device owner");
        }

        if (!fullBackupAllowable(getTransport(mCurrentTransport))) {
            Slog.i(TAG, "Full backup not currently possible -- key/value backup not yet run?");
        } else {
            if (DEBUG) {
                Slog.d(TAG, "fullTransportBackup()");
            }
@@ -8511,6 +8554,7 @@ if (MORE_DEBUG) Slog.v(TAG, " + got " + nRead + "; now wanting " + (size - soF
            for (String pkg : pkgNames) {
                enqueueFullBackup(pkg, now);
            }
        }

        if (DEBUG) {
            Slog.d(TAG, "Done with full transport backup.");
+1 −1
Original line number Diff line number Diff line
@@ -41,7 +41,7 @@ public class KeyValueBackupJob extends JobService {
    // Once someone asks for a backup, this is how long we hold off, batching
    // up additional requests, before running the actual backup pass.  Privileged
    // callers can always trigger an immediate pass via BackupManager.backupNow().
    private static final long BATCH_INTERVAL = 4 * AlarmManager.INTERVAL_HOUR;
    static final long BATCH_INTERVAL = 4 * AlarmManager.INTERVAL_HOUR;

    // Random variation in next-backup scheduling time to avoid server load spikes
    private static final int FUZZ_MILLIS = 10 * 60 * 1000;