diff options
-rw-r--r-- | services/backup/java/com/android/server/backup/BackupManagerService.java | 6 | ||||
-rw-r--r-- | services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java | 124 |
2 files changed, 120 insertions, 10 deletions
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java index 57c2f92..0b688b6 100644 --- a/services/backup/java/com/android/server/backup/BackupManagerService.java +++ b/services/backup/java/com/android/server/backup/BackupManagerService.java @@ -4690,7 +4690,11 @@ public class BackupManagerService extends IBackupManager.Stub { // ----- Restore handling ----- - private boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) { + static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) { + if (target == null) { + return false; + } + // If the target resides on the system partition, we allow it to restore // data from the like-named package in a restore set even if the signatures // do not match. (Unlike general applications, those flashed to the system diff --git a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java index 495da88..39f2441 100644 --- a/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java +++ b/services/backup/java/com/android/server/backup/PackageManagerBackupAgent.java @@ -19,15 +19,19 @@ package com.android.server.backup; import android.app.backup.BackupAgent; import android.app.backup.BackupDataInput; import android.app.backup.BackupDataOutput; +import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; +import android.content.pm.ResolveInfo; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.Signature; import android.os.Build; import android.os.ParcelFileDescriptor; import android.util.Slog; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; @@ -42,6 +46,8 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.Objects; + /** * 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 @@ -58,6 +64,9 @@ public class PackageManagerBackupAgent extends BackupAgent { // is stored using the package name as a key) private static final String GLOBAL_METADATA_KEY = "@meta@"; + // key under which we store the identity of the user's chosen default home app + private static final String DEFAULT_HOME_KEY = "@home@"; + private List<PackageInfo> mAllPackages; private PackageManager mPackageManager; // version & signature info of each app in a restore set @@ -68,7 +77,15 @@ public class PackageManagerBackupAgent extends BackupAgent { private final HashSet<String> mExisting = new HashSet<String>(); private int mStoredSdkVersion; private String mStoredIncrementalVersion; + private ComponentName mStoredHomeComponent; + private long mStoredHomeVersion; + private Signature[] mStoredHomeSigs; + private boolean mHasMetadata; + private ComponentName mRestoredHome; + private long mRestoredHomeVersion; + private String mRestoredHomeInstaller; + private Signature[] mRestoredHomeSignatures; public class Metadata { public int versionCode; @@ -136,7 +153,50 @@ public class PackageManagerBackupAgent extends BackupAgent { mExisting.clear(); } + long homeVersion = 0; + Signature[] homeSigs = null; + PackageInfo homeInfo = null; + String homeInstaller = null; + ComponentName home = getPreferredHomeComponent(); + if (home != null) { + try { + homeInfo = mPackageManager.getPackageInfo(home.getPackageName(), + PackageManager.GET_SIGNATURES); + homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName()); + homeVersion = homeInfo.versionCode; + homeSigs = homeInfo.signatures; + } catch (NameNotFoundException e) { + Slog.w(TAG, "Can't access preferred home info"); + // proceed as though there were no preferred home set + home = null; + } + } + try { + // We need to push a new preferred-home-app record if: + // 1. the version of the home app has changed since our last backup; + // 2. the home app [or absence] we now use differs from the prior state, + // OR 3. it looks like we use the same home app + version as before, but + // the signatures don't match so we treat them as different apps. + final boolean needHomeBackup = (homeVersion != mStoredHomeVersion) + || Objects.equals(home, mStoredHomeComponent) + || (home != null + && !BackupManagerService.signaturesMatch(mStoredHomeSigs, homeInfo)); + if (needHomeBackup) { + if (DEBUG) { + Slog.i(TAG, "Home preference changed; backing up new state " + home); + } + if (home != null) { + outputBufferStream.writeUTF(home.flattenToString()); + outputBufferStream.writeLong(homeVersion); + outputBufferStream.writeUTF(homeInstaller); + writeSignatureArray(outputBufferStream, homeSigs); + writeEntity(data, DEFAULT_HOME_KEY, outputBuffer.toByteArray()); + } else { + data.writeEntityHeader(DEFAULT_HOME_KEY, -1); + } + } + /* * Global metadata: * @@ -146,6 +206,7 @@ public class PackageManagerBackupAgent extends BackupAgent { * String incremental -- the incremental release name of the OS stored in * the backup set. */ + outputBuffer.reset(); if (!mExisting.contains(GLOBAL_METADATA_KEY)) { if (DEBUG) Slog.v(TAG, "Storing global metadata key"); outputBufferStream.writeInt(Build.VERSION.SDK_INT); @@ -238,7 +299,7 @@ public class PackageManagerBackupAgent extends BackupAgent { } // Finally, write the new state blob -- just the list of all apps we handled - writeStateFile(mAllPackages, newState); + writeStateFile(mAllPackages, home, homeVersion, homeSigs, newState); } private static void writeEntity(BackupDataOutput data, String key, byte[] bytes) @@ -286,6 +347,19 @@ public class PackageManagerBackupAgent extends BackupAgent { + " (" + 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(); + mRestoredHomeSignatures = readSignatureArray(inputBufferStream); + if (DEBUG) { + Slog.i(TAG, " read preferred home app " + mRestoredHome + + " version=" + mRestoredHomeVersion + + " installer=" + mRestoredHomeVersion + + " sig=" + mRestoredHomeVersion); + } + } else { // it's a file metadata record int versionCode = inputBufferStream.readInt(); @@ -365,18 +439,34 @@ public class PackageManagerBackupAgent extends BackupAgent { mStateVersions.clear(); mStoredSdkVersion = 0; mStoredIncrementalVersion = null; + mStoredHomeComponent = null; + mStoredHomeVersion = 0; + mStoredHomeSigs = 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); + BufferedInputStream inbuffer = new BufferedInputStream(instream); + DataInputStream in = new DataInputStream(inbuffer); - int bufSize = 256; - byte[] buf = new byte[bufSize]; try { String pkg = in.readUTF(); + + // First comes the preferred home app data, if any, headed by the DEFAULT_HOME_KEY tag + if (pkg.equals(DEFAULT_HOME_KEY)) { + // flattened component name, version, signature of the home app + mStoredHomeComponent = ComponentName.unflattenFromString(in.readUTF()); + mStoredHomeVersion = in.readLong(); + mStoredHomeSigs = readSignatureArray(in); + + pkg = in.readUTF(); // set up for the next block of state + } else { + // else no preferred home app on the ancestral device - fall through to the rest + } + + // After (possible) home app data comes the global metadata block if (pkg.equals(GLOBAL_METADATA_KEY)) { mStoredSdkVersion = in.readInt(); mStoredIncrementalVersion = in.readUTF(); @@ -386,7 +476,7 @@ public class PackageManagerBackupAgent extends BackupAgent { return; } - // The global metadata was first; now read all the apps + // The global metadata was last; now read all the apps while (true) { pkg = in.readUTF(); int versionCode = in.readInt(); @@ -401,13 +491,28 @@ public class PackageManagerBackupAgent extends BackupAgent { } } + private ComponentName getPreferredHomeComponent() { + return mPackageManager.getHomeActivities(new ArrayList<ResolveInfo>()); + } + // Util: write out our new backup state file - private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) { + private void writeStateFile(List<PackageInfo> pkgs, ComponentName preferredHome, + long homeVersion, Signature[] homeSignatures, ParcelFileDescriptor stateFile) { FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor()); - DataOutputStream out = new DataOutputStream(outstream); + BufferedOutputStream outbuf = new BufferedOutputStream(outstream); + DataOutputStream out = new DataOutputStream(outbuf); + // by the time we get here we know we've done all our backing up try { - // by the time we get here we know we've stored the global metadata record + // If we remembered a preferred home app, record that + if (preferredHome != null) { + out.writeUTF(DEFAULT_HOME_KEY); + out.writeUTF(preferredHome.flattenToString()); + out.writeLong(homeVersion); + writeSignatureArray(out, homeSignatures); + } + + // Conclude with the metadata block out.writeUTF(GLOBAL_METADATA_KEY); out.writeInt(Build.VERSION.SDK_INT); out.writeUTF(Build.VERSION.INCREMENTAL); @@ -417,9 +522,10 @@ public class PackageManagerBackupAgent extends BackupAgent { out.writeUTF(pkg.packageName); out.writeInt(pkg.versionCode); } + + out.flush(); } catch (IOException e) { Slog.e(TAG, "Unable to write package manager state file!"); - return; } } } |