diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java index fa88111a2707f..75e0e64b1d927 100644 --- a/services/java/com/android/server/BackupManagerService.java +++ b/services/java/com/android/server/BackupManagerService.java @@ -54,6 +54,7 @@ import com.android.internal.backup.LocalTransport; import com.android.internal.backup.IBackupTransport; import com.android.server.PackageManagerBackupAgent; +import com.android.server.PackageManagerBackupAgent.Metadata; import java.io.EOFException; import java.io.File; @@ -111,9 +112,8 @@ class BackupManagerService extends IBackupManager.Stub { // Do we need to back up the package manager metadata on the next pass? private boolean mDoPackageManager; private static final String PACKAGE_MANAGER_SENTINEL = "@pm@"; - // Backups that we have started. These are separate to prevent starvation - // if an app keeps re-enqueuing itself. - private ArrayList mBackupQueue; + + // locking around the pending-backup management private final Object mQueueLock = new Object(); // The thread performing the sequence of queued backups binds to each app's agent @@ -296,6 +296,7 @@ class BackupManagerService extends IBackupManager.Stub { } // snapshot the pending-backup set and work on that + ArrayList queue = new ArrayList(); File oldJournal = mJournal; synchronized (mQueueLock) { if (mPendingBackups.size() == 0) { @@ -303,13 +304,11 @@ class BackupManagerService extends IBackupManager.Stub { break; } - if (mBackupQueue == null) { - mBackupQueue = new ArrayList(); - for (BackupRequest b: mPendingBackups.values()) { - mBackupQueue.add(b); - } - mPendingBackups = new HashMap(); + for (BackupRequest b: mPendingBackups.values()) { + queue.add(b); } + Log.v(TAG, "clearing pending backups"); + mPendingBackups.clear(); // Start a new backup-queue journal file too if (mJournalStream != null) { @@ -328,7 +327,7 @@ class BackupManagerService extends IBackupManager.Stub { // at next boot and the journaled requests fulfilled. } - (new PerformBackupThread(transport, mBackupQueue, oldJournal)).start(); + (new PerformBackupThread(transport, queue, oldJournal)).start(); break; } @@ -759,6 +758,8 @@ class BackupManagerService extends IBackupManager.Stub { private boolean signaturesMatch(Signature[] storedSigs, Signature[] deviceSigs) { // Allow unsigned apps, but not signed on one device and unsigned on the other // !!! TODO: is this the right policy? + if (DEBUG) Log.v(TAG, "signaturesMatch(): stored=" + storedSigs + + " device=" + deviceSigs); if ((storedSigs == null || storedSigs.length == 0) && (deviceSigs == null || deviceSigs.length == 0)) { return true; @@ -800,6 +801,7 @@ class BackupManagerService extends IBackupManager.Stub { @Override public void run() { + if (DEBUG) Log.v(TAG, "Beginning restore process"); /** * Restore sequence: * @@ -847,13 +849,19 @@ class BackupManagerService extends IBackupManager.Stub { PackageInfo app = isRestorable(pkg); if (app != null) { // Validate against the backed-up signature block, too - Signature[] storedSigs - = pmAgent.getRestoredSignatures(app.packageName); - // !!! TODO: check app version here as well - if (signaturesMatch(storedSigs, app.signatures)) { - appsToRestore.add(app); + Metadata info = pmAgent.getRestoredMetadata(app.packageName); + if (app.versionCode >= info.versionCode) { + if (DEBUG) Log.v(TAG, "Restore version " + info.versionCode + + " compatible with app version " + app.versionCode); + if (signaturesMatch(info.signatures, app.signatures)) { + appsToRestore.add(app); + } else { + Log.w(TAG, "Sig mismatch restoring " + app.packageName); + } } else { - Log.w(TAG, "Sig mismatch on restore of " + app.packageName); + Log.i(TAG, "Restore set for " + app.packageName + + " is too new [" + info.versionCode + + "] for installed app version " + app.versionCode); } } } @@ -1202,11 +1210,10 @@ class BackupManagerService extends IBackupManager.Stub { pw.println(app.toString()); } } - pw.println("Pending:"); - Iterator br = mPendingBackups.values().iterator(); - while (br.hasNext()) { - pw.print(" "); - pw.println(br); + pw.println("Pending: " + mPendingBackups.size()); + for (BackupRequest req : mPendingBackups.values()) { + pw.print(" "); + pw.println(req); } } } diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java index 6bd32a02f464a..cc8443087e133 100644 --- a/services/java/com/android/server/PackageManagerBackupAgent.java +++ b/services/java/com/android/server/PackageManagerBackupAgent.java @@ -40,9 +40,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; -// !!!TODO: take this out -import java.util.zip.CRC32; - /** * We back up the signatures of each package so that during a system restore, * we can verify that the app whose data we think we have matches the app @@ -57,7 +54,17 @@ public class PackageManagerBackupAgent extends BackupAgent { private List mAllApps; private PackageManager mPackageManager; - private HashMap mRestoredSignatures; + private HashMap mRestoredSignatures; + + public class Metadata { + public int versionCode; + public Signature[] signatures; + + Metadata(int version, Signature[] sigs) { + versionCode = version; + signatures = sigs; + } + } // We're constructed with the set of applications that are participating // in backup. This set changes as apps are installed & removed. @@ -67,7 +74,7 @@ public class PackageManagerBackupAgent extends BackupAgent { mRestoredSignatures = null; } - public Signature[] getRestoredSignatures(String packageName) { + public Metadata getRestoredMetadata(String packageName) { if (mRestoredSignatures == null) { return null; } @@ -83,6 +90,9 @@ public class PackageManagerBackupAgent extends BackupAgent { // For each app we have on device, see if we've backed it up yet. If not, // write its signature block to the output, keyed on the package name. + if (DEBUG) Log.v(TAG, "onBackup()"); + ByteArrayOutputStream bufStream = new ByteArrayOutputStream(); // we'll reuse these + DataOutputStream outWriter = new DataOutputStream(bufStream); for (ApplicationInfo app : mAllApps) { String packName = app.packageName; if (!existing.contains(packName)) { @@ -90,16 +100,27 @@ public class PackageManagerBackupAgent extends BackupAgent { try { PackageInfo info = mPackageManager.getPackageInfo(packName, PackageManager.GET_SIGNATURES); - // build a byte array out of the signature list + /* + * Metadata for each package: + * + * int version -- [4] the package's versionCode + * byte[] signatures -- [len] flattened Signature[] of the package + */ + + // marshall the version code in a canonical form + bufStream.reset(); + outWriter.writeInt(info.versionCode); + byte[] versionBuf = bufStream.toByteArray(); + byte[] sigs = flattenSignatureArray(info.signatures); -// !!! TODO: take out this debugging + + // !!! TODO: take out this debugging if (DEBUG) { - CRC32 crc = new CRC32(); - crc.update(sigs); - Log.i(TAG, "+ flat sig array for " + packName + " : " - + crc.getValue()); + Log.v(TAG, "+ metadata for " + packName + " version=" + info.versionCode); } - data.writeEntityHeader(packName, sigs.length); + // Now we can write the backup entity for this package + data.writeEntityHeader(packName, versionBuf.length + sigs.length); + data.writeEntityData(versionBuf, versionBuf.length); data.writeEntityData(sigs, sigs.length); } catch (NameNotFoundException e) { // Weird; we just found it, and now are told it doesn't exist. @@ -113,6 +134,8 @@ public class PackageManagerBackupAgent extends BackupAgent { } else { // We've already backed up this app. Remove it from the set so // we can tell at the end what has disappeared from the device. + // !!! TODO: take out the debugging message + if (DEBUG) Log.v(TAG, "= already backed up metadata for " + packName); if (!existing.remove(packName)) { Log.d(TAG, "*** failed to remove " + packName + " from package set!"); } @@ -123,6 +146,8 @@ public class PackageManagerBackupAgent extends BackupAgent { // mentioned in the saved state file, but appear to no longer be present // on the device. Write a deletion entity for them. for (String app : existing) { + // !!! TODO: take out this msg + if (DEBUG) Log.v(TAG, "- removing metadata for deleted pkg " + app); try { data.writeEntityHeader(app, -1); } catch (IOException e) { @@ -141,27 +166,29 @@ public class PackageManagerBackupAgent extends BackupAgent { public void onRestore(BackupDataInput data, ParcelFileDescriptor newState) throws IOException { List restoredApps = new ArrayList(); - HashMap sigMap = new HashMap(); + HashMap sigMap = new HashMap(); while (data.readNextHeader()) { int dataSize = data.getDataSize(); byte[] buf = new byte[dataSize]; data.readEntityData(buf, 0, dataSize); - Signature[] sigs = unflattenSignatureArray(buf); + ByteArrayInputStream bufStream = new ByteArrayInputStream(buf); + DataInputStream in = new DataInputStream(bufStream); + int versionCode = in.readInt(); + + Signature[] sigs = unflattenSignatureArray(in); String pkg = data.getKey(); // !!! TODO: take out this debugging if (DEBUG) { - CRC32 crc = new CRC32(); - crc.update(buf); - Log.i(TAG, "- unflat sig array for " + pkg + " : " - + crc.getValue()); + Log.i(TAG, "+ restored metadata for " + pkg + + " versionCode=" + versionCode + " sigs=" + sigs); } ApplicationInfo app = new ApplicationInfo(); app.packageName = pkg; restoredApps.add(app); - sigMap.put(pkg, sigs); + sigMap.put(pkg, new Metadata(versionCode, sigs)); } mRestoredSignatures = sigMap; @@ -193,9 +220,7 @@ public class PackageManagerBackupAgent extends BackupAgent { return outBuf.toByteArray(); } - private Signature[] unflattenSignatureArray(byte[] buffer) { - ByteArrayInputStream inBufStream = new ByteArrayInputStream(buffer); - DataInputStream in = new DataInputStream(inBufStream); + private Signature[] unflattenSignatureArray(/*byte[] buffer*/ DataInputStream in) { Signature[] sigs = null; try {