Respect android:allowBackup="false" during full backup & restore

Packages with this manifest attribute set 'false' will not be backed
up even through the "full device backup" infrastructure.  If someone
produces an apparent restore file with data for such an application,
it will not actually be restored onto the device.

When an apk is installed during the course of a restore operation,
it is validated against the manifest contents and deleted if there
is a mismatch.  Also, if the newly-installed app is found to
disallow backups, no file content will be processed for that app.

Bug 4532159

Change-Id: I59630054584b1394e567de939192e22e597044ee
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 cd58b9b..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