| /* |
| * Copyright (C) 2017 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.appsecurity.cts; |
| |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertTrue; |
| |
| import android.platform.test.annotations.AppModeFull; |
| import android.platform.test.annotations.Presubmit; |
| |
| import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| |
| import java.util.HashMap; |
| |
| @Presubmit |
| @RunWith(DeviceJUnit4ClassRunner.class) |
| @AppModeFull(reason = "Overlays cannot be instant apps") |
| public class OverlayHostTest extends BaseAppSecurityTest { |
| |
| // Test applications |
| private static final String TARGET_OVERLAYABLE_APK = "CtsOverlayTarget.apk"; |
| private static final String TARGET_NO_OVERLAYABLE_APK = "CtsOverlayTargetNoOverlayable.apk"; |
| |
| private static final String OVERLAY_ANDROID_APK = "CtsOverlayAndroid.apk"; |
| private static final String OVERLAY_ALL_APK = "CtsOverlayPolicyAll.apk"; |
| private static final String OVERLAY_ALL_HAS_CODE_APK = "CtsOverlayPolicyAllHasCode.apk"; |
| private static final String OVERLAY_ALL_NO_NAME_APK = "CtsOverlayPolicyAllNoName.apk"; |
| private static final String OVERLAY_ALL_NO_NAME_DIFFERENT_CERT_APK = |
| "CtsOverlayPolicyAllNoNameDifferentCert.apk"; |
| private static final String OVERLAY_ALL_PIE_APK = "CtsOverlayPolicyAllPie.apk"; |
| private static final String OVERLAY_PRODUCT_APK = "CtsOverlayPolicyProduct.apk"; |
| private static final String OVERLAY_SYSTEM_APK = "CtsOverlayPolicySystem.apk"; |
| private static final String OVERLAY_VENDOR_APK = "CtsOverlayPolicyVendor.apk"; |
| private static final String OVERLAY_DIFFERENT_SIGNATURE_APK = "CtsOverlayPolicySignatureDifferent.apk"; |
| |
| // Test application package names |
| private static final String TARGET_PACKAGE = "com.android.cts.overlay.target"; |
| private static final String OVERLAY_ANDROID_PACKAGE = "com.android.cts.overlay.android"; |
| private static final String OVERLAY_ALL_PACKAGE = "com.android.cts.overlay.all"; |
| private static final String OVERLAY_PRODUCT_PACKAGE = "com.android.cts.overlay.policy.product"; |
| private static final String OVERLAY_SYSTEM_PACKAGE = "com.android.cts.overlay.policy.system"; |
| private static final String OVERLAY_VENDOR_PACKAGE = "com.android.cts.overlay.policy.vendor"; |
| private static final String OVERLAY_DIFFERENT_SIGNATURE_PACKAGE = "com.android.cts.overlay.policy.signature"; |
| |
| // Test application test class |
| private static final String TEST_APP_APK = "CtsOverlayApp.apk"; |
| private static final String TEST_APP_PACKAGE = "com.android.cts.overlay.app"; |
| private static final String TEST_APP_CLASS = "com.android.cts.overlay.app.OverlayableTest"; |
| private static final String OVERLAY_TARGET_TEST_APP_CLASS = |
| "com.android.cts.overlay.target.OverlayTargetTest"; |
| |
| // Overlay states |
| private static final String STATE_DISABLED = "STATE_DISABLED"; |
| private static final String STATE_ENABLED = "STATE_ENABLED"; |
| private static final String STATE_NO_IDMAP = "STATE_NO_IDMAP"; |
| |
| // test arguments |
| private static final String PARAM_START_SERVICE = "start_service"; |
| |
| private static final long OVERLAY_WAIT_TIMEOUT = 10000; // 10 seconds |
| |
| @Before |
| public void setUp() throws Exception { |
| new InstallMultiple().addFile(TEST_APP_APK).run(); |
| } |
| |
| @After |
| public void tearDown() throws Exception { |
| getDevice().uninstallPackage(TEST_APP_PACKAGE); |
| } |
| |
| private String getStateForOverlay(String overlayPackage) throws Exception { |
| String result = getDevice().executeShellCommand("cmd overlay dump"); |
| |
| String overlayPackageForCurrentUser = overlayPackage + ":" + getDevice().getCurrentUser(); |
| |
| int startIndex = result.indexOf(overlayPackageForCurrentUser); |
| if (startIndex < 0) { |
| return null; |
| } |
| |
| int endIndex = result.indexOf('}', startIndex); |
| assertTrue(endIndex > startIndex); |
| |
| int stateIndex = result.indexOf("mState", startIndex); |
| assertTrue(startIndex < stateIndex && stateIndex < endIndex); |
| |
| int colonIndex = result.indexOf(':', stateIndex); |
| assertTrue(stateIndex < colonIndex && colonIndex < endIndex); |
| |
| int endLineIndex = result.indexOf('\n', colonIndex); |
| assertTrue(colonIndex < endLineIndex && endLineIndex < endIndex); |
| return result.substring(colonIndex + 2, endLineIndex); |
| } |
| |
| private void waitForOverlayState(String overlayPackage, String state) throws Exception { |
| boolean overlayFound = false; |
| long startTime = System.currentTimeMillis(); |
| |
| while (!overlayFound && (System.currentTimeMillis() - startTime < OVERLAY_WAIT_TIMEOUT)) { |
| String result = getStateForOverlay(overlayPackage); |
| overlayFound = state.equals(result); |
| } |
| |
| assertTrue(overlayFound); |
| } |
| |
| private void assertFailToGenerateIdmap(String overlayApk, String overlayPackage) |
| throws Exception { |
| try { |
| getDevice().uninstallPackage(TARGET_PACKAGE); |
| getDevice().uninstallPackage(overlayPackage); |
| assertFalse(getDevice().getInstalledPackageNames().contains(OVERLAY_ALL_PACKAGE)); |
| assertFalse(getDevice().getInstalledPackageNames().contains(overlayPackage)); |
| |
| new InstallMultiple().addFile(TARGET_OVERLAYABLE_APK).run(); |
| new InstallMultiple().addFile(overlayApk).run(); |
| |
| waitForOverlayState(overlayPackage, STATE_NO_IDMAP); |
| getDevice().executeShellCommand("cmd overlay enable --user current " + overlayPackage); |
| waitForOverlayState(overlayPackage, STATE_NO_IDMAP); |
| } finally { |
| getDevice().uninstallPackage(TARGET_PACKAGE); |
| getDevice().uninstallPackage(overlayPackage); |
| } |
| } |
| |
| private void runOverlayDeviceTest(String targetApk, String overlayApk, String overlayPackage, |
| String testMethod) |
| throws Exception { |
| try { |
| getDevice().uninstallPackage(TARGET_PACKAGE); |
| getDevice().uninstallPackage(overlayPackage); |
| assertFalse(getDevice().getInstalledPackageNames().contains(TARGET_PACKAGE)); |
| assertFalse(getDevice().getInstalledPackageNames().contains(overlayPackage)); |
| |
| new InstallMultiple().addFile(overlayApk).run(); |
| new InstallMultiple().addFile(targetApk).run(); |
| |
| waitForOverlayState(overlayPackage, STATE_DISABLED); |
| getDevice().executeShellCommand("cmd overlay enable --user current " + overlayPackage); |
| waitForOverlayState(overlayPackage, STATE_ENABLED); |
| |
| runDeviceTests(TEST_APP_PACKAGE, TEST_APP_CLASS, testMethod, false /* instant */); |
| } finally { |
| getDevice().uninstallPackage(TARGET_PACKAGE); |
| getDevice().uninstallPackage(overlayPackage); |
| } |
| } |
| |
| private void runDeviceTests(String packageName, String testClassName, String testMethodName, |
| HashMap<String, String> testArgs) throws Exception { |
| Utils.runDeviceTestsAsCurrentUser(getDevice(), packageName, testClassName, testMethodName, |
| testArgs); |
| } |
| |
| /** |
| * Overlays that target android and are not signed with the platform signature must not be |
| * installed successfully. |
| */ |
| @Test |
| public void testCannotInstallTargetAndroidNotPlatformSigned() throws Exception { |
| try { |
| getDevice().uninstallPackage(OVERLAY_ANDROID_PACKAGE); |
| assertFalse(getDevice().getInstalledPackageNames().contains(OVERLAY_ANDROID_PACKAGE)); |
| |
| // Try to install the overlay, but expect an error. |
| new InstallMultiple().addFile(OVERLAY_ANDROID_APK).runExpectingFailure(); |
| |
| // The install should have failed. |
| assertFalse(getDevice().getInstalledPackageNames().contains(OVERLAY_ANDROID_PACKAGE)); |
| |
| // The package of the installed overlay should not appear in the overlay manager list. |
| assertFalse(getDevice().executeShellCommand("cmd overlay list --user current ") |
| .contains(" " + OVERLAY_ANDROID_PACKAGE + "\n")); |
| } finally { |
| getDevice().uninstallPackage(OVERLAY_ANDROID_PACKAGE); |
| } |
| } |
| |
| /** |
| * Overlays that target a pre-Q sdk and that are not signed with the platform signature must not |
| * be installed. |
| **/ |
| @Test |
| public void testCannotInstallPieOverlayNotPlatformSigned() throws Exception { |
| try { |
| getDevice().uninstallPackage(OVERLAY_ALL_PACKAGE); |
| assertFalse(getDevice().getInstalledPackageNames().contains(OVERLAY_ALL_PACKAGE)); |
| |
| // Try to install the overlay, but expect an error. |
| new InstallMultiple().addFile(OVERLAY_ALL_PIE_APK).runExpectingFailure(); |
| |
| // The install should have failed. |
| assertFalse(getDevice().getInstalledPackageNames().contains(OVERLAY_ALL_PACKAGE)); |
| |
| // The package of the installed overlay should not appear in the overlay manager list. |
| assertFalse(getDevice().executeShellCommand("cmd overlay list") |
| .contains(" " + OVERLAY_ALL_PACKAGE + "\n")); |
| } finally { |
| getDevice().uninstallPackage(OVERLAY_ALL_PACKAGE); |
| } |
| } |
| |
| /** |
| * Overlays that target Q or higher, that do not specify an android:targetName, and that are |
| * not signed with the same signature as the target package must not be installed. |
| **/ |
| @Test |
| public void testCannotInstallDifferentSignaturesNoName() throws Exception { |
| try { |
| getDevice().uninstallPackage(TARGET_PACKAGE); |
| getDevice().uninstallPackage(OVERLAY_ALL_PACKAGE); |
| assertFalse(getDevice().getInstalledPackageNames().contains(TARGET_PACKAGE)); |
| assertFalse(getDevice().getInstalledPackageNames().contains(OVERLAY_ALL_PACKAGE)); |
| |
| // Try to install the overlay, but expect an error. |
| new InstallMultiple().addFile(TARGET_NO_OVERLAYABLE_APK).run(); |
| new InstallMultiple().addFile( |
| OVERLAY_ALL_NO_NAME_DIFFERENT_CERT_APK).runExpectingFailure(); |
| |
| // The install should have failed. |
| assertFalse(getDevice().getInstalledPackageNames().contains(OVERLAY_ALL_PACKAGE)); |
| |
| // The package of the installed overlay should not appear in the overlay manager list. |
| assertFalse(getDevice().executeShellCommand("cmd overlay list --user current") |
| .contains(" " + OVERLAY_ALL_PACKAGE + "\n")); |
| } finally { |
| getDevice().uninstallPackage(OVERLAY_ALL_PACKAGE); |
| getDevice().uninstallPackage(TARGET_PACKAGE); |
| } |
| } |
| |
| /** |
| * Overlays that target Q or higher, that do not specify an android:targetName, and are |
| * installed before the target must not be allowed to successfully generate an idmap if the |
| * overlay is not signed with the same signature as the target package. |
| **/ |
| @Test |
| public void testFailIdmapDifferentSignaturesNoName() throws Exception { |
| assertFailToGenerateIdmap(OVERLAY_ALL_NO_NAME_APK, OVERLAY_ALL_PACKAGE); |
| } |
| |
| /** |
| * Overlays that target Q or higher, that do not specify an android:targetName, and are |
| * installed before the target must be allowed to successfully generate an idmap if the |
| * overlay is signed with the same signature as the target package. |
| **/ |
| @Test |
| public void testSameSignatureNoOverlayableSucceeds() throws Exception { |
| String testMethod = "testSameSignatureNoOverlayableSucceeds"; |
| runOverlayDeviceTest(TARGET_NO_OVERLAYABLE_APK, OVERLAY_ALL_NO_NAME_APK, |
| OVERLAY_ALL_PACKAGE, testMethod); |
| } |
| |
| /** |
| * Overlays installed on the data partition may only overlay resources defined under the public |
| * and signature policies if the overlay is signed with the same signature as the target. |
| */ |
| @Test |
| public void testOverlayPolicyAll() throws Exception { |
| String testMethod = "testOverlayPolicyAll"; |
| runOverlayDeviceTest(TARGET_OVERLAYABLE_APK, OVERLAY_ALL_APK, OVERLAY_ALL_PACKAGE, |
| testMethod); |
| } |
| |
| @Test |
| public void testOverlayCodeNotLoaded() throws Exception { |
| String testMethod = "testOverlayCodeNotLoaded"; |
| runOverlayDeviceTest(TARGET_OVERLAYABLE_APK, OVERLAY_ALL_HAS_CODE_APK, OVERLAY_ALL_PACKAGE, |
| testMethod); |
| } |
| |
| @Test |
| public void testOverlayPolicyAllNoNameFails() throws Exception { |
| assertFailToGenerateIdmap(OVERLAY_ALL_NO_NAME_APK, OVERLAY_ALL_PACKAGE); |
| } |
| |
| @Test |
| public void testOverlayPolicyProductFails() throws Exception { |
| assertFailToGenerateIdmap(OVERLAY_PRODUCT_APK, OVERLAY_PRODUCT_PACKAGE); |
| } |
| |
| @Test |
| public void testOverlayPolicySystemFails() throws Exception { |
| assertFailToGenerateIdmap(OVERLAY_SYSTEM_APK, OVERLAY_SYSTEM_PACKAGE); |
| } |
| |
| @Test |
| public void testOverlayPolicyVendorFails() throws Exception { |
| assertFailToGenerateIdmap(OVERLAY_VENDOR_APK, OVERLAY_VENDOR_PACKAGE); |
| } |
| |
| @Test |
| public void testOverlayPolicyDifferentSignatureFails() throws Exception { |
| assertFailToGenerateIdmap(OVERLAY_DIFFERENT_SIGNATURE_APK, |
| OVERLAY_DIFFERENT_SIGNATURE_PACKAGE); |
| } |
| |
| @Test |
| public void testFrameworkDoesNotDefineOverlayable() throws Exception { |
| String testMethod = "testFrameworkDoesNotDefineOverlayable"; |
| runDeviceTests(TEST_APP_PACKAGE, TEST_APP_CLASS, testMethod, false /* instant */); |
| } |
| |
| /** Overlays must not overlay assets. */ |
| @Test |
| public void testCannotOverlayAssets() throws Exception { |
| String testMethod = "testCannotOverlayAssets"; |
| runOverlayDeviceTest(TARGET_OVERLAYABLE_APK, OVERLAY_ALL_APK, OVERLAY_ALL_PACKAGE, |
| testMethod); |
| } |
| |
| @Test |
| public void testOverlayEnabled_activityInForeground() throws Exception { |
| final HashMap<String, String> testArgs = new HashMap<>(); |
| testArgs.put(PARAM_START_SERVICE, Boolean.FALSE.toString()); |
| try { |
| new InstallMultiple().addFile(OVERLAY_ALL_APK).run(); |
| new InstallMultiple().addFile(TARGET_OVERLAYABLE_APK).run(); |
| |
| runDeviceTests(TARGET_PACKAGE, OVERLAY_TARGET_TEST_APP_CLASS, |
| "overlayEnabled_activityInForeground", testArgs); |
| } finally { |
| getDevice().uninstallPackage(TARGET_PACKAGE); |
| getDevice().uninstallPackage(OVERLAY_ALL_PACKAGE); |
| } |
| } |
| |
| @Test |
| public void testOverlayEnabled_activityInBackground_toForeground() throws Exception { |
| final HashMap<String, String> testArgs = new HashMap<>(); |
| testArgs.put(PARAM_START_SERVICE, Boolean.FALSE.toString()); |
| try { |
| new InstallMultiple().addFile(OVERLAY_ALL_APK).run(); |
| new InstallMultiple().addFile(TARGET_OVERLAYABLE_APK).run(); |
| |
| runDeviceTests(TARGET_PACKAGE, OVERLAY_TARGET_TEST_APP_CLASS, |
| "overlayEnabled_activityInBackground_toForeground", testArgs); |
| } finally { |
| getDevice().uninstallPackage(TARGET_PACKAGE); |
| getDevice().uninstallPackage(OVERLAY_ALL_PACKAGE); |
| } |
| } |
| |
| @Test |
| public void testOverlayEnabled_activityWithServiceInForeground() throws Exception { |
| final HashMap<String, String> testArgs = new HashMap<>(); |
| testArgs.put(PARAM_START_SERVICE, Boolean.TRUE.toString()); |
| try { |
| new InstallMultiple().addFile(OVERLAY_ALL_APK).run(); |
| new InstallMultiple().addFile(TARGET_OVERLAYABLE_APK).run(); |
| |
| runDeviceTests(TARGET_PACKAGE, OVERLAY_TARGET_TEST_APP_CLASS, |
| "overlayEnabled_activityInForeground", testArgs); |
| } finally { |
| getDevice().uninstallPackage(TARGET_PACKAGE); |
| getDevice().uninstallPackage(OVERLAY_ALL_PACKAGE); |
| } |
| } |
| |
| @Test |
| public void testOverlayEnabled_activityWithServiceInBackground_toForeground() throws Exception { |
| final HashMap<String, String> testArgs = new HashMap<>(); |
| testArgs.put(PARAM_START_SERVICE, Boolean.TRUE.toString()); |
| try { |
| new InstallMultiple().addFile(OVERLAY_ALL_APK).run(); |
| new InstallMultiple().addFile(TARGET_OVERLAYABLE_APK).run(); |
| |
| runDeviceTests(TARGET_PACKAGE, OVERLAY_TARGET_TEST_APP_CLASS, |
| "overlayEnabled_activityInBackground_toForeground", testArgs); |
| } finally { |
| getDevice().uninstallPackage(TARGET_PACKAGE); |
| getDevice().uninstallPackage(OVERLAY_ALL_PACKAGE); |
| } |
| } |
| } |