Disallow concurrent backups; consult transport requestBackupTime()
* We now check for in-progress backups when asked to perform one, and don't
bother firing off another one concurrently (nor do we adjust the scheduling;
after all, someone asked to do a backup "now" and we're doing one, so we are
in line with expectations). We also defer backup passes while a restore is
in flight, and abort attempts to perform a restore during a backup pass.
* When a backup attempt fails, we now ask the transport how far in the future we
should put our retry. Previously we were simply not bothering to consult with
the transport, so we would wind up retrying backup while network connectivity
was known to be down, etc.
Change-Id: Ic7758010b74e06e90c50d46b7b0dd01b17af7c90
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 83b55c5..cb917db 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -162,6 +162,7 @@
final Object mAgentConnectLock = new Object();
IBackupAgent mConnectedAgent;
volatile boolean mConnecting;
+ volatile boolean mBackupOrRestoreInProgress = false;
// A similar synchronicity mechanism around clearing apps' data for restore
final Object mClearDataLock = new Object();
@@ -211,7 +212,7 @@
// Persistently track the need to do a full init
static final String INIT_SENTINEL_FILE_NAME = "_need_init_";
HashSet<String> mPendingInits = new HashSet<String>(); // transport names
- boolean mInitInProgress = false;
+ volatile boolean mInitInProgress = false;
public BackupManagerService(Context context) {
mContext = context;
@@ -316,9 +317,12 @@
}
} else {
// Don't run backups now if we're disabled, not yet
- // fully set up, or racing with an initialize pass.
- if (mEnabled && mProvisioned && !mInitInProgress) {
+ // fully set up, in the middle of a backup already,
+ // or racing with an initialize pass.
+ if (mEnabled && mProvisioned
+ && !mBackupOrRestoreInProgress && !mInitInProgress) {
if (DEBUG) Log.v(TAG, "Running a backup pass");
+ mBackupOrRestoreInProgress = true;
// Acquire the wakelock and pass it to the backup thread. it will
// be released once backup concludes.
@@ -328,7 +332,7 @@
mBackupHandler.sendMessage(msg);
} else {
Log.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned
- + " i=" + mInitInProgress);
+ + " b=" + mBackupOrRestoreInProgress + " i=" + mInitInProgress);
}
}
}
@@ -1106,6 +1110,12 @@
// can't happen; it's a local call
}
}
+
+ // We also want to reset the backup schedule based on whatever
+ // the transport suggests by way of retry/backoff time.
+ try {
+ startBackupAlarmsLocked(mTransport.requestBackupTime());
+ } catch (RemoteException e) { /* cannot happen */ }
}
// Either backup was successful, in which case we of course do not need
@@ -1116,7 +1126,11 @@
Log.e(TAG, "Unable to remove backup journal file " + mJournal);
}
- // Only once we're entirely finished do we release the wakelock
+ // Only once we're entirely finished do we indicate our completion
+ // and release the wakelock
+ synchronized (mQueueLock) {
+ mBackupOrRestoreInProgress = false;
+ }
mWakelock.release();
}
}
@@ -1553,6 +1567,9 @@
}
// done; we can finally release the wakelock
+ synchronized (mQueueLock) {
+ mBackupOrRestoreInProgress = false;
+ }
mWakelock.release();
}
}
@@ -1876,6 +1893,10 @@
if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass");
synchronized (mQueueLock) {
+ // Because the alarms we are using can jitter, and we want an *immediate*
+ // backup pass to happen, we restart the timer beginning with "next time,"
+ // then manually fire the backup trigger intent ourselves.
+ startBackupAlarmsLocked(BACKUP_INTERVAL);
try {
mRunBackupIntent.send();
} catch (PendingIntent.CanceledException e) {
@@ -2108,15 +2129,24 @@
return -1;
}
- for (int i = 0; i < mRestoreSets.length; i++) {
- if (token == mRestoreSets[i].token) {
- long oldId = Binder.clearCallingIdentity();
- mWakelock.acquire();
- Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
- msg.obj = new RestoreParams(mRestoreTransport, observer, token);
- mBackupHandler.sendMessage(msg);
- Binder.restoreCallingIdentity(oldId);
- return 0;
+ synchronized (mQueueLock) {
+ if (mBackupOrRestoreInProgress) {
+ Log.e(TAG, "Backup pass in progress, restore aborted");
+ return -1;
+ }
+
+ for (int i = 0; i < mRestoreSets.length; i++) {
+ if (token == mRestoreSets[i].token) {
+ long oldId = Binder.clearCallingIdentity();
+ // Suppress backups until the restore operation is finished
+ mBackupOrRestoreInProgress = true;
+ mWakelock.acquire();
+ Message msg = mBackupHandler.obtainMessage(MSG_RUN_RESTORE);
+ msg.obj = new RestoreParams(mRestoreTransport, observer, token);
+ mBackupHandler.sendMessage(msg);
+ Binder.restoreCallingIdentity(oldId);
+ return 0;
+ }
}
}