Prevent concurrent backup operations

We've seen cases (bug 5417779) where the transport kicked off an immediate
backup operation but then was perfectly content to allow the periodic
timer to start *another* pass concurrently while the first was still in
progress.  This wound up with the backup manager getting mightily
confused and leaking wakelock acquisitions, which is Very Bad(tm).

This patch adds a little bookkeeping so that the backup manager is aware
of backups in flight, and refuses to kick off a new one until the ongoing
one has finished.

Change-Id: If12b54f4db3effc8af36d31c58d8f9b415ddc01e
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 7b8657a..5758954 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -228,6 +228,7 @@
     // completed.
     final Object mAgentConnectLock = new Object();
     IBackupAgent mConnectedAgent;
+    volatile boolean mBackupRunning;
     volatile boolean mConnecting;
     volatile long mLastBackupPass;
     volatile long mNextBackupPass;
@@ -434,6 +435,9 @@
                 IBackupTransport transport = getTransport(mCurrentTransport);
                 if (transport == null) {
                     Slog.v(TAG, "Backup requested but no transport available");
+                    synchronized (mQueueLock) {
+                        mBackupRunning = false;
+                    }
                     mWakelock.release();
                     break;
                 }
@@ -470,6 +474,9 @@
                     sendMessage(pbtMessage);
                 } else {
                     Slog.v(TAG, "Backup requested but nothing pending");
+                    synchronized (mQueueLock) {
+                        mBackupRunning = false;
+                    }
                     mWakelock.release();
                 }
                 break;
@@ -804,14 +811,19 @@
                         // Don't run backups now if we're disabled or not yet
                         // fully set up.
                         if (mEnabled && mProvisioned) {
-                            if (DEBUG) Slog.v(TAG, "Running a backup pass");
+                            if (!mBackupRunning) {
+                                if (DEBUG) Slog.v(TAG, "Running a backup pass");
 
-                            // Acquire the wakelock and pass it to the backup thread.  it will
-                            // be released once backup concludes.
-                            mWakelock.acquire();
+                                // Acquire the wakelock and pass it to the backup thread.  it will
+                                // be released once backup concludes.
+                                mBackupRunning = true;
+                                mWakelock.acquire();
 
-                            Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
-                            mBackupHandler.sendMessage(msg);
+                                Message msg = mBackupHandler.obtainMessage(MSG_RUN_BACKUP);
+                                mBackupHandler.sendMessage(msg);
+                            } else {
+                                Slog.i(TAG, "Backup time but one already running");
+                            }
                         } else {
                             Slog.w(TAG, "Backup pass but e=" + mEnabled + " p=" + mProvisioned);
                         }
@@ -1948,9 +1960,14 @@
                 writeRestoreTokens();
             }
 
-            // Set up the next backup pass
-            if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
-                backupNow();
+            // Set up the next backup pass - at this point we can set mBackupRunning
+            // to false to allow another pass to fire, because we're done with the
+            // state machine sequence and the wakelock is refcounted.
+            synchronized (mQueueLock) {
+                mBackupRunning = false;
+                if (mStatus == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
+                    backupNow();
+                }
             }
 
             // Only once we're entirely finished do we release the wakelock
@@ -2400,8 +2417,8 @@
                     mLatchObject.notifyAll();
                 }
                 sendEndBackup();
-                mWakelock.release();
                 if (DEBUG) Slog.d(TAG, "Full backup pass complete.");
+                mWakelock.release();
             }
         }
 
@@ -2908,8 +2925,8 @@
                     mLatchObject.notifyAll();
                 }
                 sendEndRestore();
-                mWakelock.release();
                 Slog.d(TAG, "Full restore pass complete.");
+                mWakelock.release();
             }
         }
 
@@ -5630,7 +5647,8 @@
                     + " / " + (!mProvisioned ? "not " : "") + "provisioned / "
                     + (this.mPendingInits.size() == 0 ? "not " : "") + "pending init");
             pw.println("Auto-restore is " + (mAutoRestore ? "enabled" : "disabled"));
-            pw.println("Last backup pass: " + mLastBackupPass
+            if (mBackupRunning) pw.println("Backup currently running");
+            pw.println("Last backup pass started: " + mLastBackupPass
                     + " (now = " + System.currentTimeMillis() + ')');
             pw.println("  next scheduled: " + mNextBackupPass);