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; + } + } } |