Merge "Merge "Solve AppBackupUtils.signaturesMatch() policies" into oc-mr1-dev am: a743c01d00" into oc-mr1-dev-plus-aosp
am: 13889d75c7

Change-Id: Ifd1afbf731cfb10d2e37d86d0c5b1d64011eb9d6
diff --git a/services/backup/java/com/android/server/backup/BackupManagerService.java b/services/backup/java/com/android/server/backup/BackupManagerService.java
index cb4becc..29d562b 100644
--- a/services/backup/java/com/android/server/backup/BackupManagerService.java
+++ b/services/backup/java/com/android/server/backup/BackupManagerService.java
@@ -119,6 +119,7 @@
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.backup.IBackupTransport;
 import com.android.internal.backup.IObbBackupService;
+import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.DumpUtils;
 import com.android.server.AppWidgetBackupBridge;
 import com.android.server.EventLogTags;
@@ -8416,7 +8417,19 @@
 
     // ----- Restore handling -----
 
-    // Old style: directly match the stored vs on device signature blocks
+    /**
+     * Returns whether the signatures stored {@param storedSigs}, coming from the source apk, match
+     * the signatures of the apk installed on the device, the target apk. If the target resides in
+     * the system partition we return true. Otherwise it's considered a match if both conditions
+     * hold:
+     *
+     * <ul>
+     *   <li>Source and target have at least one signature each
+     *   <li>Target contains all signatures in source
+     * </ul>
+     *
+     * Note that if {@param target} is null we return false.
+     */
     static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
         if (target == null) {
             return false;
@@ -8428,26 +8441,24 @@
         // partition will be signed with the device's platform certificate, so on
         // different phones the same system app will have different signatures.)
         if ((target.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
-            if (MORE_DEBUG) Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
+            if (MORE_DEBUG) {
+                Slog.v(TAG, "System app " + target.packageName + " - skipping sig check");
+            }
             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 (MORE_DEBUG) Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs
-                + " device=" + deviceSigs);
-        if ((storedSigs == null || storedSigs.length == 0)
-                && (deviceSigs == null || deviceSigs.length == 0)) {
-            return true;
+        if (MORE_DEBUG) {
+            Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs + " device=" + deviceSigs);
         }
-        if (storedSigs == null || deviceSigs == null) {
+
+        // Don't allow unsigned apps on either end
+        if (ArrayUtils.isEmpty(storedSigs) || ArrayUtils.isEmpty(deviceSigs)) {
             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?
+        // 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;
 
diff --git a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
index c033d98..4abf18a 100644
--- a/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
+++ b/services/backup/java/com/android/server/backup/utils/AppBackupUtils.java
@@ -26,6 +26,8 @@
 import android.os.Process;
 import android.util.Slog;
 
+import com.android.internal.util.ArrayUtils;
+
 /**
  * Utility methods wrapping operations on ApplicationInfo and PackageInfo.
  */
@@ -91,9 +93,18 @@
     }
 
     /**
-     * Old style: directly match the stored vs on device signature blocks.
+     * Returns whether the signatures stored {@param storedSigs}, coming from the source apk, match
+     * the signatures of the apk installed on the device, the target apk. If the target resides in
+     * the system partition we return true. Otherwise it's considered a match if both conditions
+     * hold:
+     *
+     * <ul>
+     *   <li>Source and target have at least one signature each
+     *   <li>Target contains all signatures in source
+     * </ul>
+     *
+     * Note that if {@param target} is null we return false.
      */
-    // TODO(b/37977154): Resolve questionable policies.
     public static boolean signaturesMatch(Signature[] storedSigs, PackageInfo target) {
         if (target == null) {
             return false;
@@ -111,24 +122,18 @@
             return true;
         }
 
-        // Allow unsigned apps, but not signed on one device and unsigned on the other
-        // TODO(b/37977154): is this the right policy?
         Signature[] deviceSigs = target.signatures;
         if (MORE_DEBUG) {
             Slog.v(TAG, "signaturesMatch(): stored=" + storedSigs + " device=" + deviceSigs);
         }
-        if ((storedSigs == null || storedSigs.length == 0)
-                && (deviceSigs == null || deviceSigs.length == 0)) {
-            return true;
-        }
-        // TODO(b/37977154): This allows empty stored signature, is this right?
-        if (storedSigs == null || deviceSigs == null) {
+
+        // Don't allow unsigned apps on either end
+        if (ArrayUtils.isEmpty(storedSigs) || ArrayUtils.isEmpty(deviceSigs)) {
             return false;
         }
 
-        // TODO(b/37977154): this demands that every stored signature match one
-        // that is present on device, and does not demand the converse.
-        // Is this this right policy?
+        // 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;
 
diff --git a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
index 650681e..db0ec07 100644
--- a/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
+++ b/services/tests/servicestests/src/com/android/server/backup/utils/AppBackupUtilsTest.java
@@ -31,8 +31,6 @@
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
-import java.util.Random;
-
 @SmallTest
 @Presubmit
 @RunWith(AndroidJUnit4.class)
@@ -40,7 +38,10 @@
     private static final String CUSTOM_BACKUP_AGENT_NAME = "custom.backup.agent";
     private static final String TEST_PACKAGE_NAME = "test_package";
 
-    private final Random mRandom = new Random(1000000009);
+    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);
 
     @Test
     public void appIsEligibleForBackup_backupNotAllowed_returnsFalse() throws Exception {
@@ -220,7 +221,7 @@
 
     @Test
     public void signaturesMatch_targetIsNull_returnsFalse() throws Exception {
-        boolean result = AppBackupUtils.signaturesMatch(new Signature[0], null);
+        boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1}, null);
 
         assertThat(result).isFalse();
     }
@@ -237,61 +238,10 @@
     }
 
     @Test
-    public void signaturesMatch_allowsUnsignedApps_bothSignaturesNull_returnsTrue()
+    public void signaturesMatch_disallowsUnsignedApps_storedSignatureNull_returnsFalse()
             throws Exception {
         PackageInfo packageInfo = new PackageInfo();
-        packageInfo.signatures = null;
-        packageInfo.applicationInfo = new ApplicationInfo();
-
-        boolean result = AppBackupUtils.signaturesMatch(null, packageInfo);
-
-        assertThat(result).isTrue();
-    }
-
-    @Test
-    public void signaturesMatch_allowsUnsignedApps_bothSignaturesEmpty_returnsTrue()
-            throws Exception {
-        PackageInfo packageInfo = new PackageInfo();
-        packageInfo.signatures = new Signature[0];
-        packageInfo.applicationInfo = new ApplicationInfo();
-
-        boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo);
-
-        assertThat(result).isTrue();
-    }
-
-    @Test
-    public void
-    signaturesMatch_allowsUnsignedApps_storedSignatureNullTargetSignatureEmpty_returnsTrue()
-            throws Exception {
-        PackageInfo packageInfo = new PackageInfo();
-        packageInfo.signatures = new Signature[0];
-        packageInfo.applicationInfo = new ApplicationInfo();
-
-        boolean result = AppBackupUtils.signaturesMatch(null, packageInfo);
-
-        assertThat(result).isTrue();
-    }
-
-    @Test
-    public void
-    signaturesMatch_allowsUnsignedApps_storedSignatureEmptyTargetSignatureNull_returnsTrue()
-            throws Exception {
-        PackageInfo packageInfo = new PackageInfo();
-        packageInfo.signatures = null;
-        packageInfo.applicationInfo = new ApplicationInfo();
-
-        boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo);
-
-        assertThat(result).isTrue();
-    }
-
-    @Test
-    public void
-    signaturesMatch_disallowsAppsUnsignedOnOnlyOneDevice_storedSignatureIsNull_returnsFalse()
-            throws Exception {
-        PackageInfo packageInfo = new PackageInfo();
-        packageInfo.signatures = new Signature[]{generateRandomSignature()};
+        packageInfo.signatures = new Signature[] {SIGNATURE_1};
         packageInfo.applicationInfo = new ApplicationInfo();
 
         boolean result = AppBackupUtils.signaturesMatch(null, packageInfo);
@@ -300,37 +250,78 @@
     }
 
     @Test
