[Privacy] Compare app signing certificates before restoring
backed up permissions to apps
Resubmitting with fix, see http://b/247728455#comment4 for details on
the fix and http://b/184847040#comment54 for testing rationale.
Earlier Reverted Changes:
If6f0df898:[automerge] Revert "[Privacy] Compare app signing ...
I9fe0701c1:Revert "[Privacy] Compare app signing certificates...
I4f031a63c:Revert "[Privacy] Compare app signing certificates...
Iec42d2fbb:[automerge] Revert "[Privacy] Compare app signing ...
Bug: 184847040
Test: atest CtsSecurityTestCases:PermissionBackupCertificateCheckTest
Merged-In: I84f48d1590ac04eac41ff2e7e59c2e986167101f
Change-Id: I84f48d1590ac04eac41ff2e7e59c2e986167101f
diff --git a/tests/tests/security/Android.bp b/tests/tests/security/Android.bp
index 0cf3dd5..9f4cda5 100644
--- a/tests/tests/security/Android.bp
+++ b/tests/tests/security/Android.bp
@@ -33,6 +33,7 @@
"compatibility-common-util-devicesidelib",
"guava",
"platform-test-annotations",
+ "permission-test-util-lib",
"sts-device-util",
"hamcrest-library",
"NeneInternal",
@@ -80,7 +81,17 @@
":CtsDeviceInfo",
":RolePermissionOverrideTestApp",
":SplitBluetoothPermissionTestApp",
- ],
+ ":CtsPermissionBackupAppCert1",
+ ":CtsPermissionBackupAppCert1Dup",
+ ":CtsPermissionBackupAppCert2",
+ ":CtsPermissionBackupAppCert3",
+ ":CtsPermissionBackupAppCert4",
+ ":CtsPermissionBackupAppCert12",
+ ":CtsPermissionBackupAppCert12Dup",
+ ":CtsPermissionBackupAppCert34",
+ ":CtsPermissionBackupAppCert123",
+ ":CtsPermissionBackupAppCert4History124",
+ ],
}
android_test_helper_app {
@@ -89,6 +100,95 @@
manifest: "testdata/packageinstallertestapp.xml",
}
+android_test_helper_app {
+ name: "CtsPermissionBackupAppCert1",
+ min_sdk_version: "30",
+ certificate: ":permission-test-cert-1",
+ manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "CtsPermissionBackupAppCert1Dup",
+ min_sdk_version: "30",
+ certificate: ":permission-test-cert-1",
+ manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "CtsPermissionBackupAppCert2",
+ min_sdk_version: "30",
+ certificate: ":permission-test-cert-2",
+ manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "CtsPermissionBackupAppCert3",
+ min_sdk_version: "30",
+ certificate: ":permission-test-cert-3",
+ manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "CtsPermissionBackupAppCert4",
+ min_sdk_version: "30",
+ certificate: ":permission-test-cert-4",
+ manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "CtsPermissionBackupAppCert12",
+ min_sdk_version: "30",
+ certificate: ":permission-test-cert-1",
+ additional_certificates: [
+ ":permission-test-cert-2",
+ ],
+ manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "CtsPermissionBackupAppCert12Dup",
+ min_sdk_version: "30",
+ certificate: ":permission-test-cert-1",
+ additional_certificates: [
+ ":permission-test-cert-2",
+ ],
+ manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "CtsPermissionBackupAppCert34",
+ min_sdk_version: "30",
+ certificate: ":permission-test-cert-3",
+ additional_certificates: [
+ ":permission-test-cert-4",
+ ],
+ manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "CtsPermissionBackupAppCert123",
+ min_sdk_version: "30",
+ certificate: ":permission-test-cert-1",
+ additional_certificates: [
+ ":permission-test-cert-2",
+ ":permission-test-cert-3",
+ ],
+ manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
+android_test_helper_app {
+ name: "CtsPermissionBackupAppCert4History124",
+ min_sdk_version: "30",
+ certificate: ":permission-test-cert-4",
+ additional_certificates: [
+ ":permission-test-cert-1",
+
+ ],
+ rotationMinSdkVersion: "30",
+ lineage: ":permission-test-cert-with-rotation-history",
+ manifest: "testdata/permissionbackuptestapp/AndroidManifest.xml",
+}
+
android_app_certificate {
name: "security_cts_test_certificate",
certificate: "security_cts_test_cert",
@@ -98,3 +198,30 @@
name: "RolePermissionOverrideTestApp",
manifest: "testdata/rolepermissionoverridetestapp.xml",
}
+
+android_app_certificate {
+ name: "permission-test-cert-1",
+ certificate: "test-cert-1",
+}
+
+android_app_certificate {
+ name: "permission-test-cert-2",
+ certificate: "test-cert-2",
+}
+
+android_app_certificate {
+ name: "permission-test-cert-3",
+ certificate: "test-cert-3",
+}
+
+android_app_certificate {
+ name: "permission-test-cert-4",
+ certificate: "test-cert-4",
+}
+
+filegroup {
+ name: "permission-test-cert-with-rotation-history",
+ srcs: [
+ "test-cert-with-1-2-4-in-rotation-history",
+ ],
+}
diff --git a/tests/tests/security/AndroidTest.xml b/tests/tests/security/AndroidTest.xml
index 9bd5eb7..73e6bc7 100644
--- a/tests/tests/security/AndroidTest.xml
+++ b/tests/tests/security/AndroidTest.xml
@@ -52,6 +52,16 @@
<option name="cleanup" value="true" />
<option name="push" value="RolePermissionOverrideTestApp.apk->/data/local/tmp/cts/security/RolePermissionOverrideTestApp.apk" />
<option name="push" value="SplitBluetoothPermissionTestApp.apk->/data/local/tmp/cts/security/SplitBluetoothPermissionTestApp.apk" />
+ <option name="push" value="CtsPermissionBackupAppCert1.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert1.apk" />
+ <option name="push" value="CtsPermissionBackupAppCert1Dup.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert1Dup.apk" />
+ <option name="push" value="CtsPermissionBackupAppCert2.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert2.apk" />
+ <option name="push" value="CtsPermissionBackupAppCert3.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert3.apk" />
+ <option name="push" value="CtsPermissionBackupAppCert4.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert4.apk" />
+ <option name="push" value="CtsPermissionBackupAppCert12.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert12.apk" />
+ <option name="push" value="CtsPermissionBackupAppCert12Dup.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert12Dup.apk" />
+ <option name="push" value="CtsPermissionBackupAppCert123.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert123.apk" />
+ <option name="push" value="CtsPermissionBackupAppCert34.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert34.apk" />
+ <option name="push" value="CtsPermissionBackupAppCert4History124.apk->/data/local/tmp/cts/security/CtsPermissionBackupAppCert4History124.apk" />
</target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
@@ -61,4 +71,9 @@
<option name="test-timeout" value="900000" />
<option name="hidden-api-checks" value="false" />
</test>
+
+ <target_preparer class="android.cts.backup.BackupPreparer">
+ <option name="enable-backup-if-needed" value="true" />
+ <option name="select-local-transport" value="true" />
+ </target_preparer>
</configuration>
diff --git a/tests/tests/security/src/android/security/cts/PermissionBackupCertificateCheckTest.kt b/tests/tests/security/src/android/security/cts/PermissionBackupCertificateCheckTest.kt
new file mode 100644
index 0000000..1170939
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/PermissionBackupCertificateCheckTest.kt
@@ -0,0 +1,813 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package android.security.cts
+
+import android.Manifest.permission.*
+import android.app.AppOpsManager
+import android.content.pm.PackageManager.*
+import android.os.ParcelFileDescriptor
+import android.permission.cts.PermissionUtils.grantPermission
+import android.platform.test.annotations.AppModeFull
+import android.platform.test.annotations.AsbSecurityTest
+import androidx.test.InstrumentationRegistry
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.compatibility.common.util.BackupUtils
+import com.android.compatibility.common.util.BackupUtils.LOCAL_TRANSPORT_TOKEN
+import com.android.compatibility.common.util.BusinessLogicTestCase
+import com.android.compatibility.common.util.ShellUtils.runShellCommand
+import com.android.compatibility.common.util.SystemUtil.callWithShellPermissionIdentity
+import com.android.compatibility.common.util.SystemUtil.runWithShellPermissionIdentity
+import com.android.sts.common.util.StsExtraBusinessLogicTestCase
+import java.io.InputStream
+import org.junit.After
+import org.junit.Assert.assertEquals
+import org.junit.Assert.assertFalse
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+
+/**
+ * Tests that permissions for backed up apps are restored only after checking that their signing
+ * certificates are compared.
+ *
+ * @see [com.android.permissioncontroller.permission.service.BackupHelper]
+ */
+@AppModeFull
+@RunWith(AndroidJUnit4::class)
+class PermissionBackupCertificateCheckTest : StsExtraBusinessLogicTestCase() {
+ private val backupUtils: BackupUtils =
+ object : BackupUtils() {
+ override fun executeShellCommand(command: String): InputStream {
+ val pfd =
+ BusinessLogicTestCase.getInstrumentation()
+ .uiAutomation
+ .executeShellCommand(command)
+ return ParcelFileDescriptor.AutoCloseInputStream(pfd)
+ }
+ }
+
+ private var isBackupSupported = false
+
+ private val targetContext = InstrumentationRegistry.getTargetContext()
+
+ @Before
+ fun setUp() {
+ val packageManager = BusinessLogicTestCase.getInstrumentation().context.packageManager
+ isBackupSupported =
+ (packageManager != null && packageManager.hasSystemFeature(FEATURE_BACKUP))
+
+ if (isBackupSupported) {
+ assertTrue("Backup not enabled", backupUtils.isBackupEnabled)
+ assertTrue("LocalTransport not selected", backupUtils.isLocalTransportSelected)
+ backupUtils.executeShellCommandSync("setprop log.tag.$APP_LOG_TAG VERBOSE")
+ }
+ }
+
+ @After
+ fun tearDown() {
+ uninstallIfInstalled(APP)
+ clearFlag(APP, ACCESS_FINE_LOCATION, FLAG_PERMISSION_USER_SET)
+ clearFlag(APP, ACCESS_BACKGROUND_LOCATION, FLAG_PERMISSION_USER_SET)
+ }
+
+ /**
+ * Test backup and restore of regular runtime permissions, when the app being restored has the
+ * same certificate as the backed up app.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_sameCert_restoresRuntimePermissions() {
+ install(APP_APK_CERT_1)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_1_DUP)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+ assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+ }
+ }
+
+ /**
+ * Test backup and restore of regular runtime permissions, when the app being restored has a
+ * different certificate as the backed up app.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_diffCert_doesNotGrantRuntimePermissions() {
+ install(APP_APK_CERT_1)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_3)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+ }
+ }
+
+ /**
+ * Test backup and restore of regular runtime permissions, when the app being restored has the
+ * backed up app's certificate in its signing history.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_midHistoryToRotated_restoresRuntimePermissions() {
+ install(APP_APK_CERT_2)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_4_HISTORY_1_2_4)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+ assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+ }
+ }
+
+ /**
+ * Test backup and restore of regular runtime permissions, when the app being restored has the
+ * backed up app's certificate as the original certificate in its signing history.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_origToRotated_restoresRuntimePermissions() {
+ install(APP_APK_CERT_1)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_4_HISTORY_1_2_4)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+ assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+ }
+ }
+
+ /**
+ * Test backup and restore of regular runtime permissions, when the backed up app has the
+ * restored app's certificate in its signing history.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_rotatedToMidHistory_restoresRuntimePermissions() {
+ install(APP_APK_CERT_4_HISTORY_1_2_4)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_2)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+ assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+ }
+ }
+
+ /**
+ * Test backup and restore of regular runtime permissions, when the backed up app has the
+ * restored app's certificate in its signing history as its original certificate.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_rotatedToOrig_restoresRuntimePermissions() {
+ install(APP_APK_CERT_4_HISTORY_1_2_4)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_1)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+ assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+ }
+ }
+
+ /**
+ * Test backup and restore of regular runtime permissions, when the backed up app has the same
+ * certificate as the restored app, but the restored app additionally has signing certificate
+ * history.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_sameWithHistory_restoresRuntimePermissions() {
+ install(APP_APK_CERT_4)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_4_HISTORY_1_2_4)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+ assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+ }
+ }
+
+ /**
+ * Test backup and restore of regular runtime permissions, when the backed up app has the same
+ * certificate as the restored app, but the backed up app additionally has signing certificate
+ * history.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_sameWithoutHistory_restoresRuntimePermissions() {
+ install(APP_APK_CERT_4_HISTORY_1_2_4)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_4)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+ assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+ }
+ }
+
+ /**
+ * Test backup and restore of regular runtime permissions, when the app being restored has
+ * signing history, but the backed up app's certificate is not in this signing history.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_notInBackedUpHistory_doesNotRestoreRuntimePerms() {
+ install(APP_APK_CERT_4_HISTORY_1_2_4)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_3)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+ }
+ }
+
+ /**
+ * Test backup and restore of regular runtime permissions, when the app being restored has
+ * signing history, but the backed up app's certificate is not in this signing history.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_notInRestoredHistory_doesNotRestoreRuntimePerms() {
+ install(APP_APK_CERT_3)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_4_HISTORY_1_2_4)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+ }
+ }
+
+ /**
+ * Test backup and restore of regular runtime permissions, when the app being restored has
+ * multiple certificates, and the backed up app also has identical multiple certificates.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_sameMultCerts_restoresRuntimePermissions() {
+ install(APP_APK_CERT_1_2)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_1_2_DUP)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+ assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+ }
+ }
+
+ /**
+ * Test backup and restore of regular runtime permissions, when the app being restored has
+ * multiple certificates, and the backed up app do not have identical multiple certificates.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_diffMultCerts_doesNotRestoreRuntimePermissions() {
+ install(APP_APK_CERT_1_2)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_3_4)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+ }
+ }
+
+ /**
+ * Test backup and restore of regular runtime permissions, when the app being restored has
+ * multiple certificates, and the backed up app's certificate is present in th restored app's
+ * certificates.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_singleToMultiCert_restoresRuntimePerms() {
+ install(APP_APK_CERT_1)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_1_2_3)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+ }
+ }
+
+ /**
+ * Test backup and restore of regular runtime permissions, when the backed up app and the app
+ * being restored have multiple certificates, and the backed up app's certificates are a subset
+ * of the restored app's certificates.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_multCertsToSuperset_doesNotRestoreRuntimePerms() {
+ install(APP_APK_CERT_1_2)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_1_2_3)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+ }
+ }
+
+ /**
+ * Test backup and restore of regular runtime permissions, when the backed up app and the app
+ * being restored have multiple certificates, and the backed up app's certificates are a
+ * superset of the restored app's certificates.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_multCertsToSubset_doesNotRestoreRuntimePermissions() {
+ install(APP_APK_CERT_1_2_3)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_1_2)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, READ_CONTACTS))
+ }
+ }
+
+ /**
+ * Test backup and restore of tri-state permissions, when both foreground and background runtime
+ * permissions are not granted and the backed up and restored app have compatible certificates.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_fgBgDenied_matchingCerts_restoresFgBgPermissions() {
+ install(APP_APK_CERT_2)
+ if (!isBackupSupported) {
+ return
+ }
+ // Make a token change to permission state, to enable to us to determine when restore is
+ // complete.
+ grantPermission(APP, WRITE_CONTACTS)
+ // PERMISSION_DENIED is the default state, so we mark the permissions as user set in order
+ // to ensure that permissions are backed up.
+ setFlag(APP, ACCESS_FINE_LOCATION, FLAG_PERMISSION_USER_SET)
+ setFlag(APP, ACCESS_BACKGROUND_LOCATION, FLAG_PERMISSION_USER_SET)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_4_HISTORY_1_2_4)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+
+ // Wait until restore is complete.
+ assertEquals(PERMISSION_GRANTED, checkPermission(APP, WRITE_CONTACTS))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION))
+ assertEquals(AppOpsManager.MODE_IGNORED, getAppOp(APP, ACCESS_FINE_LOCATION))
+ }
+ }
+
+ /**
+ * Test backup and restore of tri-state permissions, when both foreground and background runtime
+ * permissions are not granted and the backed up and restored app don't have compatible
+ * certificates.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_fgBgDenied_notMatchingCerts_doesNotRestorePerms() {
+ install(APP_APK_CERT_1)
+ if (!isBackupSupported) {
+ return
+ }
+ // Make a token change to permission state, to enable to us to determine when restore is
+ // complete.
+ grantPermission(APP, WRITE_CONTACTS)
+ // PERMISSION_DENIED is the default state, so we mark the permissions as user set in order
+ // to ensure that permissions are backed up.
+ setFlag(APP, ACCESS_FINE_LOCATION, FLAG_PERMISSION_USER_SET)
+ setFlag(APP, ACCESS_BACKGROUND_LOCATION, FLAG_PERMISSION_USER_SET)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_2)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+
+ // Wait until restore is complete.
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, WRITE_CONTACTS))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION))
+ assertEquals(AppOpsManager.MODE_IGNORED, getAppOp(APP, ACCESS_FINE_LOCATION))
+ }
+ }
+
+ /**
+ * Test backup and restore of tri-state permissions, when foreground runtime permission is
+ * granted and the backed up and restored app have compatible certificates.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_fgGranted_matchingCerts_restoresFgBgPermissions() {
+ install(APP_APK_CERT_2)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+ // PERMISSION_DENIED is the default state, so we mark the permissions as user set in order
+ // to ensure that permissions are backed up.
+ setFlag(APP, ACCESS_BACKGROUND_LOCATION, FLAG_PERMISSION_USER_SET)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_4_HISTORY_1_2_4)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+ assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION))
+ assertEquals(AppOpsManager.MODE_FOREGROUND, getAppOp(APP, ACCESS_FINE_LOCATION))
+ }
+ }
+
+ /**
+ * Test backup and restore of tri-state permissions, when foreground runtime permission is
+ * granted and the backed up and restored app don't have compatible certificates.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_fgGranted_notMatchingCerts_doesNotRestoreFgBgPerms() {
+ install(APP_APK_CERT_1)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+ // PERMISSION_DENIED is the default state, so we mark the permissions as user set in order
+ // to ensure that permissions are backed up.
+ setFlag(APP, ACCESS_BACKGROUND_LOCATION, FLAG_PERMISSION_USER_SET)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_2)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION))
+ assertEquals(AppOpsManager.MODE_IGNORED, getAppOp(APP, ACCESS_FINE_LOCATION))
+ }
+ }
+
+ /**
+ * Test backup and restore of tri-state permissions, when foreground and background runtime
+ * permissions are granted and the backed up and restored app have compatible certificates.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_fgBgGranted_matchingCerts_restoresFgBgPermissions() {
+ install(APP_APK_CERT_2)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+ grantPermission(APP, ACCESS_BACKGROUND_LOCATION)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_4_HISTORY_1_2_4)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+ assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION))
+ assertEquals(AppOpsManager.MODE_ALLOWED, getAppOp(APP, ACCESS_FINE_LOCATION))
+ }
+ }
+
+ /**
+ * Test backup and restore of tri-state permissions, when foreground and background runtime
+ * permissions are granted and the backed up and restored app don't have compatible
+ * certificates.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_fgBgGranted_notMatchingCerts_restoresFgBgPerms() {
+ install(APP_APK_CERT_1)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+ grantPermission(APP, ACCESS_BACKGROUND_LOCATION)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_2)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually {
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION))
+ assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_BACKGROUND_LOCATION))
+ assertEquals(AppOpsManager.MODE_IGNORED, getAppOp(APP, ACCESS_FINE_LOCATION))
+ }
+ }
+
+ /**
+ * Test backup and restore of flags when the backed up app and restored app have compatible
+ * certificates.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_matchingCerts_restoresFlags() {
+ install(APP_APK_CERT_2)
+ if (!isBackupSupported) {
+ return
+ }
+ setFlag(APP, WRITE_CONTACTS, FLAG_PERMISSION_USER_SET)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_4_HISTORY_1_2_4)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually { assertTrue(isFlagSet(APP, WRITE_CONTACTS, FLAG_PERMISSION_USER_SET)) }
+ }
+
+ /**
+ * Test backup and restore of flags when the backed up app and restored app don't have
+ * compatible certificates.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_notMatchingCerts_doesNotRestoreFlag() {
+ install(APP_APK_CERT_1)
+ if (!isBackupSupported) {
+ return
+ }
+ setFlag(APP, WRITE_CONTACTS, FLAG_PERMISSION_USER_SET)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ install(APP_APK_CERT_2)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+
+ eventually { assertFalse(isFlagSet(APP, WRITE_CONTACTS, FLAG_PERMISSION_USER_SET)) }
+ }
+
+ /**
+ * Test backup and delayed restore of regular runtime permission, i.e. when an app is installed
+ * after restore has run, and the backed up app and restored app have compatible certificates.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_appInstalledLater_matchingCerts_restoresCorrectly() {
+ install(APP_APK_CERT_2)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+ install(APP_APK_CERT_4_HISTORY_1_2_4)
+
+ eventually { assertEquals(PERMISSION_GRANTED, checkPermission(APP, ACCESS_FINE_LOCATION)) }
+ }
+
+ /**
+ * Test backup and delayed restore of regular runtime permission, i.e. when an app is installed
+ * after restore has run, and the backed up app and restored app don't have compatible
+ * certificates.
+ */
+ @Test
+ @AsbSecurityTest(cveBugId = [184847040])
+ fun testRestore_appInstalledLater_notMatchingCerts_doesNotRestore() {
+ install(APP_APK_CERT_1)
+ if (!isBackupSupported) {
+ return
+ }
+ grantPermission(APP, ACCESS_FINE_LOCATION)
+
+ backupUtils.backupNowAndAssertSuccess(ANDROID_PACKAGE)
+ uninstallIfInstalled(APP)
+ backupUtils.restoreAndAssertSuccess(LOCAL_TRANSPORT_TOKEN, ANDROID_PACKAGE)
+ install(APP_APK_CERT_4_HISTORY_1_2_4)
+
+ eventually { assertEquals(PERMISSION_DENIED, checkPermission(APP, ACCESS_FINE_LOCATION)) }
+ }
+
+ private fun install(apk: String) {
+ val output = runShellCommand("pm install -r $apk")
+ assertEquals("Success", output)
+ }
+
+ private fun uninstallIfInstalled(packageName: String) {
+ runShellCommand("pm uninstall $packageName")
+ }
+
+ private fun setFlag(app: String, permission: String, flag: Int) {
+ runWithShellPermissionIdentity {
+ targetContext.packageManager.updatePermissionFlags(
+ permission, app, flag, flag, targetContext.user)
+ }
+ }
+
+ private fun clearFlag(app: String, permission: String, flag: Int) {
+ runWithShellPermissionIdentity {
+ targetContext.packageManager.updatePermissionFlags(
+ permission, app, flag, 0, targetContext.user)
+ }
+ }
+
+ private fun isFlagSet(app: String, permission: String, flag: Int): Boolean {
+ return try {
+ callWithShellPermissionIdentity<Int> {
+ targetContext.packageManager.getPermissionFlags(permission, app, targetContext.user)
+ } and flag == flag
+ } catch (e: Exception) {
+ throw RuntimeException(e)
+ }
+ }
+
+ private fun checkPermission(app: String, permission: String): Int {
+ return targetContext.packageManager.checkPermission(permission, app)
+ }
+
+ private fun getAppOp(app: String, permission: String): Int {
+ return try {
+ callWithShellPermissionIdentity {
+ targetContext
+ .getSystemService<AppOpsManager>(AppOpsManager::class.java)!!
+ .unsafeCheckOpRaw(
+ AppOpsManager.permissionToOp(permission)!!,
+ targetContext.packageManager.getPackageUid(app, 0),
+ app)
+ }
+ } catch (e: Exception) {
+ throw RuntimeException(e)
+ }
+ }
+
+ companion object {
+ /** The name of the package of the apps under test */
+ private const val APP = "android.security.permissionbackup"
+ /** The apk of the packages */
+ private const val APK_PATH = "/data/local/tmp/cts/security/"
+ private const val APP_APK_CERT_1 = "${APK_PATH}CtsPermissionBackupAppCert1.apk"
+ private const val APP_APK_CERT_1_DUP = "${APK_PATH}CtsPermissionBackupAppCert1Dup.apk"
+ private const val APP_APK_CERT_2 = "${APK_PATH}CtsPermissionBackupAppCert2.apk"
+ private const val APP_APK_CERT_3 = "${APK_PATH}CtsPermissionBackupAppCert3.apk"
+ private const val APP_APK_CERT_4 = "${APK_PATH}CtsPermissionBackupAppCert4.apk"
+ private const val APP_APK_CERT_1_2 = "${APK_PATH}CtsPermissionBackupAppCert12.apk"
+ private const val APP_APK_CERT_1_2_DUP = "${APK_PATH}CtsPermissionBackupAppCert12Dup.apk"
+ private const val APP_APK_CERT_1_2_3 = "${APK_PATH}CtsPermissionBackupAppCert123.apk"
+ private const val APP_APK_CERT_3_4 = "${APK_PATH}CtsPermissionBackupAppCert34.apk"
+ private const val APP_APK_CERT_4_HISTORY_1_2_4 =
+ "${APK_PATH}CtsPermissionBackupAppCert4History124.apk"
+ private const val APP_LOG_TAG = "PermissionBackupApp"
+ /** The name of the package for backup */
+ private const val ANDROID_PACKAGE = "android"
+ private const val TIMEOUT_MILLIS: Long = 10000
+
+ /**
+ * Make sure that a [Runnable] eventually finishes without throwing an [Exception].
+ *
+ * @param r The [Runnable] to run.
+ */
+ fun eventually(r: Runnable) {
+ val start = System.currentTimeMillis()
+ while (true) {
+ try {
+ r.run()
+ return
+ } catch (e: Throwable) {
+ if (System.currentTimeMillis() - start < TIMEOUT_MILLIS) {
+ try {
+ Thread.sleep(100)
+ } catch (ignored: InterruptedException) {
+ throw RuntimeException(e)
+ }
+ } else {
+ throw e
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/tests/tests/security/test-cert-1.pk8 b/tests/tests/security/test-cert-1.pk8
new file mode 100644
index 0000000..f781c30
--- /dev/null
+++ b/tests/tests/security/test-cert-1.pk8
Binary files differ
diff --git a/tests/tests/security/test-cert-1.x509.pem b/tests/tests/security/test-cert-1.x509.pem
new file mode 100644
index 0000000..06adcfe
--- /dev/null
+++ b/tests/tests/security/test-cert-1.x509.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBbDCCARGgAwIBAgIJAMoPtk37ZudyMAoGCCqGSM49BAMCMBIxEDAOBgNVBAMM
+B2VjLXAyNTYwHhcNMTYwMzMxMTQ1ODA2WhcNNDMwODE3MTQ1ODA2WjASMRAwDgYD
+VQQDDAdlYy1wMjU2MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEpl8RPSLLSROQ
+gwesMe4roOkTi3hfrGU20U6izpDStL/hlLUM3I4Wn1SnOpke8Pp2MpglvgeMx4J0
+BwPaRLTX66NQME4wHQYDVR0OBBYEFNQTNWi5WzAVizIgceqMQ/9bBczIMB8GA1Ud
+IwQYMBaAFNQTNWi5WzAVizIgceqMQ/9bBczIMAwGA1UdEwQFMAMBAf8wCgYIKoZI
+zj0EAwIDSQAwRgIhAPUEoIZsrvAp9BcULFy3E1THn/zR1kBhjfyk8Z4W23jWAiEA
++O6kgpeZwGytCMbT0tLsBeBXQVTnR+oP27gELLZVqt0=
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/test-cert-2.pk8 b/tests/tests/security/test-cert-2.pk8
new file mode 100644
index 0000000..5e73f27
--- /dev/null
+++ b/tests/tests/security/test-cert-2.pk8
Binary files differ
diff --git a/tests/tests/security/test-cert-2.x509.pem b/tests/tests/security/test-cert-2.x509.pem
new file mode 100644
index 0000000..f8e5e65
--- /dev/null
+++ b/tests/tests/security/test-cert-2.x509.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBbTCCAROgAwIBAgIJAIhVvR3SsrIlMAoGCCqGSM49BAMCMBIxEDAOBgNVBAMM
+B2VjLXAyNTYwHhcNMTgwNzEzMTc0MTUxWhcNMjgwNzEwMTc0MTUxWjAUMRIwEAYD
+VQQDDAllYy1wMjU2XzIwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQdTMoEcq2X
+7jzs7w2pPWK0UMZ4gzOzbnVTzen3SrXfALu6a6lQ5oRh1wu8JxtiFR2tLeK/YgPN
+IHaAHHqdRCLho1AwTjAdBgNVHQ4EFgQUeZHZKwII/ESL9QbU78n/9CjLXl8wHwYD
+VR0jBBgwFoAU1BM1aLlbMBWLMiBx6oxD/1sFzMgwDAYDVR0TBAUwAwEB/zAKBggq
+hkjOPQQDAgNIADBFAiAnaauxtJ/C9TR5xK6SpmMdq/1SLJrLC7orQ+vrmcYwEQIh
+ANJg+x0fF2z5t/pgCYv9JDGfSQWj5f2hAKb+Giqxn/Ce
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/test-cert-3.pk8 b/tests/tests/security/test-cert-3.pk8
new file mode 100644
index 0000000..d7309dd
--- /dev/null
+++ b/tests/tests/security/test-cert-3.pk8
Binary files differ
diff --git a/tests/tests/security/test-cert-3.x509.pem b/tests/tests/security/test-cert-3.x509.pem
new file mode 100644
index 0000000..c028ff7
--- /dev/null
+++ b/tests/tests/security/test-cert-3.x509.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBbjCCARWgAwIBAgIJAIOU9crRaomnMAoGCCqGSM49BAMCMBQxEjAQBgNVBAMM
+CWVjLXAyNTZfMjAeFw0xODA3MTQwMDA1MjZaFw0yODA3MTEwMDA1MjZaMBQxEjAQ
+BgNVBAMMCWVjLXAyNTZfMzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABPMeYkMO
+nbb8WSjZdfxOR0GbrPyy4HyJKZ5s1+NE3SGt/TCNWMtJoaKj/srM7qSGIGnzC+Fk
+O8wlUEDYCJ37N0OjUDBOMB0GA1UdDgQWBBRvjQgosT769Xf8hrDpn6PlS8vP8DAf
+BgNVHSMEGDAWgBR5kdkrAgj8RIv1BtTvyf/0KMteXzAMBgNVHRMEBTADAQH/MAoG
+CCqGSM49BAMCA0cAMEQCICVr2qJ4TCc+TMKRpZWkZ3ne6d6QRNyferggMJVn35/p
+AiAaStjGmJG1qMR0NP6VQO0fSXm1+tNIPz+gTVZ3NVpXng==
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/test-cert-4.pk8 b/tests/tests/security/test-cert-4.pk8
new file mode 100644
index 0000000..3675d50
--- /dev/null
+++ b/tests/tests/security/test-cert-4.pk8
Binary files differ
diff --git a/tests/tests/security/test-cert-4.x509.pem b/tests/tests/security/test-cert-4.x509.pem
new file mode 100644
index 0000000..4060400
--- /dev/null
+++ b/tests/tests/security/test-cert-4.x509.pem
@@ -0,0 +1,10 @@
+-----BEGIN CERTIFICATE-----
+MIIBezCCASCgAwIBAgIUbIy4qBhDPB5kMfsW+zrg+1rWCqcwCgYIKoZIzj0EAwIw
+FDESMBAGA1UEAwwJZWMtcDI1Nl8zMB4XDTIwMDUxMzE5MTUyOFoXDTMwMDUxMTE5
+MTUyOFowFDESMBAGA1UEAwwJZWMtcDI1Nl80MFkwEwYHKoZIzj0CAQYIKoZIzj0D
+AQcDQgAE20pgAx55rUnLdZAH1oVdRGm5HIurBlQ08vupca3n5NGVmaD2e15wjP2n
+VD5WMMN2nTfgk2QNfHaKFRRM0OXc9KNQME4wHQYDVR0OBBYEFG54lwMyVUM2tu6J
+JOqnAjDjk/Z4MB8GA1UdIwQYMBaAFG+NCCixPvr1d/yGsOmfo+VLy8/wMAwGA1Ud
+EwQFMAMBAf8wCgYIKoZIzj0EAwIDSQAwRgIhAM54bnnsdUdEYILpyvkQYU/4B1j5
+gZ+w8UhpUGer4PzUAiEApIgeMy3ewhFq0rWc+JHQ8zH/fifne3xiBseYjZtTkzA=
+-----END CERTIFICATE-----
diff --git a/tests/tests/security/test-cert-with-1-2-4-in-rotation-history b/tests/tests/security/test-cert-with-1-2-4-in-rotation-history
new file mode 100644
index 0000000..7326e46
--- /dev/null
+++ b/tests/tests/security/test-cert-with-1-2-4-in-rotation-history
Binary files differ
diff --git a/tests/tests/security/testdata/permissionbackuptestapp/AndroidManifest.xml b/tests/tests/security/testdata/permissionbackuptestapp/AndroidManifest.xml
new file mode 100644
index 0000000..2b75d8c
--- /dev/null
+++ b/tests/tests/security/testdata/permissionbackuptestapp/AndroidManifest.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2018 The Android Open Source Project
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="android.security.permissionbackup" >
+
+ <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
+ <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
+ <uses-permission android:name="android.permission.READ_CONTACTS"/>
+ <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
+
+ <application
+ android:label="Android Permission Backup CTS App">
+ <uses-library android:name="android.test.runner" />
+ </application>
+</manifest>