Only re-initialize backup state if @pm@ metadata is missing,
to defensively work around a still-mysterious bug where the
list of saved packages ends up being empty even though we still
have state pending.  If we do re-initialize, then wipe all state
to make sure the right thing happens.

Don't keep open journal files -- close them after every update.
A bit less efficient, but possibly more reliable (again, this is
defensive programming here).  Also change "rwd" to "rws" mode
for fully synchronous operation.
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index ef0cbee..e722eb1 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -205,11 +205,9 @@
     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>();
 
     // Persistently track the need to do a full init
@@ -257,7 +255,7 @@
         // Set up the backup-request journaling
         mJournalDir = new File(mBaseStateDir, "pending");
         mJournalDir.mkdirs();   // creates mBaseStateDir along the way
-        makeJournalLocked();    // okay because no other threads are running yet
+        mJournal = null;        // will be created on first use
 
         // Set up the various sorts of package tracking we do
         initPackageTracking();
@@ -369,56 +367,51 @@
         // this log, we sanity-check its contents here and reconstruct it.
         mEverStored = new File(mBaseStateDir, "processed");
         File tempProcessedFile = new File(mBaseStateDir, "processed.new");
-        try {
-            // If there are previous contents, parse them out then start a new
-            // file to continue the recordkeeping.
-            if (mEverStored.exists()) {
-                RandomAccessFile temp = new RandomAccessFile(tempProcessedFile, "rw");
-                mEverStoredStream = new RandomAccessFile(mEverStored, "r");
-
-                // parse its existing contents
-                mEverStoredStream.seek(0);
-                temp.seek(0);
-                try {
-                    while (true) {
-                        PackageInfo info;
-                        String pkg = mEverStoredStream.readUTF();
-                        try {
-                            info = mPackageManager.getPackageInfo(pkg, 0);
-                            mEverStoredApps.add(pkg);
-                            temp.writeUTF(pkg);
-                            if (DEBUG) Log.v(TAG, "   + " + pkg);
-                        } catch (NameNotFoundException e) {
-                            // nope, this package was uninstalled; don't include it
-                            if (DEBUG) Log.v(TAG, "   - " + pkg);
-                        }
-                    }
-                } catch (EOFException e) {
-                    // now we're at EOF
-                }
-
-                // Once we've rewritten the backup history log, atomically replace the
-                // old one with the new one then reopen the file for continuing use.
-                temp.close();
-                mEverStoredStream.close();
-                tempProcessedFile.renameTo(mEverStored);
-            }
-            // This will create the file if it doesn't exist
-            mEverStoredStream = new RandomAccessFile(mEverStored, "rwd");
-            mEverStoredStream.seek(mEverStoredStream.length());
-        } catch (IOException e) {
-            Log.e(TAG, "Unable to open known-stored file!");
-            mEverStoredStream = null;
-        }
 
         // If we were in the middle of removing something from the ever-backed-up
         // file, there might be a transient "processed.new" file still present.
-        // We've reconstructed a coherent state at this point though, so we can
-        // safely discard that file now.
+        // Ignore it -- we'll validate "processed" against the current package set.
         if (tempProcessedFile.exists()) {
             tempProcessedFile.delete();
         }
 
+        // If there are previous contents, parse them out then start a new
+        // file to continue the recordkeeping.
+        if (mEverStored.exists()) {
+            RandomAccessFile temp = null;
+            RandomAccessFile in = null;
+
+            try {
+                temp = new RandomAccessFile(tempProcessedFile, "rws");
+                in = new RandomAccessFile(mEverStored, "r");
+
+                while (true) {
+                    PackageInfo info;
+                    String pkg = in.readUTF();
+                    try {
+                        info = mPackageManager.getPackageInfo(pkg, 0);
+                        mEverStoredApps.add(pkg);
+                        temp.writeUTF(pkg);
+                        if (DEBUG) Log.v(TAG, "   + " + pkg);
+                    } catch (NameNotFoundException e) {
+                        // nope, this package was uninstalled; don't include it
+                        if (DEBUG) Log.v(TAG, "   - " + pkg);
+                    }
+                }
+            } catch (EOFException e) {
+                // Once we've rewritten the backup history log, atomically replace the
+                // old one with the new one then reopen the file for continuing use.
+                if (!tempProcessedFile.renameTo(mEverStored)) {
+                    Log.e(TAG, "Error renaming " + tempProcessedFile + " to " + mEverStored);
+                }
+            } catch (IOException e) {
+                Log.e(TAG, "Error in processed file", e);
+            } finally {
+                try { if (temp != null) temp.close(); } catch (IOException e) {}
+                try { if (in != null) in.close(); } catch (IOException e) {}
+            }
+        }
+
         // Register for broadcasts about package install, etc., so we can
         // update the provider list.
         IntentFilter filter = new IntentFilter();
