diff options
| -rw-r--r-- | services/java/com/android/server/BackupManagerService.java | 99 |
1 files changed, 94 insertions, 5 deletions
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index d3067ec3a5c8..47a6edeb0bec 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -51,11 +51,13 @@ import com.android.internal.backup.LocalTransport; import com.android.internal.backup.GoogleTransport; import com.android.internal.backup.IBackupTransport; +import java.io.EOFException; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; +import java.io.RandomAccessFile; import java.lang.String; import java.util.ArrayList; import java.util.HashMap; @@ -122,6 +124,9 @@ class BackupManagerService extends IBackupManager.Stub { private File mStateDir; private File mDataDir; + private File mJournalDir; + private File mJournal; + private RandomAccessFile mJournalStream; public BackupManagerService(Context context) { mContext = context; @@ -133,6 +138,11 @@ class BackupManagerService extends IBackupManager.Stub { mStateDir.mkdirs(); mDataDir = Environment.getDownloadCacheDirectory(); + // Set up the backup-request journaling + mJournalDir = new File(mStateDir, "pending"); + mJournalDir.mkdirs(); + makeJournalLocked(); // okay because no other threads are running yet + //!!! TODO: default to cloud transport, not local mTransportId = BackupManager.TRANSPORT_LOCAL; @@ -141,6 +151,10 @@ class BackupManagerService extends IBackupManager.Stub { addPackageParticipantsLocked(null); } + // Now that we know about valid backup participants, parse any + // leftover journal files and schedule a new backup pass + parseLeftoverJournals(); + // Register for broadcasts about package install, etc., so we can // update the provider list. IntentFilter filter = new IntentFilter(); @@ -150,6 +164,46 @@ class BackupManagerService extends IBackupManager.Stub { 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(); + } + } + } + } + } + // ----- Track installation/removal of packages ----- BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { @@ -198,6 +252,8 @@ class BackupManagerService extends IBackupManager.Stub { switch (msg.what) { case MSG_RUN_BACKUP: // snapshot the pending-backup set and work on that + File oldJournal = mJournal; + RandomAccessFile oldJournalStream = mJournalStream; synchronized (mQueueLock) { if (mBackupQueue == null) { mBackupQueue = new ArrayList<BackupRequest>(); @@ -207,10 +263,22 @@ class BackupManagerService extends IBackupManager.Stub { mPendingBackups = new HashMap<ApplicationInfo,BackupRequest>(); } // !!! TODO: start a new backup-queue journal file too - // WARNING: If we crash after this line, anything in mPendingBackups will - // be lost. FIX THIS. + if (mJournalStream != null) { + try { + mJournalStream.close(); + } catch (IOException e) { + // don't need to do anything + } + makeJournalLocked(); + } + + // At this point, we have started a new journal file, and the old + // file identity is being passed to the backup processing thread. + // When it completes successfully, that old journal file will be + // deleted. If we crash prior to that, the old journal is parsed + // at next boot and the journaled requests fulfilled. } - (new PerformBackupThread(mTransportId, mBackupQueue)).run(); + (new PerformBackupThread(mTransportId, mBackupQueue, oldJournal)).run(); break; case MSG_RUN_FULL_BACKUP: @@ -433,10 +501,13 @@ class BackupManagerService extends IBackupManager.Stub { private static final String TAG = "PerformBackupThread"; int mTransport; ArrayList<BackupRequest> mQueue; + File mJournal; - public PerformBackupThread(int transportId, ArrayList<BackupRequest> queue) { + public PerformBackupThread(int transportId, ArrayList<BackupRequest> queue, + File journal) { mTransport = transportId; mQueue = queue; + mJournal = journal; } @Override @@ -468,6 +539,10 @@ class BackupManagerService extends IBackupManager.Stub { Log.e(TAG, "Error ending transport"); e.printStackTrace(); } + + if (!mJournal.delete()) { + Log.e(TAG, "Unable to remove backup journal file " + mJournal.getAbsolutePath()); + } } private void doQueuedBackups(IBackupTransport transport) { @@ -782,7 +857,9 @@ class BackupManagerService extends IBackupManager.Stub { // one already there, then overwrite it, but no harm done. BackupRequest req = new BackupRequest(app, false); mPendingBackups.put(app, req); - // !!! TODO: write to the pending-backup journal file in case of crash + + // Journal this request in case of crash + writeToJournalLocked(packageName); } } @@ -804,6 +881,18 @@ class BackupManagerService extends IBackupManager.Stub { } } + 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; + } + } + } + // Schedule a backup pass for a given package. This method will schedule a // full backup even for apps that do not declare an android:backupAgent, so // use with care. |