diff options
| -rw-r--r-- | services/java/com/android/server/PackageManagerBackupAgent.java | 136 | 
1 files changed, 94 insertions, 42 deletions
| diff --git a/services/java/com/android/server/PackageManagerBackupAgent.java b/services/java/com/android/server/PackageManagerBackupAgent.java index 16f14e8..a1b4c26 100644 --- a/services/java/com/android/server/PackageManagerBackupAgent.java +++ b/services/java/com/android/server/PackageManagerBackupAgent.java @@ -59,7 +59,14 @@ public class PackageManagerBackupAgent extends BackupAgent {      private List<PackageInfo> mAllPackages;      private PackageManager mPackageManager; +    // version & signature info of each app in a restore set      private HashMap<String, Metadata> mRestoredSignatures; +    // The version info of each backed-up app as read from the state file +    private HashMap<String, Metadata> mStateVersions = new HashMap<String, Metadata>(); + +    private final HashSet<String> mExisting = new HashSet<String>(); +    private int mStoredSdkVersion; +    private String mStoredIncrementalVersion;      public class Metadata {          public int versionCode; @@ -96,24 +103,39 @@ public class PackageManagerBackupAgent extends BackupAgent {          ByteArrayOutputStream bufStream = new ByteArrayOutputStream();  // we'll reuse these          DataOutputStream outWriter = new DataOutputStream(bufStream); -        HashSet<String> existing = parseStateFile(oldState); +        parseStateFile(oldState); + +        // If the stored version string differs, we need to re-backup all +        // of the metadata.  We force this by removing everything from the +        // "already backed up" map built by parseStateFile(). +        if (mStoredIncrementalVersion == null +                || !mStoredIncrementalVersion.equals(Build.VERSION.INCREMENTAL)) { +            Log.i(TAG, "Previous metadata " + mStoredIncrementalVersion + " mismatch vs " +                    + Build.VERSION.INCREMENTAL + " - rewriting"); +            mExisting.clear(); +        }          try {              /*               * Global metadata:               * -             * int version -- the SDK version of the OS itself on the device -             *                that produced this backup set.  Used to reject -             *                backups from later OSes onto earlier ones. +             * int SDKversion -- the SDK version of the OS itself on the device +             *                   that produced this backup set.  Used to reject +             *                   backups from later OSes onto earlier ones. +             * String incremental -- the incremental release name of the OS stored in +             *                       the backup set.               */ -            if (!existing.contains(GLOBAL_METADATA_KEY)) { +            if (!mExisting.contains(GLOBAL_METADATA_KEY)) {                  if (DEBUG) Log.v(TAG, "Storing global metadata key");                  outWriter.writeInt(Build.VERSION.SDK_INT); +                outWriter.writeUTF(Build.VERSION.INCREMENTAL);                  byte[] metadata = bufStream.toByteArray();                  data.writeEntityHeader(GLOBAL_METADATA_KEY, metadata.length);                  data.writeEntityData(metadata, metadata.length);              } else {                  if (DEBUG) Log.v(TAG, "Global metadata key already stored"); +                // don't consider it to have been skipped/deleted +                mExisting.remove(GLOBAL_METADATA_KEY);              }              // For each app we have on device, see if we've backed it up yet.  If not, @@ -123,11 +145,36 @@ public class PackageManagerBackupAgent extends BackupAgent {                  if (packName.equals(GLOBAL_METADATA_KEY)) {                      // We've already handled the metadata key; skip it here                      continue; -                } else if (!existing.contains(packName)) { -                    // We haven't stored this app's signatures yet, so we do that now +                } else { +                    PackageInfo info = null;                      try { -                        PackageInfo info = mPackageManager.getPackageInfo(packName, +                        info = mPackageManager.getPackageInfo(packName,                                  PackageManager.GET_SIGNATURES); +                    } catch (NameNotFoundException e) { +                        // Weird; we just found it, and now are told it doesn't exist. +                        // Treat it as having been removed from the device. +                        mExisting.add(packName); +                        continue; +                    } + +                    boolean doBackup = false; +                    if (!mExisting.contains(packName)) { +                        // We haven't backed up this app before +                        doBackup = true; +                    } else { +                        // We *have* backed this one up before.  Check whether the version +                        // of the backup matches the version of the current app; if they +                        // don't match, the app has been updated and we need to store its +                        // metadata again.  In either case, take it out of mExisting so that +                        // we don't consider it deleted later. +                        if (info.versionCode != mStateVersions.get(packName).versionCode) { +                            doBackup = true; +                        } +                        mExisting.remove(packName); +                    } + +                    if (doBackup) { +                        // We need to store this app's metadata                          /*                           * Metadata for each package:                           * @@ -135,7 +182,7 @@ public class PackageManagerBackupAgent extends BackupAgent {                           * byte[] signatures -- [len] flattened Signature[] of the package                           */ -                        // marshall the version code in a canonical form +                        // marshal the version code in a canonical form                          bufStream.reset();                          outWriter.writeInt(info.versionCode);                          byte[] versionBuf = bufStream.toByteArray(); @@ -153,18 +200,6 @@ public class PackageManagerBackupAgent extends BackupAgent {                          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. -                        // Treat it as having been removed from the device. -                        existing.add(packName); -                    } -                } 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!");                      }                  }              } @@ -172,7 +207,7 @@ public class PackageManagerBackupAgent extends BackupAgent {              // At this point, the only entries in 'existing' are apps that were              // 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) { +            for (String app : mExisting) {                  // !!! TODO: take out this msg                  if (DEBUG) Log.v(TAG, "- removing metadata for deleted pkg " + app);                  try { @@ -215,17 +250,21 @@ public class PackageManagerBackupAgent extends BackupAgent {              DataInputStream in = new DataInputStream(baStream);              if (key.equals(GLOBAL_METADATA_KEY)) { -                storedSystemVersion = in.readInt(); +                int storedSdkVersion = in.readInt();                  if (DEBUG) Log.v(TAG, "   storedSystemVersion = " + storedSystemVersion);                  if (storedSystemVersion > Build.VERSION.SDK_INT) {                      // returning before setting the sig map means we rejected the restore set                      Log.w(TAG, "Restore set was from a later version of Android; not restoring");                      return;                  } +                mStoredSdkVersion = storedSdkVersion; +                mStoredIncrementalVersion = in.readUTF();                  // !!! TODO: remove this debugging output                  if (DEBUG) {                      Log.i(TAG, "Restore set version " + storedSystemVersion -                            + " is compatible with OS version " + Build.VERSION.SDK_INT); +                            + " is compatible with OS version " + Build.VERSION.SDK_INT +                            + " (" + mStoredIncrementalVersion + " vs " +                            + Build.VERSION.INCREMENTAL + ")");                  }              } else {                  // it's a file metadata record @@ -302,31 +341,45 @@ public class PackageManagerBackupAgent extends BackupAgent {      }      // Util: parse out an existing state file into a usable structure -    private HashSet<String> parseStateFile(ParcelFileDescriptor stateFile) { -        HashSet<String> set = new HashSet<String>(); +    private void parseStateFile(ParcelFileDescriptor stateFile) { +        mExisting.clear(); +        mStateVersions.clear(); +        mStoredSdkVersion = 0; +        mStoredIncrementalVersion = null; +          // The state file is just the list of app names we have stored signatures for +        // with the exception of the metadata block, to which is also appended the +        // version numbers corresponding with the last time we wrote this PM block. +        // If they mismatch the current system, we'll re-store the metadata key.          FileInputStream instream = new FileInputStream(stateFile.getFileDescriptor());          DataInputStream in = new DataInputStream(instream);          int bufSize = 256;          byte[] buf = new byte[bufSize];          try { -            int nameSize = in.readInt(); -            if (bufSize < nameSize) { -                bufSize = nameSize + 32; -                buf = new byte[bufSize]; +            String pkg = in.readUTF(); +            if (pkg.equals(GLOBAL_METADATA_KEY)) { +                mStoredSdkVersion = in.readInt(); +                mStoredIncrementalVersion = in.readUTF(); +                mExisting.add(GLOBAL_METADATA_KEY); +            } else { +                Log.e(TAG, "No global metadata in state file!"); +                return; +            } + +            // The global metadata was first; now read all the apps +            while (true) { +                pkg = in.readUTF(); +                int versionCode = in.readInt(); +                mExisting.add(pkg); +                mStateVersions.put(pkg, new Metadata(versionCode, null));              } -            in.read(buf, 0, nameSize); -            String pkg = new String(buf, 0, nameSize); -            set.add(pkg);          } catch (EOFException eof) {              // safe; we're done          } catch (IOException e) {              // whoops, bad state file.  abort. -            Log.e(TAG, "Unable to read Package Manager state file"); -            return null; +            Log.e(TAG, "Unable to read Package Manager state file: " + e);          } -        return set;      }      // Util: write out our new backup state file @@ -336,15 +389,14 @@ public class PackageManagerBackupAgent extends BackupAgent {          try {              // by the time we get here we know we've stored the global metadata record -            byte[] metaNameBuf = GLOBAL_METADATA_KEY.getBytes(); -            out.writeInt(metaNameBuf.length); -            out.write(metaNameBuf); +            out.writeUTF(GLOBAL_METADATA_KEY); +            out.writeInt(Build.VERSION.SDK_INT); +            out.writeUTF(Build.VERSION.INCREMENTAL);              // now write all the app names too              for (PackageInfo pkg : pkgs) { -                byte[] pkgNameBuf = pkg.packageName.getBytes(); -                out.writeInt(pkgNameBuf.length); -                out.write(pkgNameBuf); +                out.writeUTF(pkg.packageName); +                out.writeInt(pkg.versionCode);              }          } catch (IOException e) {              Log.e(TAG, "Unable to write package manager state file!"); | 