@@ -428,41 +421,29 @@
         mContext.registerReceiver(mBroadcastReceiver, filter);
     }
 
-    private void makeJournalLocked() {
-        try {
-            mJournal = File.createTempFile("journal", null, mJournalDir);
-            mJournalStream = new RandomAccessFile(mJournal, "rwd");
-        } catch (IOException e) {
-            Log.e(TAG, "Unable to write backup journals");
-            mJournal = null;
-            mJournalStream = null;
-        }
-    }
-
     private void parseLeftoverJournals() {
-        if (mJournal != null) {
-            File[] allJournals = mJournalDir.listFiles();
-            for (File f : allJournals) {
-                if (f.compareTo(mJournal) != 0) {
-                    // This isn't the current journal, so it must be a leftover.  Read
-                    // out the package names mentioned there and schedule them for
-                    // backup.
-                    try {
-                        Log.i(TAG, "Found stale backup journal, scheduling:");
-                        RandomAccessFile in = new RandomAccessFile(f, "r");
-                        while (true) {
-                            String packageName = in.readUTF();
-                            Log.i(TAG, "    + " + packageName);
-                            dataChanged(packageName);
-                        }
-                    } catch (EOFException e) {
-                        // no more data; we're done
-                    } catch (Exception e) {
-                        // can't read it or other error; just skip it
-                    } finally {
-                        // close/delete the file
-                        f.delete();
+        for (File f : mJournalDir.listFiles()) {
+            if (mJournal == null || f.compareTo(mJournal) != 0) {
+                // This isn't the current journal, so it must be a leftover.  Read
+                // out the package names mentioned there and schedule them for
+                // backup.
+                RandomAccessFile in = null;
+                try {
+                    Log.i(TAG, "Found stale backup journal, scheduling:");
+                    in = new RandomAccessFile(f, "r");
+                    while (true) {
+                        String packageName = in.readUTF();
+                        Log.i(TAG, "    + " + packageName);
+                        dataChanged(packageName);
                     }
+                } catch (EOFException e) {
+                    // no more data; we're done
+                } catch (Exception e) {
+                    Log.e(TAG, "Can't read " + f, e);
+                } finally {
+                    // close/delete the file
+                    try { if (in != null) in.close(); } catch (IOException e) {}
+                    f.delete();
                 }
             }
         }
@@ -505,19 +486,8 @@
     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();
+            mEverStored.delete();
 
             // Remove all the state files
             for (File sf : stateFileDir.listFiles()) {
@@ -533,11 +503,7 @@
                 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
-                    }
+                    dataChanged(app.packageName);
                 }
             }
         }
@@ -652,7 +618,6 @@
 
                 // snapshot the pending-backup set and work on that
                 ArrayList<BackupRequest> queue = new ArrayList<BackupRequest>();
-                File oldJournal = mJournal;
                 synchronized (mQueueLock) {
                     // Do we have any work to do?
                     if (mPendingBackups.size() > 0) {
@@ -663,14 +628,8 @@
                         mPendingBackups.clear();
 
                         // Start a new backup-queue journal file too
-                        if (mJournalStream != null) {
-                            try {
-                                mJournalStream.close();
-                            } catch (IOException e) {
-                                // don't need to do anything
-                            }
-                            makeJournalLocked();
-                        }
+                        File oldJournal = mJournal;
+                        mJournal = null;
 
                         // At this point, we have started a new journal file, and the old
                         // file identity is being passed to the backup processing thread.
@@ -763,11 +722,7 @@
                 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
-                    }
+                    dataChanged(pkg.packageName);
                 }
             }
         }
@@ -871,58 +826,57 @@
     // 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 && !packageName.equals(PACKAGE_MANAGER_SENTINEL)) {
