Back up the preferred home app, if any

If the user has stated a preference about their home app, make sure we
capture that so that a system restore can return them to that preferred
situation.  It's built into the system metadata so that we can, if
necessary, fast-path configuration of that home app while the rest of
the restore operation is in flight.

Change-Id: I64dfee8f7a2a9e40f556cd19298d7b367c6aa8dc
This commit is contained in:
Christopher Tate
2014-04-04 16:58:10 -07:00
parent 118d113478
commit a99d02170b
2 changed files with 122 additions and 12 deletions

View File

@@ -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

View File

@@ -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 {
}
}
// Util: write out our new backup state file
private void writeStateFile(List<PackageInfo> pkgs, ParcelFileDescriptor stateFile) {
FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
DataOutputStream out = new DataOutputStream(outstream);
private ComponentName getPreferredHomeComponent() {
return mPackageManager.getHomeActivities(new ArrayList<ResolveInfo>());
}
// Util: write out our new backup state file
private void writeStateFile(List<PackageInfo> pkgs, ComponentName preferredHome,
long homeVersion, Signature[] homeSignatures, ParcelFileDescriptor stateFile) {
FileOutputStream outstream = new FileOutputStream(stateFile.getFileDescriptor());
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;
}
}
}