Merge "Respect android:allowBackup="false" during full backup & restore"
diff --git a/packages/BackupRestoreConfirmation/AndroidManifest.xml b/packages/BackupRestoreConfirmation/AndroidManifest.xml
index ed9a519..19848f6 100644
--- a/packages/BackupRestoreConfirmation/AndroidManifest.xml
+++ b/packages/BackupRestoreConfirmation/AndroidManifest.xml
@@ -21,7 +21,7 @@
<uses-permission android:name="android.permission.BACKUP" />
<application android:allowClearUserData="false"
- android:killAfterRestore="false"
+ android:allowBackup="false"
android:permission="android.permission.CONFIRM_FULL_BACKUP" >
<activity android:name=".BackupRestoreConfirmation"
diff --git a/services/java/com/android/server/BackupManagerService.java b/services/java/com/android/server/BackupManagerService.java
index cd58b9b9..7c6d3c1 100644
--- a/services/java/com/android/server/BackupManagerService.java
+++ b/services/java/com/android/server/BackupManagerService.java
@@ -39,6 +39,7 @@
import android.content.ServiceConnection;
import android.content.pm.ApplicationInfo;
import android.content.pm.IPackageDataObserver;
+import android.content.pm.IPackageDeleteObserver;
import android.content.pm.IPackageInstallObserver;
import android.content.pm.IPackageManager;
import android.content.pm.PackageInfo;
@@ -1709,6 +1710,16 @@
}
}
+ // Cull any packages that have indicated that backups are not permitted.
+ for (int i = 0; i < packagesToBackup.size(); ) {
+ PackageInfo info = packagesToBackup.get(i);
+ if ((info.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
+ packagesToBackup.remove(i);
+ } else {
+ i++;
+ }
+ }
+
// Now back up the app data via the agent mechanism
PackageInfo pkg = null;
try {
@@ -1937,7 +1948,6 @@
// Which packages we've already wiped data on. We prepopulate this
// with a whitelist of packages known to be unclearable.
mClearedPackages.add("android");
- mClearedPackages.add("com.android.backupconfirm");
mClearedPackages.add("com.android.providers.settings");
}
@@ -2314,6 +2324,7 @@
class RestoreInstallObserver extends IPackageInstallObserver.Stub {
final AtomicBoolean mDone = new AtomicBoolean();
+ String mPackageName;
int mResult;
public void reset() {
@@ -2341,12 +2352,45 @@
throws RemoteException {
synchronized (mDone) {
mResult = returnCode;
+ mPackageName = packageName;
mDone.set(true);
mDone.notifyAll();
}
}
}
+
+ class RestoreDeleteObserver extends IPackageDeleteObserver.Stub {
+ final AtomicBoolean mDone = new AtomicBoolean();
+ int mResult;
+
+ public void reset() {
+ synchronized (mDone) {
+ mDone.set(false);
+ }
+ }
+
+ public void waitForCompletion() {
+ synchronized (mDone) {
+ while (mDone.get() == false) {
+ try {
+ mDone.wait();
+ } catch (InterruptedException e) { }
+ }
+ }
+ }
+
+ @Override
+ public void packageDeleted(String packageName, int returnCode) throws RemoteException {
+ synchronized (mDone) {
+ mResult = returnCode;
+ mDone.set(true);
+ mDone.notifyAll();
+ }
+ }
+ }
+
final RestoreInstallObserver mInstallObserver = new RestoreInstallObserver();
+ final RestoreDeleteObserver mDeleteObserver = new RestoreDeleteObserver();
boolean installApk(FileMetadata info, String installerPackage, InputStream instream) {
boolean okay = true;
@@ -2385,6 +2429,49 @@
if (mPackagePolicies.get(info.packageName) != RestorePolicy.ACCEPT) {
okay = false;
}
+ } else {
+ // Okay, the install succeeded. Make sure it was the right app.
+ boolean uninstall = false;
+ if (!mInstallObserver.mPackageName.equals(info.packageName)) {
+ Slog.w(TAG, "Restore stream claimed to include apk for "
+ + info.packageName + " but apk was really "
+ + mInstallObserver.mPackageName);
+ // delete the package we just put in place; it might be fraudulent
+ okay = false;
+ uninstall = true;
+ } else {
+ try {
+ PackageInfo pkg = mPackageManager.getPackageInfo(info.packageName,
+ PackageManager.GET_SIGNATURES);
+ if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_ALLOW_BACKUP) == 0) {
+ Slog.w(TAG, "Restore stream contains apk of package "
+ + info.packageName + " but it disallows backup/restore");
+ okay = false;
+ } else {
+ // So far so good -- do the signatures match the manifest?
+ Signature[] sigs = mManifestSignatures.get(info.packageName);
+ if (!signaturesMatch(sigs, pkg)) {
+ Slog.w(TAG, "Installed app " + info.packageName
+ + " signatures do not match restore manifest");
+ okay = false;
+ uninstall = true;
+ }
+ }
+ } catch (NameNotFoundException e) {
+ Slog.w(TAG, "Install of package " + info.packageName
+ + " succeeded but now not found");
+ okay = false;
+ }
+ }
+
+ // If we're not okay at this point, we need to delete the package
+ // that we just installed.
+ if (uninstall) {
+ mDeleteObserver.reset();
+ mPackageManager.deletePackage(mInstallObserver.mPackageName,
+ mDeleteObserver, 0);
+ mDeleteObserver.waitForCompletion();
+ }
}
} catch (IOException e) {
Slog.e(TAG, "Unable to transcribe restored apk for install");
@@ -2441,38 +2528,48 @@
boolean hasApk = str[0].equals("1");
offset = extractLine(buffer, offset, str);
int numSigs = Integer.parseInt(str[0]);
- Signature[] sigs = null;
if (numSigs > 0) {
- sigs = new Signature[numSigs];
+ Signature[] sigs = new Signature[numSigs];
for (int i = 0; i < numSigs; i++) {
offset = extractLine(buffer, offset, str);
sigs[i] = new Signature(str[0]);
}
+ mManifestSignatures.put(info.packageName, sigs);
// Okay, got the manifest info we need...
try {
- // Verify signatures against any installed version; if they
- // don't match, then we fall though and ignore the data. The
- // signatureMatch() method explicitly ignores the signature
- // check for packages installed on the system partition, because
- // such packages are signed with the platform cert instead of
- // the app developer's cert, so they're different on every
- // device.
PackageInfo pkgInfo = mPackageManager.getPackageInfo(
info.packageName, PackageManager.GET_SIGNATURES);
- if (signaturesMatch(sigs, pkgInfo)) {
- if (pkgInfo.versionCode >= version) {
- Slog.i(TAG, "Sig + version match; taking data");
- policy = RestorePolicy.ACCEPT;
+ // Fall through to IGNORE if the app explicitly disallows backup
+ final int flags = pkgInfo.applicationInfo.flags;
+ if ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0) {
+ // Verify signatures against any installed version; if they
+ // don't match, then we fall though and ignore the data. The
+ // signatureMatch() method explicitly ignores the signature
+ // check for packages installed on the system partition, because
+ // such packages are signed with the platform cert instead of
+ // the app developer's cert, so they're different on every
+ // device.
+ if (signaturesMatch(sigs, pkgInfo)) {
+ if (pkgInfo.versionCode >= version) {
+ Slog.i(TAG, "Sig + version match; taking data");
+ policy = RestorePolicy.ACCEPT;
+ } else {
+ // The data is from a newer version of the app than
+ // is presently installed. That means we can only
+ // use it if the matching apk is also supplied.
+ Slog.d(TAG, "Data version " + version
+ + " is newer than installed version "
+ + pkgInfo.versionCode + " - requiring apk");
+ policy = RestorePolicy.ACCEPT_IF_APK;
+ }
} else {
- // The data is from a newer version of the app than
- // is presently installed. That means we can only
- // use it if the matching apk is also supplied.
- Slog.d(TAG, "Data version " + version
- + " is newer than installed version "
- + pkgInfo.versionCode + " - requiring apk");
- policy = RestorePolicy.ACCEPT_IF_APK;
+ Slog.w(TAG, "Restore manifest signatures do not match "
+ + "installed application for " + info.packageName);
}
+ } else {
+ if (DEBUG) Slog.i(TAG, "Restore manifest from "
+ + info.packageName + " but allowBackup=false");
}
} catch (NameNotFoundException e) {
// Okay, the target app isn't installed. We can process