summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java321
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;
+ }
+ }
}