blob: 6a2f7f88a37edde91824323144cad825584a90f2 [file] [log] [blame]
/*
* 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);
}
}
}