Merge "Allow restoring of apps that rotated key"
This commit is contained in:
committed by
Android (Google) Code Review
commit
961f409d37
@@ -544,4 +544,16 @@ public abstract class PackageManagerInternal {
|
||||
/** Updates the flags for the given permission. */
|
||||
public abstract void updatePermissionFlagsTEMP(@NonNull String permName,
|
||||
@NonNull String packageName, int flagMask, int flagValues, int userId);
|
||||
|
||||
/**
|
||||
* Returns true if it's still safe to restore data backed up from this app's version
|
||||
* that was signed with restoringFromSigHash.
|
||||
*/
|
||||
public abstract boolean isDataRestoreSafe(byte[] restoringFromSigHash, String packageName);
|
||||
|
||||
/**
|
||||
* Returns true if it's still safe to restore data backed up from this app's version
|
||||
* that was signed with restoringFromSig.
|
||||
*/
|
||||
public abstract boolean isDataRestoreSafe(Signature restoringFromSig, String packageName);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.content.ComponentName;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.Signature;
|
||||
@@ -30,6 +31,7 @@ import android.os.Build;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.backup.utils.AppBackupUtils;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
@@ -235,7 +237,7 @@ public class PackageManagerBackupAgent extends BackupAgent {
|
||||
if (home != null) {
|
||||
try {
|
||||
homeInfo = mPackageManager.getPackageInfo(home.getPackageName(),
|
||||
PackageManager.GET_SIGNATURES);
|
||||
PackageManager.GET_SIGNING_CERTIFICATES);
|
||||
homeInstaller = mPackageManager.getInstallerPackageName(home.getPackageName());
|
||||
homeVersion = homeInfo.getLongVersionCode();
|
||||
homeSigHashes = BackupUtils.hashSignatureArray(homeInfo.signatures);
|
||||
@@ -252,10 +254,11 @@ public class PackageManagerBackupAgent extends BackupAgent {
|
||||
// 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.
|
||||
PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
|
||||
final boolean needHomeBackup = (homeVersion != mStoredHomeVersion)
|
||||
|| !Objects.equals(home, mStoredHomeComponent)
|
||||
|| (home != null
|
||||
&& !BackupUtils.signaturesMatch(mStoredHomeSigHashes, homeInfo));
|
||||
&& !BackupUtils.signaturesMatch(mStoredHomeSigHashes, homeInfo, pmi));
|
||||
if (needHomeBackup) {
|
||||
if (DEBUG) {
|
||||
Slog.i(TAG, "Home preference changed; backing up new state " + home);
|
||||
|
||||
@@ -36,11 +36,13 @@ import android.app.backup.IFullBackupRestoreObserver;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.os.RemoteException;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.backup.BackupRestoreTask;
|
||||
import com.android.server.backup.FileMetadata;
|
||||
import com.android.server.backup.KeyValueAdbRestoreEngine;
|
||||
@@ -207,8 +209,11 @@ public class FullRestoreEngine extends RestoreEngine {
|
||||
if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
|
||||
Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
|
||||
info);
|
||||
PackageManagerInternal pmi = LocalServices.getService(
|
||||
PackageManagerInternal.class);
|
||||
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
|
||||
mBackupManagerService.getPackageManager(), allowApks, info, signatures);
|
||||
mBackupManagerService.getPackageManager(), allowApks, info, signatures,
|
||||
pmi);
|
||||
mManifestSignatures.put(info.packageName, signatures);
|
||||
mPackagePolicies.put(pkg, restorePolicy);
|
||||
mPackageInstallers.put(pkg, info.installerPackageName);
|
||||
|
||||
@@ -40,6 +40,7 @@ import android.app.backup.IFullBackupRestoreObserver;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.Environment;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
@@ -47,6 +48,7 @@ import android.os.RemoteException;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.backup.BackupManagerService;
|
||||
import com.android.server.backup.FileMetadata;
|
||||
import com.android.server.backup.KeyValueAdbRestoreEngine;
|
||||
@@ -470,9 +472,11 @@ public class PerformAdbRestoreTask implements Runnable {
|
||||
if (info.path.equals(BACKUP_MANIFEST_FILENAME)) {
|
||||
Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
|
||||
info);
|
||||
PackageManagerInternal pmi = LocalServices.getService(
|
||||
PackageManagerInternal.class);
|
||||
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
|
||||
mBackupManagerService.getPackageManager(), allowApks,
|
||||
info, signatures);
|
||||
info, signatures, pmi);
|
||||
mManifestSignatures.put(info.packageName, signatures);
|
||||
mPackagePolicies.put(pkg, restorePolicy);
|
||||
mPackageInstallers.put(pkg, info.installerPackageName);
|
||||
|
||||
@@ -43,6 +43,7 @@ import android.app.backup.RestoreDescription;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
@@ -57,6 +58,7 @@ import android.util.Slog;
|
||||
import com.android.internal.backup.IBackupTransport;
|
||||
import com.android.server.AppWidgetBackupBridge;
|
||||
import com.android.server.EventLogTags;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.backup.BackupRestoreTask;
|
||||
import com.android.server.backup.BackupUtils;
|
||||
import com.android.server.backup.PackageManagerBackupAgent;
|
||||
@@ -504,7 +506,7 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
|
||||
|
||||
try {
|
||||
mCurrentPackage = backupManagerService.getPackageManager().getPackageInfo(
|
||||
pkgName, PackageManager.GET_SIGNATURES);
|
||||
pkgName, PackageManager.GET_SIGNING_CERTIFICATES);
|
||||
} catch (NameNotFoundException e) {
|
||||
// Whoops, we thought we could restore this package but it
|
||||
// turns out not to be present. Skip it.
|
||||
@@ -619,7 +621,8 @@ public class PerformUnifiedRestoreTask implements BackupRestoreTask {
|
||||
}
|
||||
|
||||
Metadata metaInfo = mPmAgent.getRestoredMetadata(packageName);
|
||||
if (!BackupUtils.signaturesMatch(metaInfo.sigHashes, mCurrentPackage)) {
|
||||
PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
|
||||
if (!BackupUtils.signaturesMatch(metaInfo.sigHashes, mCurrentPackage, pmi)) {
|
||||
Slog.w(TAG, "Signature mismatch restoring " + packageName);
|
||||
mMonitor = BackupManagerMonitorUtils.monitorEvent(mMonitor,
|
||||
BackupManagerMonitor.LOG_EVENT_ID_SIGNATURE_MISMATCH, mCurrentPackage,
|
||||
|
||||
@@ -25,6 +25,7 @@ import android.app.backup.BackupTransport;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.Process;
|
||||
import android.util.Slog;
|
||||
@@ -37,6 +38,9 @@ import com.android.server.backup.transport.TransportClient;
|
||||
* Utility methods wrapping operations on ApplicationInfo and PackageInfo.
|
||||
*/
|
||||
public class AppBackupUtils {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
/**
|
||||
* Returns whether app is eligible for backup.
|
||||
*
|
||||
@@ -88,7 +92,8 @@ public class AppBackupUtils {
|
||||
public static boolean appIsRunningAndEligibleForBackupWithTransport(
|
||||
@Nullable TransportClient transportClient, String packageName, PackageManager pm) {
|
||||
try {
|
||||
PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
|
||||
PackageInfo packageInfo = pm.getPackageInfo(packageName,
|
||||
PackageManager.GET_SIGNING_CERTIFICATES);
|
||||
ApplicationInfo applicationInfo = packageInfo.applicationInfo;
|
||||
if (!appIsEligibleForBackup(applicationInfo, pm)
|
||||
|| appIsStopped(applicationInfo)
|
||||
@@ -165,12 +170,18 @@ public class AppBackupUtils {
|
||||
*
|
||||
* <ul>
|
||||
* <li>Source and target have at least one signature each
|
||||
* <li>Target contains all signatures in source
|
||||
* <li>Target contains all signatures in source, and nothing more
|
||||
* </ul>
|
||||
*
|
||||
* or if both source and target have exactly one signature, and they don't match, we check
|
||||
* if the app was ever signed with source signature (i.e. app has rotated key)
|
||||
* Note: key rotation is only supported for apps ever signed with one key, and those apps will
|
||||
* not be allowed to be signed by more certificates in the future
|
||||
*
|
||||
* Note that if {@param target} is null we return false.
|
||||
*/
|
||||
public static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
|
||||
public static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target,
|
||||
PackageManagerInternal pmi) {
|
||||
if (target == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -187,33 +198,52 @@ public class AppBackupUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
Signature[] deviceSigs = target.signatures;
|
||||
if (MORE_DEBUG) {
|
||||
Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs + " device=" + deviceSigs);
|
||||
}
|
||||
|
||||
// Don't allow unsigned apps on either end
|
||||
if (ArrayUtils.isEmpty(storedSigs) || ArrayUtils.isEmpty(deviceSigs)) {
|
||||
if (ArrayUtils.isEmpty(storedSigs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Signatures can be added over time, so the target-device apk needs to contain all the
|
||||
// source-device apk signatures, but not necessarily the other way around.
|
||||
int nStored = storedSigs.length;
|
||||
int nDevice = deviceSigs.length;
|
||||
Signature[][] deviceHistorySigs = target.signingCertificateHistory;
|
||||
if (ArrayUtils.isEmpty(deviceHistorySigs)) {
|
||||
Slog.w(TAG, "signingCertificateHistory is empty, app was either unsigned or the flag" +
|
||||
" PackageManager#GET_SIGNING_CERTIFICATES was not specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < nStored; i++) {
|
||||
boolean match = false;
|
||||
for (int j = 0; j < nDevice; j++) {
|
||||
if (storedSigs[i].equals(deviceSigs[j])) {
|
||||
match = true;
|
||||
break;
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs + " device=" + deviceHistorySigs);
|
||||
}
|
||||
|
||||
final int nStored = storedSigs.length;
|
||||
if (nStored == 1) {
|
||||
// if the app is only signed with one sig, it's possible it has rotated its key
|
||||
// (the checks with signing history are delegated to PackageManager)
|
||||
// TODO: address the case that app has declared restoreAnyVersion and is restoring
|
||||
// from higher version to lower after having rotated the key (i.e. higher version has
|
||||
// different sig than lower version that we want to restore to)
|
||||
return pmi.isDataRestoreSafe(storedSigs[0], target.packageName);
|
||||
} else {
|
||||
// the app couldn't have rotated keys, since it was signed with multiple sigs - do
|
||||
// a comprehensive 1-to-1 signatures check
|
||||
// since app hasn't rotated key, we only need to check with deviceHistorySigs[0]
|
||||
Signature[] deviceSigs = deviceHistorySigs[0];
|
||||
int nDevice = deviceSigs.length;
|
||||
|
||||
// ensure that each stored sig matches an on-device sig
|
||||
for (int i = 0; i < nStored; i++) {
|
||||
boolean match = false;
|
||||
for (int j = 0; j < nDevice; j++) {
|
||||
if (storedSigs[i].equals(deviceSigs[j])) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
return false;
|
||||
}
|
||||
// we have found a match for all stored sigs
|
||||
return true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import android.content.pm.PackageInstaller;
|
||||
import android.content.pm.PackageInstaller.Session;
|
||||
import android.content.pm.PackageInstaller.SessionParams;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.Bundle;
|
||||
import android.os.IBinder;
|
||||
@@ -37,6 +38,7 @@ import android.os.Process;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.backup.FileMetadata;
|
||||
import com.android.server.backup.restore.RestoreDeleteObserver;
|
||||
import com.android.server.backup.restore.RestorePolicy;
|
||||
@@ -142,9 +144,8 @@ public class RestoreUtils {
|
||||
uninstall = true;
|
||||
} else {
|
||||
try {
|
||||
PackageInfo pkg = packageManager.getPackageInfo(
|
||||
info.packageName,
|
||||
PackageManager.GET_SIGNATURES);
|
||||
PackageInfo pkg = packageManager.getPackageInfo(info.packageName,
|
||||
PackageManager.GET_SIGNING_CERTIFICATES);
|
||||
if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP)
|
||||
== 0) {
|
||||
Slog.w(TAG, "Restore stream contains apk of package "
|
||||
@@ -154,7 +155,9 @@ public class RestoreUtils {
|
||||
} else {
|
||||
// So far so good -- do the signatures match the manifest?
|
||||
Signature[] sigs = manifestSignatures.get(info.packageName);
|
||||
if (AppBackupUtils.signaturesMatch(sigs, pkg)) {
|
||||
PackageManagerInternal pmi = LocalServices.getService(
|
||||
PackageManagerInternal.class);
|
||||
if (AppBackupUtils.signaturesMatch(sigs, pkg, pmi)) {
|
||||
// If this is a system-uid app without a declared backup agent,
|
||||
// don't restore any of the file data.
|
||||
if ((pkg.applicationInfo.uid < Process.FIRST_APPLICATION_UID)
|
||||
|
||||
@@ -50,6 +50,7 @@ import android.app.backup.IBackupManagerMonitor;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.Bundle;
|
||||
import android.os.Process;
|
||||
@@ -385,7 +386,8 @@ public class TarBackupReader {
|
||||
* @return a restore policy constant.
|
||||
*/
|
||||
public RestorePolicy chooseRestorePolicy(PackageManager packageManager,
|
||||
boolean allowApks, FileMetadata info, Signature[] signatures) {
|
||||
boolean allowApks, FileMetadata info, Signature[] signatures,
|
||||
PackageManagerInternal pmi) {
|
||||
if (signatures == null) {
|
||||
return RestorePolicy.IGNORE;
|
||||
}
|
||||
@@ -395,7 +397,7 @@ public class TarBackupReader {
|
||||
// Okay, got the manifest info we need...
|
||||
try {
|
||||
PackageInfo pkgInfo = packageManager.getPackageInfo(
|
||||
info.packageName, PackageManager.GET_SIGNATURES);
|
||||
info.packageName, PackageManager.GET_SIGNING_CERTIFICATES);
|
||||
// Fall through to IGNORE if the app explicitly disallows backup
|
||||
final int flags = pkgInfo.applicationInfo.flags;
|
||||
if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
|
||||
@@ -411,7 +413,7 @@ public class TarBackupReader {
|
||||
// such packages are signed with the platform cert instead of
|
||||
// the app developer's cert, so they're different on every
|
||||
// device.
|
||||
if (AppBackupUtils.signaturesMatch(signatures, pkgInfo)) {
|
||||
if (AppBackupUtils.signaturesMatch(signatures, pkgInfo, pmi)) {
|
||||
if ((pkgInfo.applicationInfo.flags
|
||||
& ApplicationInfo.FLAG_RESTORE_ANY_VERSION) != 0) {
|
||||
Slog.i(TAG, "Package has restoreAnyVersion; taking data");
|
||||
|
||||
@@ -18,9 +18,12 @@ package com.android.server.backup;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.Signature;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
@@ -30,9 +33,10 @@ import java.util.List;
|
||||
public class BackupUtils {
|
||||
private static final String TAG = "BackupUtils";
|
||||
|
||||
private static final boolean DEBUG = false; // STOPSHIP if true
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
public static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target) {
|
||||
public static boolean signaturesMatch(ArrayList<byte[]> storedSigHashes, PackageInfo target,
|
||||
PackageManagerInternal pmi) {
|
||||
if (target == null) {
|
||||
return false;
|
||||
}
|
||||
@@ -47,48 +51,54 @@ public class BackupUtils {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Allow unsigned apps, but not signed on one device and unsigned on the other
|
||||
// !!! TODO: is this the right policy?
|
||||
Signature[] deviceSigs = target.signatures;
|
||||
if (DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes
|
||||
+ " device=" + deviceSigs);
|
||||
if ((storedSigHashes == null || storedSigHashes.size() == 0)
|
||||
&& (deviceSigs == null || deviceSigs.length == 0)) {
|
||||
return true;
|
||||
}
|
||||
if (storedSigHashes == null || deviceSigs == null) {
|
||||
// Don't allow unsigned apps on either end
|
||||
if (ArrayUtils.isEmpty(storedSigHashes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// !!! TODO: this demands that every stored signature match one
|
||||
// that is present on device, and does not demand the converse.
|
||||
// Is this this right policy?
|
||||
final int nStored = storedSigHashes.size();
|
||||
final int nDevice = deviceSigs.length;
|
||||
|
||||
// hash each on-device signature
|
||||
ArrayList<byte[]> deviceHashes = new ArrayList<byte[]>(nDevice);
|
||||
for (int i = 0; i < nDevice; i++) {
|
||||
deviceHashes.add(hashSignature(deviceSigs[i]));
|
||||
Signature[][] deviceHistorySigs = target.signingCertificateHistory;
|
||||
if (ArrayUtils.isEmpty(deviceHistorySigs)) {
|
||||
Slog.w(TAG, "signingCertificateHistory is empty, app was either unsigned or the flag" +
|
||||
" PackageManager#GET_SIGNING_CERTIFICATES was not specified");
|
||||
return false;
|
||||
}
|
||||
|
||||
// now ensure that each stored sig (hash) matches an on-device sig (hash)
|
||||
for (int n = 0; n < nStored; n++) {
|
||||
boolean match = false;
|
||||
final byte[] storedHash = storedSigHashes.get(n);
|
||||
for (int i = 0; i < nDevice; i++) {
|
||||
if (Arrays.equals(storedHash, deviceHashes.get(i))) {
|
||||
match = true;
|
||||
break;
|
||||
if (DEBUG) {
|
||||
Slog.v(TAG, "signaturesMatch(): stored=" + storedSigHashes
|
||||
+ " device=" + deviceHistorySigs);
|
||||
}
|
||||
|
||||
final int nStored = storedSigHashes.size();
|
||||
if (nStored == 1) {
|
||||
// if the app is only signed with one sig, it's possible it has rotated its key
|
||||
// the checks with signing history are delegated to PackageManager
|
||||
// TODO: address the case that app has declared restoreAnyVersion and is restoring
|
||||
// from higher version to lower after having rotated the key (i.e. higher version has
|
||||
// different sig than lower version that we want to restore to)
|
||||
return pmi.isDataRestoreSafe(storedSigHashes.get(0), target.packageName);
|
||||
} else {
|
||||
// the app couldn't have rotated keys, since it was signed with multiple sigs - do
|
||||
// a comprehensive 1-to-1 signatures check
|
||||
// since app hasn't rotated key, we only need to check with deviceHistorySigs[0]
|
||||
ArrayList<byte[]> deviceHashes = hashSignatureArray(deviceHistorySigs[0]);
|
||||
int nDevice = deviceHashes.size();
|
||||
|
||||
// ensure that each stored sig matches an on-device sig
|
||||
for (int i = 0; i < nStored; i++) {
|
||||
boolean match = false;
|
||||
for (int j = 0; j < nDevice; j++) {
|
||||
if (Arrays.equals(storedSigHashes.get(i), deviceHashes.get(j))) {
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!match) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// match is false when no on-device sig matched one of the stored ones
|
||||
if (!match) {
|
||||
return false;
|
||||
}
|
||||
// we have found a match for all stored sigs
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static byte[] hashSignature(byte[] signature) {
|
||||
|
||||
@@ -176,6 +176,7 @@ import android.content.pm.PackageParser.PackageLite;
|
||||
import android.content.pm.PackageParser.PackageParserException;
|
||||
import android.content.pm.PackageParser.ParseFlags;
|
||||
import android.content.pm.PackageParser.ServiceIntentInfo;
|
||||
import android.content.pm.PackageParser.SigningDetails;
|
||||
import android.content.pm.PackageParser.SigningDetails.SignatureSchemeVersion;
|
||||
import android.content.pm.PackageStats;
|
||||
import android.content.pm.PackageUserState;
|
||||
@@ -23319,6 +23320,39 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
|
||||
permName, packageName, flagMask, flagValues, userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataRestoreSafe(byte[] restoringFromSigHash, String packageName) {
|
||||
SigningDetails sd = getSigningDetails(packageName);
|
||||
if (sd == null) {
|
||||
return false;
|
||||
}
|
||||
return sd.hasSha256Certificate(restoringFromSigHash,
|
||||
SigningDetails.CertCapabilities.INSTALLED_DATA);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDataRestoreSafe(Signature restoringFromSig, String packageName) {
|
||||
SigningDetails sd = getSigningDetails(packageName);
|
||||
if (sd == null) {
|
||||
return false;
|
||||
}
|
||||
return sd.hasCertificate(restoringFromSig,
|
||||
SigningDetails.CertCapabilities.INSTALLED_DATA);
|
||||
}
|
||||
|
||||
private SigningDetails getSigningDetails(String packageName) {
|
||||
synchronized (mPackages) {
|
||||
if (packageName == null) {
|
||||
return null;
|
||||
}
|
||||
PackageParser.Package p = mPackages.get(packageName);
|
||||
if (p == null) {
|
||||
return null;
|
||||
}
|
||||
return p.mSigningDetails;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPermissionFlagsTEMP(String permName, String packageName, int userId) {
|
||||
return PackageManagerService.this.getPermissionFlags(permName, packageName, userId);
|
||||
|
||||
@@ -18,10 +18,12 @@ package com.android.server.pm;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.UserIdInt;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.util.Slog;
|
||||
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.server.LocalServices;
|
||||
import com.android.server.backup.BackupUtils;
|
||||
|
||||
import libcore.util.HexEncoding;
|
||||
@@ -136,7 +138,8 @@ class ShortcutPackageInfo {
|
||||
|
||||
//@DisabledReason
|
||||
public int canRestoreTo(ShortcutService s, PackageInfo currentPackage, boolean anyVersionOkay) {
|
||||
if (!BackupUtils.signaturesMatch(mSigHashes, currentPackage)) {
|
||||
PackageManagerInternal pmi = LocalServices.getService(PackageManagerInternal.class);
|
||||
if (!BackupUtils.signaturesMatch(mSigHashes, currentPackage, pmi)) {
|
||||
Slog.w(TAG, "Can't restore: Package signature mismatch");
|
||||
return ShortcutInfo.DISABLED_REASON_SIGNATURE_MISMATCH;
|
||||
}
|
||||
|
||||
@@ -3125,7 +3125,8 @@ public class ShortcutService extends IShortcutService.Stub {
|
||||
try {
|
||||
return mIPackageManager.getPackageInfo(
|
||||
packageName, PACKAGE_MATCH_FLAGS
|
||||
| (getSignatures ? PackageManager.GET_SIGNATURES : 0), userId);
|
||||
| (getSignatures ? PackageManager.GET_SIGNING_CERTIFICATES : 0),
|
||||
userId);
|
||||
} catch (RemoteException e) {
|
||||
// Shouldn't happen.
|
||||
Slog.wtf(TAG, "RemoteException", e);
|
||||
|
||||
@@ -18,9 +18,14 @@ package com.android.server.backup.utils;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.Process;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
@@ -30,6 +35,7 @@ import android.support.test.runner.AndroidJUnit4;
|
||||
import com.android.server.backup.BackupManagerService;
|
||||
import com.android.server.backup.testutils.PackageManagerStub;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -45,7 +51,14 @@ public class AppBackupUtilsTest {
|
||||
private static final Signature SIGNATURE_3 = generateSignature((byte) 3);
|
||||
private static final Signature SIGNATURE_4 = generateSignature((byte) 4);
|
||||
|
||||
private final PackageManagerStub mPackageManagerStub = new PackageManagerStub();
|
||||
private PackageManagerStub mPackageManagerStub;
|
||||
private PackageManagerInternal mMockPackageManagerInternal;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mPackageManagerStub = new PackageManagerStub();
|
||||
mMockPackageManagerInternal = mock(PackageManagerInternal.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void appIsEligibleForBackup_backupNotAllowed_returnsFalse() throws Exception {
|
||||
@@ -358,7 +371,8 @@ public class AppBackupUtilsTest {
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_targetIsNull_returnsFalse() throws Exception {
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, null);
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, null,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
@@ -369,7 +383,8 @@ public class AppBackupUtilsTest {
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo);
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
@@ -378,10 +393,11 @@ public class AppBackupUtilsTest {
|
||||
public void signaturesMatch_disallowsUnsignedApps_storedSignatureNull_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = new Signature[] {SIGNATURE_1};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(null, packageInfo);
|
||||
boolean result = AppBackupUtils.signaturesMatch(null, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
@@ -390,10 +406,11 @@ public class AppBackupUtilsTest {
|
||||
public void signaturesMatch_disallowsUnsignedApps_storedSignatureEmpty_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = new Signature[] {SIGNATURE_1};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo);
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
@@ -404,11 +421,11 @@ public class AppBackupUtilsTest {
|
||||
signaturesMatch_disallowsUnsignedApps_targetSignatureEmpty_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = new Signature[0];
|
||||
packageInfo.signingCertificateHistory = new Signature[0][0];
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1},
|
||||
packageInfo);
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
@@ -418,11 +435,11 @@ public class AppBackupUtilsTest {
|
||||
signaturesMatch_disallowsUnsignedApps_targetSignatureNull_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = null;
|
||||
packageInfo.signingCertificateHistory = null;
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1},
|
||||
packageInfo);
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
@@ -431,10 +448,11 @@ public class AppBackupUtilsTest {
|
||||
public void signaturesMatch_disallowsUnsignedApps_bothSignaturesNull_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = null;
|
||||
packageInfo.signingCertificateHistory = null;
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(null, packageInfo);
|
||||
boolean result = AppBackupUtils.signaturesMatch(null, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
@@ -443,10 +461,11 @@ public class AppBackupUtilsTest {
|
||||
public void signaturesMatch_disallowsUnsignedApps_bothSignaturesEmpty_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = new Signature[0];
|
||||
packageInfo.signingCertificateHistory = new Signature[0][0];
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo);
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
@@ -458,11 +477,14 @@ public class AppBackupUtilsTest {
|
||||
Signature signature3Copy = new Signature(SIGNATURE_3.toByteArray());
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {
|
||||
{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
|
||||
};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(
|
||||
new Signature[]{signature3Copy, signature1Copy, signature2Copy}, packageInfo);
|
||||
new Signature[] {signature3Copy, signature1Copy, signature2Copy}, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
@@ -473,11 +495,14 @@ public class AppBackupUtilsTest {
|
||||
Signature signature2Copy = new Signature(SIGNATURE_2.toByteArray());
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {
|
||||
{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
|
||||
};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(
|
||||
new Signature[]{signature2Copy, signature1Copy}, packageInfo);
|
||||
new Signature[]{signature2Copy, signature1Copy}, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
@@ -488,11 +513,14 @@ public class AppBackupUtilsTest {
|
||||
Signature signature2Copy = new Signature(SIGNATURE_2.toByteArray());
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = new Signature[]{signature1Copy, signature2Copy};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {
|
||||
{signature1Copy, signature2Copy}
|
||||
};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(
|
||||
new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}, packageInfo);
|
||||
new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
@@ -503,11 +531,76 @@ public class AppBackupUtilsTest {
|
||||
Signature signature2Copy = new Signature(SIGNATURE_2.toByteArray());
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signatures = new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {
|
||||
{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
|
||||
};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(
|
||||
new Signature[]{signature1Copy, signature2Copy, SIGNATURE_4}, packageInfo);
|
||||
new Signature[]{signature1Copy, signature2Copy, SIGNATURE_4}, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_singleStoredSignatureNoRotation_returnsTrue()
|
||||
throws Exception {
|
||||
Signature signature1Copy = new Signature(SIGNATURE_1.toByteArray());
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(signature1Copy,
|
||||
packageInfo.packageName);
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {signature1Copy},
|
||||
packageInfo, mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_singleStoredSignatureWithRotationAssumeDataCapability_returnsTrue()
|
||||
throws Exception {
|
||||
Signature signature1Copy = new Signature(SIGNATURE_1.toByteArray());
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
// we know signature1Copy is in history, and we want to assume it has
|
||||
// SigningDetails.CertCapabilities.INSTALLED_DATA capability
|
||||
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(signature1Copy,
|
||||
packageInfo.packageName);
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {signature1Copy},
|
||||
packageInfo, mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
signaturesMatch_singleStoredSignatureWithRotationAssumeNoDataCapability_returnsFalse()
|
||||
throws Exception {
|
||||
Signature signature1Copy = new Signature(SIGNATURE_1.toByteArray());
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
// we know signature1Copy is in history, but we want to assume it does not have
|
||||
// SigningDetails.CertCapabilities.INSTALLED_DATA capability
|
||||
doReturn(false).when(mMockPackageManagerInternal).isDataRestoreSafe(signature1Copy,
|
||||
packageInfo.packageName);
|
||||
|
||||
boolean result = AppBackupUtils.signaturesMatch(new Signature[] {signature1Copy},
|
||||
packageInfo, mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@@ -28,6 +28,9 @@ import static android.app.backup.BackupManagerMonitor.LOG_EVENT_ID_VERSION_OF_BA
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoMoreInteractions;
|
||||
import static org.mockito.Mockito.verifyZeroInteractions;
|
||||
@@ -37,6 +40,7 @@ import android.app.backup.IBackupManagerMonitor;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.Signature;
|
||||
import android.os.Bundle;
|
||||
import android.os.Process;
|
||||
@@ -79,6 +83,7 @@ public class TarBackupReaderTest {
|
||||
|
||||
@Mock private BytesReadListener mBytesReadListenerMock;
|
||||
@Mock private IBackupManagerMonitor mBackupManagerMonitorMock;
|
||||
@Mock private PackageManagerInternal mMockPackageManagerInternal;
|
||||
|
||||
private final PackageManagerStub mPackageManagerStub = new PackageManagerStub();
|
||||
private Context mContext;
|
||||
@@ -139,7 +144,8 @@ public class TarBackupReaderTest {
|
||||
Signature[] signatures = tarBackupReader.readAppManifestAndReturnSignatures(
|
||||
fileMetadata);
|
||||
RestorePolicy restorePolicy = tarBackupReader.chooseRestorePolicy(
|
||||
mPackageManagerStub, false /* allowApks */, fileMetadata, signatures);
|
||||
mPackageManagerStub, false /* allowApks */, fileMetadata, signatures,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(restorePolicy).isEqualTo(RestorePolicy.IGNORE);
|
||||
assertThat(fileMetadata.packageName).isEqualTo(TEST_PACKAGE_NAME);
|
||||
@@ -152,7 +158,8 @@ public class TarBackupReaderTest {
|
||||
signatures = tarBackupReader.readAppManifestAndReturnSignatures(
|
||||
fileMetadata);
|
||||
restorePolicy = tarBackupReader.chooseRestorePolicy(
|
||||
mPackageManagerStub, false /* allowApks */, fileMetadata, signatures);
|
||||
mPackageManagerStub, false /* allowApks */, fileMetadata, signatures,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(restorePolicy).isEqualTo(RestorePolicy.IGNORE);
|
||||
assertThat(fileMetadata.packageName).isEqualTo(TEST_PACKAGE_NAME);
|
||||
@@ -214,7 +221,8 @@ public class TarBackupReaderTest {
|
||||
mBytesReadListenerMock, mBackupManagerMonitorMock);
|
||||
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
true /* allowApks */, new FileMetadata(), null /* signatures */);
|
||||
true /* allowApks */, new FileMetadata(), null /* signatures */,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
|
||||
verifyZeroInteractions(mBackupManagerMonitorMock);
|
||||
@@ -234,7 +242,8 @@ public class TarBackupReaderTest {
|
||||
PackageManagerStub.sPackageInfo = null;
|
||||
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
true /* allowApks */, info, new Signature[0] /* signatures */);
|
||||
true /* allowApks */, info, new Signature[0] /* signatures */,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT_IF_APK);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
@@ -258,7 +267,8 @@ public class TarBackupReaderTest {
|
||||
PackageManagerStub.sPackageInfo = null;
|
||||
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
true /* allowApks */, info, new Signature[0] /* signatures */);
|
||||
true /* allowApks */, info, new Signature[0] /* signatures */,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT_IF_APK);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
@@ -283,7 +293,8 @@ public class TarBackupReaderTest {
|
||||
PackageManagerStub.sPackageInfo = null;
|
||||
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */);
|
||||
false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
@@ -307,7 +318,8 @@ public class TarBackupReaderTest {
|
||||
PackageManagerStub.sPackageInfo = packageInfo;
|
||||
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */);
|
||||
false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
@@ -333,7 +345,8 @@ public class TarBackupReaderTest {
|
||||
PackageManagerStub.sPackageInfo = packageInfo;
|
||||
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */);
|
||||
false /* allowApks */, new FileMetadata(), new Signature[0] /* signatures */,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
@@ -358,11 +371,11 @@ public class TarBackupReaderTest {
|
||||
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
|
||||
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
|
||||
packageInfo.applicationInfo.backupAgentName = null;
|
||||
packageInfo.signatures = new Signature[]{FAKE_SIGNATURE_2};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_2}};
|
||||
PackageManagerStub.sPackageInfo = packageInfo;
|
||||
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
false /* allowApks */, new FileMetadata(), signatures);
|
||||
false /* allowApks */, new FileMetadata(), signatures, mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
@@ -383,16 +396,19 @@ public class TarBackupReaderTest {
|
||||
Signature[] signatures = new Signature[]{FAKE_SIGNATURE_1};
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
packageInfo.applicationInfo.flags |=
|
||||
ApplicationInfo.FLAG_ALLOW_BACKUP | ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
|
||||
packageInfo.applicationInfo.uid = Process.SYSTEM_UID;
|
||||
packageInfo.applicationInfo.backupAgentName = "backup.agent";
|
||||
packageInfo.signatures = new Signature[]{FAKE_SIGNATURE_1};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
|
||||
PackageManagerStub.sPackageInfo = packageInfo;
|
||||
|
||||
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
|
||||
packageInfo.packageName);
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
false /* allowApks */, new FileMetadata(), signatures);
|
||||
false /* allowApks */, new FileMetadata(), signatures, mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
@@ -412,16 +428,19 @@ public class TarBackupReaderTest {
|
||||
Signature[] signatures = new Signature[]{FAKE_SIGNATURE_1};
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
packageInfo.applicationInfo.flags |=
|
||||
ApplicationInfo.FLAG_ALLOW_BACKUP | ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
|
||||
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
|
||||
packageInfo.applicationInfo.backupAgentName = null;
|
||||
packageInfo.signatures = new Signature[]{FAKE_SIGNATURE_1};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
|
||||
PackageManagerStub.sPackageInfo = packageInfo;
|
||||
|
||||
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
|
||||
packageInfo.packageName);
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
false /* allowApks */, new FileMetadata(), signatures);
|
||||
false /* allowApks */, new FileMetadata(), signatures, mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
@@ -444,17 +463,20 @@ public class TarBackupReaderTest {
|
||||
info.version = 1;
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
|
||||
packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
|
||||
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
|
||||
packageInfo.applicationInfo.backupAgentName = null;
|
||||
packageInfo.signatures = new Signature[]{FAKE_SIGNATURE_1};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
|
||||
packageInfo.versionCode = 2;
|
||||
PackageManagerStub.sPackageInfo = packageInfo;
|
||||
|
||||
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
|
||||
packageInfo.packageName);
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
false /* allowApks */, info, signatures);
|
||||
false /* allowApks */, info, signatures, mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
@@ -479,17 +501,20 @@ public class TarBackupReaderTest {
|
||||
info.hasApk = true;
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
|
||||
packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
|
||||
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
|
||||
packageInfo.applicationInfo.backupAgentName = null;
|
||||
packageInfo.signatures = new Signature[]{FAKE_SIGNATURE_1};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
|
||||
packageInfo.versionCode = 1;
|
||||
PackageManagerStub.sPackageInfo = packageInfo;
|
||||
|
||||
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
|
||||
packageInfo.packageName);
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
true /* allowApks */, info, signatures);
|
||||
true /* allowApks */, info, signatures, mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.ACCEPT_IF_APK);
|
||||
verifyNoMoreInteractions(mBackupManagerMonitorMock);
|
||||
@@ -510,17 +535,20 @@ public class TarBackupReaderTest {
|
||||
info.version = 2;
|
||||
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_ALLOW_BACKUP;
|
||||
packageInfo.applicationInfo.flags &= ~ApplicationInfo.FLAG_RESTORE_ANY_VERSION;
|
||||
packageInfo.applicationInfo.uid = Process.FIRST_APPLICATION_UID;
|
||||
packageInfo.applicationInfo.backupAgentName = null;
|
||||
packageInfo.signatures = new Signature[]{FAKE_SIGNATURE_1};
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{FAKE_SIGNATURE_1}};
|
||||
packageInfo.versionCode = 1;
|
||||
PackageManagerStub.sPackageInfo = packageInfo;
|
||||
|
||||
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(FAKE_SIGNATURE_1,
|
||||
packageInfo.packageName);
|
||||
RestorePolicy policy = tarBackupReader.chooseRestorePolicy(mPackageManagerStub,
|
||||
false /* allowApks */, info, signatures);
|
||||
false /* allowApks */, info, signatures, mMockPackageManagerInternal);
|
||||
|
||||
assertThat(policy).isEqualTo(RestorePolicy.IGNORE);
|
||||
ArgumentCaptor<Bundle> bundleCaptor = ArgumentCaptor.forClass(Bundle.class);
|
||||
|
||||
@@ -15,73 +15,298 @@
|
||||
*/
|
||||
package com.android.server.pm.backup;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManagerInternal;
|
||||
import android.content.pm.PackageParser.Package;
|
||||
import android.content.pm.Signature;
|
||||
import android.test.AndroidTestCase;
|
||||
import android.test.MoreAsserts;
|
||||
import android.test.suitebuilder.annotation.SmallTest;
|
||||
import android.platform.test.annotations.Presubmit;
|
||||
import android.support.test.filters.SmallTest;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.server.backup.BackupUtils;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
@SmallTest
|
||||
public class BackupUtilsTest extends AndroidTestCase {
|
||||
@Presubmit
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class BackupUtilsTest {
|
||||
|
||||
private Signature[] genSignatures(String... signatures) {
|
||||
final Signature[] sigs = new Signature[signatures.length];
|
||||
for (int i = 0; i < signatures.length; i++){
|
||||
sigs[i] = new Signature(signatures[i].getBytes());
|
||||
}
|
||||
return sigs;
|
||||
private static final Signature SIGNATURE_1 = generateSignature((byte) 1);
|
||||
private static final Signature SIGNATURE_2 = generateSignature((byte) 2);
|
||||
private static final Signature SIGNATURE_3 = generateSignature((byte) 3);
|
||||
private static final Signature SIGNATURE_4 = generateSignature((byte) 4);
|
||||
private static final byte[] SIGNATURE_HASH_1 = BackupUtils.hashSignature(SIGNATURE_1);
|
||||
private static final byte[] SIGNATURE_HASH_2 = BackupUtils.hashSignature(SIGNATURE_2);
|
||||
private static final byte[] SIGNATURE_HASH_3 = BackupUtils.hashSignature(SIGNATURE_3);
|
||||
private static final byte[] SIGNATURE_HASH_4 = BackupUtils.hashSignature(SIGNATURE_4);
|
||||
|
||||
private PackageManagerInternal mMockPackageManagerInternal;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mMockPackageManagerInternal = mock(PackageManagerInternal.class);
|
||||
}
|
||||
|
||||
private PackageInfo genPackage(String... signatures) {
|
||||
final PackageInfo pi = new PackageInfo();
|
||||
pi.packageName = "package";
|
||||
pi.applicationInfo = new ApplicationInfo();
|
||||
pi.signatures = genSignatures(signatures);
|
||||
@Test
|
||||
public void signaturesMatch_targetIsNull_returnsFalse() throws Exception {
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, null,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
return pi;
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
public void testSignaturesMatch() {
|
||||
final ArrayList<byte[]> stored1 = BackupUtils.hashSignatureArray(Arrays.asList(
|
||||
"abc".getBytes()));
|
||||
final ArrayList<byte[]> stored2 = BackupUtils.hashSignatureArray(Arrays.asList(
|
||||
"abc".getBytes(), "def".getBytes()));
|
||||
@Test
|
||||
public void signaturesMatch_systemApplication_returnsTrue() throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
packageInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
|
||||
|
||||
PackageInfo pi;
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
// False for null package.
|
||||
assertFalse(BackupUtils.signaturesMatch(stored1, null));
|
||||
|
||||
// If it's a system app, signatures don't matter.
|
||||
pi = genPackage("xyz");
|
||||
pi.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
|
||||
assertTrue(BackupUtils.signaturesMatch(stored1, pi));
|
||||
|
||||
// Non system apps.
|
||||
assertTrue(BackupUtils.signaturesMatch(stored1, genPackage("abc")));
|
||||
|
||||
// Superset is okay.
|
||||
assertTrue(BackupUtils.signaturesMatch(stored1, genPackage("abc", "xyz")));
|
||||
assertTrue(BackupUtils.signaturesMatch(stored1, genPackage("xyz", "abc")));
|
||||
|
||||
assertFalse(BackupUtils.signaturesMatch(stored1, genPackage("xyz")));
|
||||
assertFalse(BackupUtils.signaturesMatch(stored1, genPackage("xyz", "def")));
|
||||
|
||||
assertTrue(BackupUtils.signaturesMatch(stored2, genPackage("def", "abc")));
|
||||
assertTrue(BackupUtils.signaturesMatch(stored2, genPackage("x", "def", "abc", "y")));
|
||||
|
||||
// Subset is not okay.
|
||||
assertFalse(BackupUtils.signaturesMatch(stored2, genPackage("abc")));
|
||||
assertFalse(BackupUtils.signaturesMatch(stored2, genPackage("def")));
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_disallowsUnsignedApps_storedSignatureNull_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = BackupUtils.signaturesMatch(null, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_disallowsUnsignedApps_storedSignatureEmpty_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void
|
||||
signaturesMatch_disallowsUnsignedApps_targetSignatureEmpty_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = new Signature[0][0];
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
signaturesMatch_disallowsUnsignedApps_targetSignatureNull_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = null;
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_disallowsUnsignedApps_bothSignaturesNull_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = null;
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
boolean result = BackupUtils.signaturesMatch(null, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_disallowsUnsignedApps_bothSignaturesEmpty_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = new Signature[0][0];
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_equalSignatures_returnsTrue() throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {
|
||||
{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
|
||||
};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
storedSigHashes.add(SIGNATURE_HASH_2);
|
||||
storedSigHashes.add(SIGNATURE_HASH_3);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_extraSignatureInTarget_returnsTrue() throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {
|
||||
{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
|
||||
};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
storedSigHashes.add(SIGNATURE_HASH_2);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_extraSignatureInStored_returnsFalse() throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1, SIGNATURE_2}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
storedSigHashes.add(SIGNATURE_HASH_2);
|
||||
storedSigHashes.add(SIGNATURE_HASH_3);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_oneNonMatchingSignature_returnsFalse() throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {
|
||||
{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}
|
||||
};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
storedSigHashes.add(SIGNATURE_HASH_2);
|
||||
storedSigHashes.add(SIGNATURE_HASH_4);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_singleStoredSignatureNoRotation_returnsTrue()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(SIGNATURE_HASH_1,
|
||||
packageInfo.packageName);
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signaturesMatch_singleStoredSignatureWithRotationAssumeDataCapability_returnsTrue()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
// we know SIGNATURE_1 is in history, and we want to assume it has
|
||||
// SigningDetails.CertCapabilities.INSTALLED_DATA capability
|
||||
doReturn(true).when(mMockPackageManagerInternal).isDataRestoreSafe(SIGNATURE_HASH_1,
|
||||
packageInfo.packageName);
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void
|
||||
signaturesMatch_singleStoredSignatureWithRotationAssumeNoDataCapability_returnsFalse()
|
||||
throws Exception {
|
||||
PackageInfo packageInfo = new PackageInfo();
|
||||
packageInfo.packageName = "test";
|
||||
packageInfo.signingCertificateHistory = new Signature[][] {{SIGNATURE_1}, {SIGNATURE_2}};
|
||||
packageInfo.applicationInfo = new ApplicationInfo();
|
||||
|
||||
// we know SIGNATURE_1 is in history, but we want to assume it does not have
|
||||
// SigningDetails.CertCapabilities.INSTALLED_DATA capability
|
||||
doReturn(false).when(mMockPackageManagerInternal).isDataRestoreSafe(SIGNATURE_HASH_1,
|
||||
packageInfo.packageName);
|
||||
|
||||
ArrayList<byte[]> storedSigHashes = new ArrayList<>();
|
||||
storedSigHashes.add(SIGNATURE_HASH_1);
|
||||
boolean result = BackupUtils.signaturesMatch(storedSigHashes, packageInfo,
|
||||
mMockPackageManagerInternal);
|
||||
|
||||
assertThat(result).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHashSignature() {
|
||||
final byte[] sig1 = "abc".getBytes();
|
||||
final byte[] sig2 = "def".getBytes();
|
||||
@@ -115,4 +340,10 @@ public class BackupUtilsTest extends AndroidTestCase {
|
||||
MoreAsserts.assertEquals(hash2a, listA.get(1));
|
||||
MoreAsserts.assertEquals(hash2a, listB.get(1));
|
||||
}
|
||||
|
||||
private static Signature generateSignature(byte i) {
|
||||
byte[] signatureBytes = new byte[256];
|
||||
signatureBytes[0] = i;
|
||||
return new Signature(signatureBytes);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user