-    public void
-    signaturesMatch_disallowsAppsUnsignedOnOnlyOneDevice_targetSignatureIsNull_returnsFalse()
+    public void signaturesMatch_disallowsUnsignedApps_storedSignatureEmpty_returnsFalse()
             throws Exception {
         PackageInfo packageInfo = new PackageInfo();
-        packageInfo.signatures = null;
+        packageInfo.signatures = new Signature[] {SIGNATURE_1};
         packageInfo.applicationInfo = new ApplicationInfo();
 
-        boolean result = AppBackupUtils.signaturesMatch(new Signature[]{generateRandomSignature()},
+        boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo);
+
+        assertThat(result).isFalse();
+    }
+
+
+    @Test
+    public void
+    signaturesMatch_disallowsUnsignedApps_targetSignatureEmpty_returnsFalse()
+            throws Exception {
+        PackageInfo packageInfo = new PackageInfo();
+        packageInfo.signatures = new Signature[0];
+        packageInfo.applicationInfo = new ApplicationInfo();
+
+        boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1},
                 packageInfo);
 
         assertThat(result).isFalse();
     }
 
     @Test
-    public void signaturesMatch_signaturesMatch_returnsTrue() throws Exception {
-        Signature signature1 = generateRandomSignature();
-        Signature signature2 = generateRandomSignature();
-        Signature signature3 = generateRandomSignature();
-        assertThat(signature1).isNotEqualTo(signature2);
-        assertThat(signature2).isNotEqualTo(signature3);
-        assertThat(signature1).isNotEqualTo(signature3);
+    public void
+    signaturesMatch_disallowsUnsignedApps_targetSignatureNull_returnsFalse()
+            throws Exception {
+        PackageInfo packageInfo = new PackageInfo();
+        packageInfo.signatures = null;
+        packageInfo.applicationInfo = new ApplicationInfo();
 
-        Signature signature1Copy = new Signature(signature1.toByteArray());
-        Signature signature2Copy = new Signature(signature2.toByteArray());
-        Signature signature3Copy = new Signature(signature3.toByteArray());
-        assertThat(signature1Copy).isEqualTo(signature1);
-        assertThat(signature2Copy).isEqualTo(signature2);
-        assertThat(signature3Copy).isEqualTo(signature3);
+        boolean result = AppBackupUtils.signaturesMatch(new Signature[] {SIGNATURE_1},
+                packageInfo);
+
+        assertThat(result).isFalse();
+    }
+
+    @Test
+    public void signaturesMatch_disallowsUnsignedApps_bothSignaturesNull_returnsFalse()
+            throws Exception {
+        PackageInfo packageInfo = new PackageInfo();
+        packageInfo.signatures = null;
+        packageInfo.applicationInfo = new ApplicationInfo();
+
+        boolean result = AppBackupUtils.signaturesMatch(null, packageInfo);
+
+        assertThat(result).isFalse();
+    }
+
+    @Test
+    public void signaturesMatch_disallowsUnsignedApps_bothSignaturesEmpty_returnsFalse()
+            throws Exception {
+        PackageInfo packageInfo = new PackageInfo();
+        packageInfo.signatures = new Signature[0];
+        packageInfo.applicationInfo = new ApplicationInfo();
+
+        boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo);
+
+        assertThat(result).isFalse();
+    }
+
+    @Test
+    public void signaturesMatch_equalSignatures_returnsTrue() throws Exception {
+        Signature signature1Copy = new Signature(SIGNATURE_1.toByteArray());
+        Signature signature2Copy = new Signature(SIGNATURE_2.toByteArray());
+        Signature signature3Copy = new Signature(SIGNATURE_3.toByteArray());
 
         PackageInfo packageInfo = new PackageInfo();
-        packageInfo.signatures = new Signature[]{signature1, signature2, signature3};
+        packageInfo.signatures = new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3};
         packageInfo.applicationInfo = new ApplicationInfo();
 
         boolean result = AppBackupUtils.signaturesMatch(
@@ -341,20 +332,11 @@
 
     @Test
     public void signaturesMatch_extraSignatureInTarget_returnsTrue() throws Exception {
-        Signature signature1 = generateRandomSignature();
-        Signature signature2 = generateRandomSignature();
-        Signature signature3 = generateRandomSignature();
-        assertThat(signature1).isNotEqualTo(signature2);
-        assertThat(signature2).isNotEqualTo(signature3);
-        assertThat(signature1).isNotEqualTo(signature3);
-
-        Signature signature1Copy = new Signature(signature1.toByteArray());
-        Signature signature2Copy = new Signature(signature2.toByteArray());
-        assertThat(signature1Copy).isEqualTo(signature1);
-        assertThat(signature2Copy).isEqualTo(signature2);
+        Signature signature1Copy = new Signature(SIGNATURE_1.toByteArray());
+        Signature signature2Copy = new Signature(SIGNATURE_2.toByteArray());
 
         PackageInfo packageInfo = new PackageInfo();
-        packageInfo.signatures = new Signature[]{signature1, signature2, signature3};
+        packageInfo.signatures = new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3};
         packageInfo.applicationInfo = new ApplicationInfo();
 
         boolean result = AppBackupUtils.signaturesMatch(
@@ -365,96 +347,37 @@
 
     @Test
     public void signaturesMatch_extraSignatureInStored_returnsFalse() throws Exception {
-        Signature signature1 = generateRandomSignature();
-        Signature signature2 = generateRandomSignature();
-        Signature signature3 = generateRandomSignature();
-        assertThat(signature1).isNotEqualTo(signature2);
-        assertThat(signature2).isNotEqualTo(signature3);
-        assertThat(signature1).isNotEqualTo(signature3);
-
-        Signature signature1Copy = new Signature(signature1.toByteArray());
-        Signature signature2Copy = new Signature(signature2.toByteArray());
-        assertThat(signature1Copy).isEqualTo(signature1);
-        assertThat(signature2Copy).isEqualTo(signature2);
+        Signature signature1Copy = new Signature(SIGNATURE_1.toByteArray());
+        Signature signature2Copy = new Signature(SIGNATURE_2.toByteArray());
 
         PackageInfo packageInfo = new PackageInfo();
         packageInfo.signatures = new Signature[]{signature1Copy, signature2Copy};
         packageInfo.applicationInfo = new ApplicationInfo();
 
         boolean result = AppBackupUtils.signaturesMatch(
-                new Signature[]{signature1, signature2, signature3}, packageInfo);
-
-        assertThat(result).isFalse();
-    }
-
-    @Test
-    public void signaturesMatch_emptyStoredSignatures_returnsTrue() throws Exception {
-        Signature signature1 = generateRandomSignature();
-        Signature signature2 = generateRandomSignature();
-        Signature signature3 = generateRandomSignature();
-        assertThat(signature1).isNotEqualTo(signature2);
-        assertThat(signature2).isNotEqualTo(signature3);
-        assertThat(signature1).isNotEqualTo(signature3);
-
-        PackageInfo packageInfo = new PackageInfo();
-        packageInfo.signatures = new Signature[]{signature1, signature2, signature3};
-        packageInfo.applicationInfo = new ApplicationInfo();
-
-        boolean result = AppBackupUtils.signaturesMatch(new Signature[0], packageInfo);
-
-        assertThat(result).isTrue();
-    }
-
-    @Test
-    public void signaturesMatch_emptyTargetSignatures_returnsFalse() throws Exception {
-        Signature signature1 = generateRandomSignature();
-        Signature signature2 = generateRandomSignature();
-        Signature signature3 = generateRandomSignature();
-        assertThat(signature1).isNotEqualTo(signature2);
-        assertThat(signature2).isNotEqualTo(signature3);
-        assertThat(signature1).isNotEqualTo(signature3);
-
-        PackageInfo packageInfo = new PackageInfo();
-        packageInfo.signatures = new Signature[0];
-        packageInfo.applicationInfo = new ApplicationInfo();
-
-        boolean result = AppBackupUtils.signaturesMatch(
-                new Signature[]{signature1, signature2, signature3}, packageInfo);
+                new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3}, packageInfo);
 
         assertThat(result).isFalse();
     }
 
     @Test
     public void signaturesMatch_oneNonMatchingSignature_returnsFalse() throws Exception {
-        Signature signature1 = generateRandomSignature();
-        Signature signature2 = generateRandomSignature();
-        Signature signature3 = generateRandomSignature();
-        Signature signature4 = generateRandomSignature();
-        assertThat(signature1).isNotEqualTo(signature2);
-        assertThat(signature2).isNotEqualTo(signature3);
-        assertThat(signature1).isNotEqualTo(signature3);
-        assertThat(signature1).isNotEqualTo(signature4);
-        assertThat(signature2).isNotEqualTo(signature4);
-        assertThat(signature3).isNotEqualTo(signature4);
-
-        Signature signature1Copy = new Signature(signature1.toByteArray());
-        Signature signature2Copy = new Signature(signature2.toByteArray());
-        assertThat(signature1Copy).isEqualTo(signature1);
-        assertThat(signature2Copy).isEqualTo(signature2);
+        Signature signature1Copy = new Signature(SIGNATURE_1.toByteArray());
+        Signature signature2Copy = new Signature(SIGNATURE_2.toByteArray());
 
         PackageInfo packageInfo = new PackageInfo();
-        packageInfo.signatures = new Signature[]{signature1, signature2, signature3};
+        packageInfo.signatures = new Signature[]{SIGNATURE_1, SIGNATURE_2, SIGNATURE_3};
         packageInfo.applicationInfo = new ApplicationInfo();
 
         boolean result = AppBackupUtils.signaturesMatch(
-                new Signature[]{signature1Copy, signature2Copy, signature4}, packageInfo);
+                new Signature[]{signature1Copy, signature2Copy, SIGNATURE_4}, packageInfo);
 
         assertThat(result).isFalse();
     }
 
-    private Signature generateRandomSignature() {
+    private static Signature generateSignature(byte i) {
         byte[] signatureBytes = new byte[256];
-        mRandom.nextBytes(signatureBytes);
+        signatureBytes[0] = i;
         return new Signature(signatureBytes);
     }
 }