diff options
| -rw-r--r-- | services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java | 321 | 
1 files changed, 248 insertions, 73 deletions
| diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java index 2d2993dced69..3cf374faada4 100644 --- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java +++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java @@ -70,12 +70,28 @@ public class PackageManagerBackupAgent extends BackupAgent {      private static final String DEFAULT_HOME_KEY = "@home@";      // Sentinel: start of state file, followed by a version number +    // Note that STATE_FILE_VERSION=2 is tied to UNDEFINED_ANCESTRAL_RECORD_VERSION=-1 *as well as* +    // ANCESTRAL_RECORD_VERSION=1 (introduced Android P). +    // Should the ANCESTRAL_RECORD_VERSION be bumped up in the future, STATE_FILE_VERSION will also +    // need bumping up, assuming more data needs saving to the state file.      private static final String STATE_FILE_HEADER = "=state=";      private static final int STATE_FILE_VERSION = 2; -    // Current version of the saved ancestral-dataset file format +    // key under which we store the saved ancestral-dataset format (starting from Android P) +    // IMPORTANT: this key needs to come first in the restore data stream (to find out +    // whether this version of Android knows how to restore the incoming data set), so it needs +    // to be always the first one in alphabetical order of all the keys +    private static final String ANCESTRAL_RECORD_KEY = "@ancestral_record@"; + +    // Current version of the saved ancestral-dataset format +    // Note that this constant was not used until Android P, and started being used +    // to version @pm@ data for forwards-compatibility.      private static final int ANCESTRAL_RECORD_VERSION = 1; +    // Undefined version of the saved ancestral-dataset file format means that the restore data +    // is coming from pre-Android P device. +    private static final int UNDEFINED_ANCESTRAL_RECORD_VERSION = -1; +      private List<PackageInfo> mAllPackages;      private PackageManager mPackageManager;      // version & signature info of each app in a restore set @@ -175,9 +191,8 @@ public class PackageManagerBackupAgent extends BackupAgent {          // additional involvement by the transport to obtain.          return mRestoredSignatures.keySet();      } -     -    // The backed up data is the signature block for each app, keyed by -    // the package name. + +    // The backed up data is the signature block for each app, keyed by the package name.      public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data,              ParcelFileDescriptor newState) {          if (DEBUG) Slog.v(TAG, "onBackup()"); @@ -196,6 +211,22 @@ public class PackageManagerBackupAgent extends BackupAgent {              mExisting.clear();          } +        /* +         * Ancestral record version: +         * +         * int ancestralRecordVersion -- the version of the format in which this backup set is +         *                               produced +         */ +        try { +            if (DEBUG) Slog.v(TAG, "Storing ancestral record version key"); +            outputBufferStream.writeInt(ANCESTRAL_RECORD_VERSION); +            writeEntity(data, ANCESTRAL_RECORD_KEY, outputBuffer.toByteArray()); +        } catch (IOException e) { +            // Real error writing data +            Slog.e(TAG, "Unable to write package backup data file!"); +            return; +        } +          long homeVersion = 0;          ArrayList<byte[]> homeSigHashes = null;          PackageInfo homeInfo = null; @@ -230,6 +261,7 @@ public class PackageManagerBackupAgent extends BackupAgent {                      Slog.i(TAG, "Home preference changed; backing up new state " + home);                  }                  if (home != null) { +                    outputBuffer.reset();                      outputBufferStream.writeUTF(home.flattenToString());                      outputBufferStream.writeLong(homeVersion);                      outputBufferStream.writeUTF(homeInstaller != null ? homeInstaller : "" ); @@ -244,8 +276,8 @@ public class PackageManagerBackupAgent extends BackupAgent {               * Global metadata:               *               * 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. +             *                   that produced this backup set. Before Android P it was used to +             *                   reject backups from later OSes onto earlier ones.               * String incremental -- the incremental release name of the OS stored in               *                       the backup set.               */ @@ -354,7 +386,7 @@ public class PackageManagerBackupAgent extends BackupAgent {          // Finally, write the new state blob -- just the list of all apps we handled          writeStateFile(mAllPackages, home, homeVersion, homeSigHashes, newState);      } -     +      private static void writeEntity(BackupDataOutput data, String key, byte[] bytes)              throws IOException {          data.writeEntityHeader(key, bytes.length); @@ -366,83 +398,57 @@ public class PackageManagerBackupAgent extends BackupAgent {      // image.  We'll use those later to determine what we can legitimately restore.      public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState)              throws IOException { -        List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>(); -        HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>();          if (DEBUG) Slog.v(TAG, "onRestore()"); -        int storedSystemVersion = -1; -        while (data.readNextHeader()) { +        // we expect the ANCESTRAL_RECORD_KEY ("@ancestral_record@") to always come first in the +        // restore set - based on that value we use different mechanisms to consume the data; +        // if the ANCESTRAL_RECORD_KEY is missing in the restore set, it means that the data is +        // is coming from a pre-Android P device, and we consume the header data in the legacy way +        // TODO: add a CTS test to verify that backups of PMBA generated on Android P+ always +        //       contain the ANCESTRAL_RECORD_KEY, and it's always the first key +        int ancestralRecordVersion = getAncestralRecordVersionValue(data); + +        RestoreDataConsumer consumer = getRestoreDataConsumer(ancestralRecordVersion); +        if (consumer == null) { +            Slog.w(TAG, "Ancestral restore set version is unknown" +                    + " to this Android version; not restoring"); +            return; +        } else { +            consumer.consumeRestoreData(data); +        } +    } + +    private int getAncestralRecordVersionValue(BackupDataInput data) throws IOException { +        int ancestralRecordVersionValue = UNDEFINED_ANCESTRAL_RECORD_VERSION; +        if (data.readNextHeader()) {              String key = data.getKey();              int dataSize = data.getDataSize();              if (DEBUG) Slog.v(TAG, "   got key=" + key + " dataSize=" + dataSize); -            // generic setup to parse any entity data -            byte[] inputBytes = new byte[dataSize]; -            data.readEntityData(inputBytes, 0, dataSize); -            ByteArrayInputStream inputBuffer = new ByteArrayInputStream(inputBytes); -            DataInputStream inputBufferStream = new DataInputStream(inputBuffer); - -            if (key.equals(GLOBAL_METADATA_KEY)) { -                int storedSdkVersion = inputBufferStream.readInt(); -                if (DEBUG) Slog.v(TAG, "   storedSystemVersion = " + storedSystemVersion); -                if (storedSystemVersion > Build.VERSION.SDK_INT) { -                    // returning before setting the sig map means we rejected the restore set -                    Slog.w(TAG, "Restore set was from a later version of Android; not restoring"); -                    return; -                } -                mStoredSdkVersion = storedSdkVersion; -                mStoredIncrementalVersion = inputBufferStream.readUTF(); -                mHasMetadata = true; -                if (DEBUG) { -                    Slog.i(TAG, "Restore set version " + storedSystemVersion -                            + " is compatible with OS version " + Build.VERSION.SDK_INT -                            + " (" + mStoredIncrementalVersion + " vs " -                            + Build.VERSION.INCREMENTAL + ")"); -                } -            } else if (key.equals(DEFAULT_HOME_KEY)) { -                String cn = inputBufferStream.readUTF(); -                mRestoredHome = ComponentName.unflattenFromString(cn); -                mRestoredHomeVersion = inputBufferStream.readLong(); -                mRestoredHomeInstaller = inputBufferStream.readUTF(); -                mRestoredHomeSigHashes = readSignatureHashArray(inputBufferStream); -                if (DEBUG) { -                    Slog.i(TAG, "   read preferred home app " + mRestoredHome -                            + " version=" + mRestoredHomeVersion -                            + " installer=" + mRestoredHomeInstaller -                            + " sig=" + mRestoredHomeSigHashes); -                } -            } else { -                // it's a file metadata record -                int versionCodeInt = inputBufferStream.readInt(); -                long versionCode; -                if (versionCodeInt == Integer.MIN_VALUE) { -                    versionCode = inputBufferStream.readLong(); -                } else { -                    versionCode = versionCodeInt; -                } -                ArrayList<byte[]> sigs = readSignatureHashArray(inputBufferStream); -                if (DEBUG) { -                    Slog.i(TAG, "   read metadata for " + key -                            + " dataSize=" + dataSize -                            + " versionCode=" + versionCode + " sigs=" + sigs); -                } -                 -                if (sigs == null || sigs.size() == 0) { -                    Slog.w(TAG, "Not restoring package " + key -                            + " since it appears to have no signatures."); -                    continue; -                } +            if (ANCESTRAL_RECORD_KEY.equals(key)) { +                // generic setup to parse any entity data +                byte[] inputBytes = new byte[dataSize]; +                data.readEntityData(inputBytes, 0, dataSize); +                ByteArrayInputStream inputBuffer = new ByteArrayInputStream(inputBytes); +                DataInputStream inputBufferStream = new DataInputStream(inputBuffer); -                ApplicationInfo app = new ApplicationInfo(); -                app.packageName = key; -                restoredApps.add(app); -                sigMap.put(key, new Metadata(versionCode, sigs)); +                ancestralRecordVersionValue = inputBufferStream.readInt();              }          } +        return ancestralRecordVersionValue; +    } -        // On successful completion, cache the signature map for the Backup Manager to use -        mRestoredSignatures = sigMap; +    private RestoreDataConsumer getRestoreDataConsumer(int ancestralRecordVersion) { +        switch (ancestralRecordVersion) { +            case UNDEFINED_ANCESTRAL_RECORD_VERSION: +                return new LegacyRestoreDataConsumer(); +            case 1: +                return new AncestralVersion1RestoreDataConsumer(); +            default: +                Slog.e(TAG, "Unrecognized ANCESTRAL_RECORD_VERSION: " + ancestralRecordVersion); +                return null; +        }      }      private static void writeSignatureHashArray(DataOutputStream out, ArrayList<byte[]> hashes) @@ -639,4 +645,173 @@ public class PackageManagerBackupAgent extends BackupAgent {              Slog.e(TAG, "Unable to write package manager state file!");          }      } + +    interface RestoreDataConsumer { +        void consumeRestoreData(BackupDataInput data) throws IOException; +    } + +    private class LegacyRestoreDataConsumer implements RestoreDataConsumer { + +        public void consumeRestoreData(BackupDataInput data) throws IOException { +            List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>(); +            HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>(); +            int storedSystemVersion = -1; + +            if (DEBUG) Slog.i(TAG, "Using LegacyRestoreDataConsumer"); +            // we already have the first header read and "cached", since ANCESTRAL_RECORD_KEY +            // was missing +            while (true) { +                String key = data.getKey(); +                int dataSize = data.getDataSize(); + +                if (DEBUG) Slog.v(TAG, "   got key=" + key + " dataSize=" + dataSize); + +                // generic setup to parse any entity data +                byte[] inputBytes = new byte[dataSize]; +                data.readEntityData(inputBytes, 0, dataSize); +                ByteArrayInputStream inputBuffer = new ByteArrayInputStream(inputBytes); +                DataInputStream inputBufferStream = new DataInputStream(inputBuffer); + +                if (key.equals(GLOBAL_METADATA_KEY)) { +                    int storedSdkVersion = inputBufferStream.readInt(); +                    if (DEBUG) Slog.v(TAG, "   storedSystemVersion = " + storedSystemVersion); +                    mStoredSdkVersion = storedSdkVersion; +                    mStoredIncrementalVersion = inputBufferStream.readUTF(); +                    mHasMetadata = true; +                    if (DEBUG) { +                        Slog.i(TAG, "Restore set version " + storedSystemVersion +                                + " is compatible with OS version " + Build.VERSION.SDK_INT +                                + " (" + mStoredIncrementalVersion + " vs " +                                + Build.VERSION.INCREMENTAL + ")"); +                    } +                } else if (key.equals(DEFAULT_HOME_KEY)) { +                    String cn = inputBufferStream.readUTF(); +                    mRestoredHome = ComponentName.unflattenFromString(cn); +                    mRestoredHomeVersion = inputBufferStream.readLong(); +                    mRestoredHomeInstaller = inputBufferStream.readUTF(); +                    mRestoredHomeSigHashes = readSignatureHashArray(inputBufferStream); +                    if (DEBUG) { +                        Slog.i(TAG, "   read preferred home app " + mRestoredHome +                                + " version=" + mRestoredHomeVersion +                                + " installer=" + mRestoredHomeInstaller +                                + " sig=" + mRestoredHomeSigHashes); +                    } +                } else { +                    // it's a file metadata record +                    int versionCodeInt = inputBufferStream.readInt(); +                    long versionCode; +                    if (versionCodeInt == Integer.MIN_VALUE) { +                        versionCode = inputBufferStream.readLong(); +                    } else { +                        versionCode = versionCodeInt; +                    } +                    ArrayList<byte[]> sigs = readSignatureHashArray(inputBufferStream); +                    if (DEBUG) { +                        Slog.i(TAG, "   read metadata for " + key +                                + " dataSize=" + dataSize +                                + " versionCode=" + versionCode + " sigs=" + sigs); +                    } + +                    if (sigs == null || sigs.size() == 0) { +                        Slog.w(TAG, "Not restoring package " + key +                                + " since it appears to have no signatures."); +                        continue; +                    } + +                    ApplicationInfo app = new ApplicationInfo(); +                    app.packageName = key; +                    restoredApps.add(app); +                    sigMap.put(key, new Metadata(versionCode, sigs)); +                } + +                boolean readNextHeader = data.readNextHeader(); +                if (!readNextHeader) { +                    if (DEBUG) Slog.v(TAG, "LegacyRestoreDataConsumer:" +                            + " we're done reading all the headers"); +                    break; +                } +            } + +            // On successful completion, cache the signature map for the Backup Manager to use +            mRestoredSignatures = sigMap; +        } +    } + +    private class AncestralVersion1RestoreDataConsumer implements RestoreDataConsumer { + +        public void consumeRestoreData(BackupDataInput data) throws IOException { +            List<ApplicationInfo> restoredApps = new ArrayList<ApplicationInfo>(); +            HashMap<String, Metadata> sigMap = new HashMap<String, Metadata>(); +            int storedSystemVersion = -1; + +            if (DEBUG) Slog.i(TAG, "Using AncestralVersion1RestoreDataConsumer"); +            while (data.readNextHeader()) { +                String key = data.getKey(); +                int dataSize = data.getDataSize(); + +                if (DEBUG) Slog.v(TAG, "   got key=" + key + " dataSize=" + dataSize); + +                // generic setup to parse any entity data +                byte[] inputBytes = new byte[dataSize]; +                data.readEntityData(inputBytes, 0, dataSize); +                ByteArrayInputStream inputBuffer = new ByteArrayInputStream(inputBytes); +                DataInputStream inputBufferStream = new DataInputStream(inputBuffer); + +                if (key.equals(GLOBAL_METADATA_KEY)) { +                    int storedSdkVersion = inputBufferStream.readInt(); +                    if (DEBUG) Slog.v(TAG, "   storedSystemVersion = " + storedSystemVersion); +                    mStoredSdkVersion = storedSdkVersion; +                    mStoredIncrementalVersion = inputBufferStream.readUTF(); +                    mHasMetadata = true; +                    if (DEBUG) { +                        Slog.i(TAG, "Restore set version " + storedSystemVersion +                                + " is compatible with OS version " + Build.VERSION.SDK_INT +                                + " (" + mStoredIncrementalVersion + " vs " +                                + Build.VERSION.INCREMENTAL + ")"); +                    } +                } else if (key.equals(DEFAULT_HOME_KEY)) { +                    String cn = inputBufferStream.readUTF(); +                    mRestoredHome = ComponentName.unflattenFromString(cn); +                    mRestoredHomeVersion = inputBufferStream.readLong(); +                    mRestoredHomeInstaller = inputBufferStream.readUTF(); +                    mRestoredHomeSigHashes = readSignatureHashArray(inputBufferStream); +                    if (DEBUG) { +                        Slog.i(TAG, "   read preferred home app " + mRestoredHome +                                + " version=" + mRestoredHomeVersion +                                + " installer=" + mRestoredHomeInstaller +                                + " sig=" + mRestoredHomeSigHashes); +                    } +                } else { +                    // it's a file metadata record +                    int versionCodeInt = inputBufferStream.readInt(); +                    long versionCode; +                    if (versionCodeInt == Integer.MIN_VALUE) { +                        versionCode = inputBufferStream.readLong(); +                    } else { +                        versionCode = versionCodeInt; +                    } +                    ArrayList<byte[]> sigs = readSignatureHashArray(inputBufferStream); +                    if (DEBUG) { +                        Slog.i(TAG, "   read metadata for " + key +                                + " dataSize=" + dataSize +                                + " versionCode=" + versionCode + " sigs=" + sigs); +                    } + +                    if (sigs == null || sigs.size() == 0) { +                        Slog.w(TAG, "Not restoring package " + key +                                + " since it appears to have no signatures."); +                        continue; +                    } + +                    ApplicationInfo app = new ApplicationInfo(); +                    app.packageName = key; +                    restoredApps.add(app); +                    sigMap.put(key, new Metadata(versionCode, sigs)); +                } +            } + +            // On successful completion, cache the signature map for the Backup Manager to use +            mRestoredSignatures = sigMap; +        } +    }  } |