-            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;
-                    }
-                }
+        if (packageName.equals(PACKAGE_MANAGER_SENTINEL)) return;
+
+        synchronized (mEverStoredApps) {
+            if (!mEverStoredApps.add(packageName)) return;
+
+            RandomAccessFile out = null;
+            try {
+                out = new RandomAccessFile(mEverStored, "rws");
+                out.seek(out.length());
+                out.writeUTF(packageName);
+            } catch (IOException e) {
+                Log.e(TAG, "Can't log backup of " + packageName + " to " + mEverStored);
+            } finally {
+                try { if (out != null) out.close(); } catch (IOException e) {}
             }
         }
     }
 
     // Remove our awareness of having ever backed up the given package
     void removeEverBackedUp(String packageName) {
-        if (DEBUG) Log.v(TAG, "Removing backed-up knowledge of " + packageName
-                + ", new set:");
+        if (DEBUG) Log.v(TAG, "Removing backed-up knowledge of " + packageName + ", new set:");
 
-        if (mEverStoredStream != null) {
-            synchronized (mEverStoredApps) {
-                // Rewrite the file and rename to overwrite.  If we reboot in the middle,
-                // we'll recognize on initialization time that the package no longer
-                // exists and fix it up then.
-                File tempKnownFile = new File(mBaseStateDir, "processed.new");
-                try {
-                    mEverStoredStream.close();
-                    RandomAccessFile known = new RandomAccessFile(tempKnownFile, "rw");
-                    mEverStoredApps.remove(packageName);
-                    for (String s : mEverStoredApps) {
-                        known.writeUTF(s);
-                        if (DEBUG) Log.v(TAG, "    " + s);
-                    }
-                    known.close();
-                    tempKnownFile.renameTo(mEverStored);
-                    mEverStoredStream = new RandomAccessFile(mEverStored, "rwd");
-                } catch (IOException e) {
-                    // Bad: we couldn't create the new copy.  For safety's sake we
-                    // abandon the whole process and remove all what's-backed-up
-                    // state entirely, meaning we'll force a backup pass for every
-                    // participant on the next boot or [re]install.
-                    Log.w(TAG, "Error rewriting backed-up set; halting log");
-                    mEverStoredStream = null;
-                    mEverStoredApps.clear();
-                    tempKnownFile.delete();
-                    mEverStored.delete();
+        synchronized (mEverStoredApps) {
+            // Rewrite the file and rename to overwrite.  If we reboot in the middle,
+            // we'll recognize on initialization time that the package no longer
+            // exists and fix it up then.
+            File tempKnownFile = new File(mBaseStateDir, "processed.new");
+            RandomAccessFile known = null;
+            try {
+                known = new RandomAccessFile(tempKnownFile, "rws");
+                mEverStoredApps.remove(packageName);
+                for (String s : mEverStoredApps) {
+                    known.writeUTF(s);
+                    if (DEBUG) Log.v(TAG, "    " + s);
                 }
+                known.close();
+                known = null;
+                if (!tempKnownFile.renameTo(mEverStored)) {
+                    throw new IOException("Can't rename " + tempKnownFile + " to " + mEverStored);
+                }
+            } catch (IOException e) {
+                // Bad: we couldn't create the new copy.  For safety's sake we
+                // abandon the whole process and remove all what's-backed-up
+                // state entirely, meaning we'll force a backup pass for every
+                // participant on the next boot or [re]install.
+                Log.w(TAG, "Error rewriting " + mEverStored, e);
+                mEverStoredApps.clear();
+                tempKnownFile.delete();
+                mEverStored.delete();
+            } finally {
+                try { if (known != null) known.close(); } catch (IOException e) {}
             }
         }
     }
@@ -1018,8 +972,7 @@
     }
 
     class ClearDataObserver extends IPackageDataObserver.Stub {
-        public void onRemoveCompleted(String packageName, boolean succeeded)
-                throws RemoteException {
+        public void onRemoveCompleted(String packageName, boolean succeeded) {
             synchronized(mClearDataLock) {
                 mClearingData = false;
                 mClearDataLock.notifyAll();
@@ -1061,9 +1014,11 @@
             try {
                 EventLog.writeEvent(BACKUP_START_EVENT, mTransport.transportDirName());
 
-                // If we haven't stored anything yet, we need to do an init operation.
-                if (status == BackupConstants.TRANSPORT_OK && mEverStoredApps.size() == 0) {
-                    Log.i(TAG, "Initializing (wiping) backup transport storage");
+                // If we haven't stored package manager metadata yet, we must init the transport.
+                File pmState = new File(mStateDir, PACKAGE_MANAGER_SENTINEL);
+                if (status == BackupConstants.TRANSPORT_OK && pmState.length() <= 0) {
+                    Log.i(TAG, "Initializing (wiping) backup state and transport storage");
+                    resetBackupState(mStateDir);  // Just to make sure.
                     status = mTransport.initializeDevice();
                     if (status == BackupConstants.TRANSPORT_OK) {
                         EventLog.writeEvent(BACKUP_INITIALIZE_EVENT);
@@ -1107,10 +1062,9 @@
                 if (status == 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.
+                    // everything.  This must come after mBackupOrRestoreInProgress is cleared.
                     EventLog.writeEvent(BACKUP_RESET_EVENT, mTransport.transportDirName());
                     resetBackupState(mStateDir);
-                    backupNow();
                 }
             } catch (Exception e) {
                 Log.e(TAG, "Error in backup thread", e);
@@ -1123,11 +1077,7 @@
                 if (status != BackupConstants.TRANSPORT_OK) {
                     Log.w(TAG, "Backup pass unsuccessful, restaging");
                     for (BackupRequest req : mQueue) {
-                        try {
-                            dataChanged(req.appInfo.packageName);
-                        } catch (RemoteException e) {
-                            // can't happen; it's a local call
-                        }
+                        dataChanged(req.appInfo.packageName);
                     }
 
                     // We also want to reset the backup schedule based on whatever
@@ -1141,7 +1091,7 @@
                 // this pass's journal any more; or it failed, in which case we just
                 // re-enqueued all of these packages in the current active journal.
                 // Either way, we no longer need this pass's journal.
-                if (!mJournal.delete()) {
+                if (mJournal != null && !mJournal.delete()) {
                     Log.e(TAG, "Unable to remove backup journal file " + mJournal);
                 }
 
@@ -1150,6 +1100,12 @@
                 synchronized (mQueueLock) {
                     mBackupOrRestoreInProgress = false;
                 }
+
+                if (status == BackupConstants.TRANSPORT_NOT_INITIALIZED) {
+                    // This must come after mBackupOrRestoreInProgress is cleared.
+                    backupNow();
+                }
+
                 mWakelock.release();
             }
         }
@@ -1782,7 +1738,7 @@
 
     // ----- IBackupManager binder interface -----
 
-    public void dataChanged(String packageName) throws RemoteException {
+    public void dataChanged(String packageName) {
         // Record that we need a backup pass for the caller.  Since multiple callers
         // may share a uid, we need to note all candidates within that uid and schedule
         // a backup pass for each of them.
@@ -1840,14 +1796,17 @@
     }
 
     private void writeToJournalLocked(String str) {
-        if (mJournalStream != null) {
-            try {
-                mJournalStream.writeUTF(str);
-            } catch (IOException e) {
-                Log.e(TAG, "Error writing to backup journal");
-                mJournalStream = null;
-                mJournal = null;
-            }
+        RandomAccessFile out = null;
+        try {
+            if (mJournal == null) mJournal = File.createTempFile("journal", null, mJournalDir);
+            out = new RandomAccessFile(mJournal, "rws");
+            out.seek(out.length());
+            out.writeUTF(str);
+        } catch (IOException e) {
+            Log.e(TAG, "Can't write " + str + " to backup journal", e);
+            mJournal = null;
+        } finally {
+            try { if (out != null) out.close(); } catch (IOException e) {}
         }
     }
 
@@ -1902,7 +1861,7 @@
 
     // Run a backup pass immediately for any applications that have declared
     // that they have pending updates.
-    public void backupNow() throws RemoteException {
+    public void backupNow() {
         mContext.enforceCallingOrSelfPermission(android.Manifest.permission.BACKUP, "backupNow");
 
         if (DEBUG) Log.v(TAG, "Scheduling immediate backup pass");
@@ -2210,8 +2169,16 @@
 
             pw.println("Available transports:");
             for (String t : listAllTransports()) {
-                String pad = (t.equals(mCurrentTransport)) ? "  * " : "    ";
-                pw.println(pad + t);
+                pw.println((t.equals(mCurrentTransport) ? "  * " : "    ") + t);
+                try {
+                    File dir = new File(mBaseStateDir, getTransport(t).transportDirName());
+                    for (File f : dir.listFiles()) {
+                        pw.println("       " + f.getName() + " - " + f.length() + " state bytes");
+                    }
+                } catch (RemoteException e) {
+                    Log.e(TAG, "Error in transportDirName()", e);
+                    pw.println("        Error: " + e);
+                }
             }
 
             pw.println("Pending init: " + mPendingInits.size());