Merge change 26164 into eclair

* changes:
  Reset backup tracking in response to transport data-wipe notification
diff --git a/core/java/com/android/internal/backup/BackupConstants.java b/core/java/com/android/internal/backup/BackupConstants.java
new file mode 100644
index 0000000..3ee11bd
--- /dev/null
+++ b/core/java/com/android/internal/backup/BackupConstants.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.internal.backup;
+
+/**
+ * Constants used internally between the backup manager and its transports
+ */
+public class BackupConstants {
+    public static final int TRANSPORT_OK = 0;
+    public static final int TRANSPORT_ERROR = 1;
+    public static final int TRANSPORT_NOT_INITIALIZED = 2;
+}
diff --git a/core/java/com/android/internal/backup/IBackupTransport.aidl b/core/java/com/android/internal/backup/IBackupTransport.aidl
index 250bc91..47496a9 100644
--- a/core/java/com/android/internal/backup/IBackupTransport.aidl
+++ b/core/java/com/android/internal/backup/IBackupTransport.aidl
@@ -81,10 +81,14 @@
      *   will be erased prior to the storage of the data provided here.  The purpose of this
      *   is to provide a guarantee that no stale data exists in the restore set when the
      *   device begins providing backups.
-     * @return false if errors occurred (the backup should be aborted and rescheduled),
-     *   true if everything is OK so far (but {@link #finishBackup} must be called).
+     * @return If everything is okay so far, returns zero (but {@link #finishBackup} must
+     *   still be called).  If the backend dataset has unexpectedly become unavailable,
+     *   such as when it is deleted after a period of device inactivity, returns {@link
+     *   BackupManager#DATASET_UNAVAILABLE}; in this case, the transport should be
+     *   reinitalized and the entire backup pass restarted.  Any other nonzero value is a
+     *   fatal error requiring that this package's backup be aborted and rescheduled.
      */
-    boolean performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd,
+    int performBackup(in PackageInfo packageInfo, in ParcelFileDescriptor inFd,
             boolean wipeAllFirst);
 
     /**
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 981ea82..603f691f 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -56,7 +56,7 @@
         return 0;
     }
 
-    public boolean performBackup(PackageInfo packageInfo, ParcelFileDescriptor data,
+    public int performBackup(PackageInfo packageInfo, ParcelFileDescriptor data,
             boolean wipeAllFirst) throws RemoteException {
         if (DEBUG) Log.v(TAG, "performBackup() pkg=" + packageInfo.packageName);
 
@@ -99,7 +99,7 @@
                         entity.write(buf, 0, dataSize);
                     } catch (IOException e) {
                         Log.e(TAG, "Unable to update key file " + entityFile.getAbsolutePath());
-                        return false;
+                        return BackupConstants.TRANSPORT_ERROR;
                     } finally {
                         entity.close();
                     }
@@ -107,11 +107,11 @@
                     entityFile.delete();
                 }
             }
-            return true;
+            return BackupConstants.TRANSPORT_OK;
         } catch (IOException e) {
             // oops, something went wrong.  abort the operation and return error.
             Log.v(TAG, "Exception reading backup input:", e);
-            return false;
+            return BackupConstants.TRANSPORT_ERROR;
         }
     }
 
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 854a6f9..861f66d 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -57,6 +57,7 @@
 import android.backup.IRestoreSession;
 import android.backup.RestoreSet;
 
+import com.android.internal.backup.BackupConstants;
 import com.android.internal.backup.LocalTransport;
 import com.android.internal.backup.IBackupTransport;
 
@@ -101,6 +102,7 @@
     private static final int BACKUP_AGENT_FAILURE_EVENT = 2823;
     private static final int BACKUP_PACKAGE_EVENT = 2824;
     private static final int BACKUP_SUCCESS_EVENT = 2825;
+    private static final int BACKUP_RESET_EVENT = 2826;
 
     private static final int RESTORE_START_EVENT = 2830;
     private static final int RESTORE_TRANSPORT_FAILURE_EVENT = 2831;
@@ -406,6 +408,47 @@
         }
     }
 
+    // Reset all of our bookkeeping, in response to having been told that
+    // the backend data has been wiped [due to idle expiry, for example],
+    // so we must re-upload all saved settings.
+    void resetBackupState(File stateFileDir) {
+        synchronized (mQueueLock) {
+            // Wipe the "what we've ever backed up" tracking
+            try {
+                // close the ever-stored journal...
+                if (mEverStoredStream != null) {
+                    mEverStoredStream.close();
+                }
+                // ... so we can delete it and start over
+                mEverStored.delete();
+                mEverStoredStream = new RandomAccessFile(mEverStored, "rwd");
+            } catch (IOException e) {
+                Log.e(TAG, "Unable to open known-stored file!");
+                mEverStoredStream = null;
+            }
+            mEverStoredApps.clear();
+
+            // Remove all the state files
+            for (File sf : stateFileDir.listFiles()) {
+                sf.delete();
+            }
+
+            // Enqueue a new backup of every participant
+            int N = mBackupParticipants.size();
+            for (int i=0; i<N; i++) {
+                int uid = mBackupParticipants.keyAt(i);
+                HashSet<ApplicationInfo> participants = mBackupParticipants.valueAt(i);
+                for (ApplicationInfo app: participants) {
+                    try {
+                        dataChanged(app.packageName);
+                    } catch (RemoteException e) {
+                        // can't happen; we're in the same process
+                    }
+                }
+            }
+        }
+    }
+
     // Add a transport to our set of available backends
     private void registerTransport(String name, IBackupTransport transport) {
         synchronized (mTransports) {
@@ -891,8 +934,22 @@
                 // If we haven't stored anything yet, we need to do an init
                 // operation along with recording the metadata blob.
                 boolean needInit = (mEverStoredApps.size() == 0);
-                processOneBackup(pmRequest, IBackupAgent.Stub.asInterface(pmAgent.onBind()),
+                int result = processOneBackup(pmRequest,
+                        IBackupAgent.Stub.asInterface(pmAgent.onBind()),
                         mTransport, needInit);
+                if (result == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
+                    // The backend reports that our dataset has been wiped.  We need to
+                    // reset all of our bookkeeping and instead run a new backup pass for
+                    // everything.
+                    EventLog.writeEvent(BACKUP_RESET_EVENT, mTransport.transportDirName());
+                    resetBackupState(mStateDir);
+                    backupNow();
+                    return;
+                } else if (result != BackupConstants.TRANSPORT_OK) {
+                    // Give up if we couldn't even process the metadata
+                    Log.e(TAG, "Meta backup err " + result);
+                    return;
+                }
 
                 // Now run all the backups in our queue
                 int count = mQueue.size();
@@ -953,7 +1010,7 @@
             }
         }
 
-        void processOneBackup(BackupRequest request, IBackupAgent agent,
+        int processOneBackup(BackupRequest request, IBackupAgent agent,
                 IBackupTransport transport, boolean doInit) {
             final String packageName = request.appInfo.packageName;
             if (DEBUG) Log.d(TAG, "processOneBackup doBackup(" + doInit + ") on " + packageName);
@@ -1007,7 +1064,7 @@
                 EventLog.writeEvent(BACKUP_AGENT_FAILURE_EVENT, packageName, e.toString());
                 backupDataName.delete();
                 newStateName.delete();
-                return;
+                return BackupConstants.TRANSPORT_ERROR;
             } finally {
                 try { if (savedState != null) savedState.close(); } catch (IOException e) {}
                 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
@@ -1027,7 +1084,13 @@
                     // hold off on finishBackup() until the end, which implies holding off on
                     // renaming *all* the output state files (see below) until that happens.
 
-                    if (!transport.performBackup(packInfo, backupData, doInit) ||
+                    int performOkay = transport.performBackup(packInfo, backupData, doInit);
+                    if (performOkay == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
+                        Log.i(TAG, "Backend not initialized");
+                        return performOkay;
+                    }
+
+                    if ((performOkay != 0) ||
                         !transport.finishBackup()) {
                         throw new Exception("Backup transport failed");
                     }
@@ -1044,10 +1107,12 @@
             } catch (Exception e) {
                 Log.e(TAG, "Transport error backing up " + packageName, e);
                 EventLog.writeEvent(BACKUP_TRANSPORT_FAILURE_EVENT, packageName);
-                return;
+                return BackupConstants.TRANSPORT_ERROR;
             } finally {
                 try { if (backupData != null) backupData.close(); } catch (IOException e) {}
             }
+
+            return BackupConstants.TRANSPORT_OK;
         }
     }
 
@@ -1590,7 +1655,6 @@
         if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass");
         synchronized (mQueueLock) {
             try {
-                if (DEBUG) Log.v(TAG, "sending immediate backup broadcast");
                 mRunBackupIntent.send();
             } catch (PendingIntent.CanceledException e) {
                 // should never happen