Merge change 7435

* changes:
  Ensure that everything gets backed up at least once
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index 20f0750..6eba783 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -100,18 +100,18 @@
     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 @@
         }
     }
     // 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 @@
         }
     }
 
-    private class ClearParams {
+    class ClearParams {
         public IBackupTransport transport;
         public PackageInfo packageInfo;
 
@@ -176,11 +176,17 @@
     }
 
     // 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 @@
         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 @@
         // 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 @@
         }
     }
 
+    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 @@
                     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 @@
     }
 
     // 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 @@
         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 @@
                 boolean success = false;
                 try {
                     agent.doBackup(savedState, backupData, newState);
+                    logBackupComplete(packageName);
                     success = true;
                 } finally {
                     if (savedState != null) {
@@ -1235,7 +1303,8 @@
                 }
             }
         } 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 @@
                     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);