blob: 91ec15967f6305130c3c48f1ccde8c76d334fed6 [file] [log] [blame]
/*
* Copyright (C) 2016 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.assertNotNull;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import android.platform.test.annotations.AppModeFull;
import android.platform.test.annotations.AsbSecurityTest;
import com.android.tradefed.log.LogUtil.CLog;
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.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Tests for ephemeral packages.
*/
@RunWith(DeviceJUnit4ClassRunner.class)
@AppModeFull(reason = "Already handles instant installs when needed")
public class EphemeralTest extends BaseAppSecurityTest {
// a normally installed application
private static final String NORMAL_APK = "CtsEphemeralTestsNormalApp.apk";
private static final String NORMAL_PKG = "com.android.cts.normalapp";
// the first ephemerally installed application
private static final String EPHEMERAL_1_APK = "CtsEphemeralTestsEphemeralApp1.apk";
private static final String EPHEMERAL_1_PKG = "com.android.cts.ephemeralapp1";
// the second ephemerally installed application
private static final String EPHEMERAL_2_APK = "CtsEphemeralTestsEphemeralApp2.apk";
private static final String EPHEMERAL_2_PKG = "com.android.cts.ephemeralapp2";
// a normally installed application with implicitly exposed components
private static final String IMPLICIT_APK = "CtsEphemeralTestsImplicitApp.apk";
private static final String IMPLICIT_PKG = "com.android.cts.implicitapp";
// a normally installed application with no exposed components
private static final String UNEXPOSED_APK = "CtsEphemeralTestsUnexposedApp.apk";
private static final String UNEXPOSED_PKG = "com.android.cts.unexposedapp";
// an application that gets upgraded from 'instant' to 'full'
private static final String UPGRADED_APK = "CtsInstantUpgradeApp.apk";
private static final String UPGRADED_PKG = "com.android.cts.instantupgradeapp";
private static final String TEST_CLASS = ".ClientTest";
private static final String WEBVIEW_TEST_CLASS = ".WebViewTest";
private static final List<Map<String, String>> EXPECTED_EXPOSED_INTENTS =
new ArrayList<>();
static {
// Framework intents we expect the system to expose to instant applications
EXPECTED_EXPOSED_INTENTS.add(makeArgs(
"android.intent.action.CHOOSER",
null, null));
// Contact intents we expect the system to expose to instant applications
EXPECTED_EXPOSED_INTENTS.add(makeArgs(
"android.intent.action.PICK", null, "vnd.android.cursor.dir/contact"));
EXPECTED_EXPOSED_INTENTS.add(makeArgs(
"android.intent.action.PICK", null, "vnd.android.cursor.dir/phone_v2"));
EXPECTED_EXPOSED_INTENTS.add(makeArgs(
"android.intent.action.PICK", null, "vnd.android.cursor.dir/email_v2"));
EXPECTED_EXPOSED_INTENTS.add(makeArgs(
"android.intent.action.PICK", null, "vnd.android.cursor.dir/postal-address_v2"));
// Storage intents we expect the system to expose to instant applications
EXPECTED_EXPOSED_INTENTS.add(makeArgs(
"android.intent.action.OPEN_DOCUMENT",
"android.intent.category.OPENABLE", "\\*/\\*"));
EXPECTED_EXPOSED_INTENTS.add(makeArgs(
"android.intent.action.OPEN_DOCUMENT", null, "\\*/\\*"));
EXPECTED_EXPOSED_INTENTS.add(makeArgs(
"android.intent.action.GET_CONTENT",
"android.intent.category.OPENABLE", "\\*/\\*"));
EXPECTED_EXPOSED_INTENTS.add(makeArgs(
"android.intent.action.GET_CONTENT", null, "\\*/\\*"));
EXPECTED_EXPOSED_INTENTS.add(makeArgs(
"android.intent.action.OPEN_DOCUMENT_TREE", null, null));
EXPECTED_EXPOSED_INTENTS.add(makeArgs(
"android.intent.action.CREATE_DOCUMENT",
"android.intent.category.OPENABLE", "text/plain"));
EXPECTED_EXPOSED_INTENTS.add(makeArgs(
"android.intent.action.CREATE_DOCUMENT", null, "text/plain"));
/** Camera intents we expect the system to expose to instant applications */
EXPECTED_EXPOSED_INTENTS.add(makeArgs(
"android.media.action.IMAGE_CAPTURE", null, null));
EXPECTED_EXPOSED_INTENTS.add(makeArgs(
"android.media.action.VIDEO_CAPTURE", null, null));
}
private String mOldVerifierValue;
private Boolean mIsUnsupportedDevice;
@Before
public void setUp() throws Exception {
mIsUnsupportedDevice = isDeviceUnsupported();
if (mIsUnsupportedDevice) {
return;
}
Utils.prepareSingleUser(getDevice());
assertNotNull(getAbi());
assertNotNull(getBuild());
uninstallTestPackages();
installTestPackages();
}
@After
public void tearDown() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
uninstallTestPackages();
}
@Test
public void testNormalQuery() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), NORMAL_PKG, TEST_CLASS, "testQuery");
}
@Test
public void testNormalStartNormal() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), NORMAL_PKG, TEST_CLASS, "testStartNormal");
}
@Test
public void testNormalStartEphemeral() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), NORMAL_PKG, TEST_CLASS,
"testStartEphemeral");
}
@Test
public void testEphemeralQuery() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS, "testQuery");
}
@Test
public void testEphemeralStartNormal() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testStartNormal");
}
// each connection to an exposed component needs to run in its own test to
// avoid sharing state. once an instant app is exposed to a component, it's
// exposed until the device restarts or the instant app is removed.
@Test
public void testEphemeralStartExposed01() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testStartExposed01");
}
@Test
public void testEphemeralStartExposed02() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testStartExposed02");
}
@Test
public void testEphemeralStartExposed03() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testStartExposed03");
}
@Test
public void testEphemeralStartExposed04() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testStartExposed04");
}
@Test
public void testEphemeralStartExposed05() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testStartExposed05");
}
@Test
public void testEphemeralStartExposed06() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testStartExposed06");
}
@Test
public void testEphemeralStartExposed07() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testStartExposed07");
}
@Test
public void testEphemeralStartExposed08() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testStartExposed08");
}
@Test
public void testEphemeralStartExposed09() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testStartExposed09");
}
@Test
public void testEphemeralStartExposed10() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testStartExposed10");
}
@Test
public void testEphemeralStartExposed11() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testStartExposed11");
}
@Test
public void testEphemeralStartEphemeral() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testStartEphemeral");
}
@Test
public void testEphemeralGetInstaller01() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
installEphemeralApp(EPHEMERAL_1_APK, "com.android.cts.normalapp");
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testGetInstaller01");
}
@Test
public void testEphemeralGetInstaller02() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
installApp(NORMAL_APK, "com.android.cts.normalapp");
installEphemeralApp(EPHEMERAL_1_APK, "com.android.cts.normalapp");
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testGetInstaller02");
}
@Test
public void testEphemeralGetInstaller03() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
installApp(NORMAL_APK, "com.android.cts.normalapp");
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testGetInstaller03");
}
@Test
public void testExposedSystemActivities() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
for (Map<String, String> testArgs : EXPECTED_EXPOSED_INTENTS) {
final boolean exposed = isIntentExposed(testArgs);
if (exposed) {
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testExposedActivity", testArgs);
} else {
CLog.w("Skip intent; " + dumpArgs(testArgs));
}
}
}
@Test
public void testBuildSerialUnknown() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testBuildSerialUnknown");
}
@Test
public void testPackageInfo() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testPackageInfo");
}
@Test
public void testActivityInfo() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testActivityInfo");
}
@Test
public void testWebViewLoads() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, WEBVIEW_TEST_CLASS,
"testWebViewLoads");
}
@Test
public void testInstallPermissionNotGranted() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testInstallPermissionNotGranted");
}
@Test
public void testInstallPermissionGranted() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testInstallPermissionGranted");
}
@Test
@AsbSecurityTest(cveBugId = 140256621)
public void testInstallPermissionNotGrantedInPackageInfo() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testInstallPermissionNotGrantedInPackageInfo");
}
@Test
@AsbSecurityTest(cveBugId = 140256621)
public void testInstallPermissionGrantedInPackageInfo() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testInstallPermissionGrantedInPackageInfo");
}
/** Test for android.permission.INSTANT_APP_FOREGROUND_SERVICE */
@Test
public void testStartForegroundService() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
// Make sure the test package does not have INSTANT_APP_FOREGROUND_SERVICE
getDevice().executeShellCommand("cmd package revoke --user cur " + EPHEMERAL_1_PKG
+ " android.permission.INSTANT_APP_FOREGROUND_SERVICE");
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testStartForegroundService");
}
/** Test for android.permission.RECORD_AUDIO */
@Test
public void testRecordAudioPermission() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testRecordAudioPermission");
}
/** Test for android.permission.CAMERA */
@Test
public void testCameraPermission() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testCameraPermission");
}
/** Test for android.permission.READ_PHONE_NUMBERS */
@Test
public void testReadPhoneNumbersPermission() throws Exception {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testReadPhoneNumbersPermission");
}
/** Test for android.permission.ACCESS_COARSE_LOCATION */
@Test
public void testAccessCoarseLocationPermission() throws Throwable {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testAccessCoarseLocationPermission");
}
/** Test for android.permission.NETWORK */
@Test
public void testInternetPermission() throws Throwable {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testInternetPermission");
}
/** Test for android.permission.VIBRATE */
@Test
public void testVibratePermission() throws Throwable {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testVibratePermission");
}
/** Test for android.permission.WAKE_LOCK */
@Test
public void testWakeLockPermission() throws Throwable {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testWakeLockPermission");
}
/** Test for search manager */
@Test
public void testGetSearchableInfo() throws Throwable {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testGetSearchableInfo");
}
/** Test for upgrade from instant --> full */
@Test
public void testInstantAppUpgrade() throws Throwable {
if (mIsUnsupportedDevice) {
return;
}
installEphemeralApp(UPGRADED_APK);
Utils.runDeviceTestsAsCurrentUser(getDevice(), UPGRADED_PKG, TEST_CLASS,
"testInstantApplicationWritePreferences");
Utils.runDeviceTestsAsCurrentUser(getDevice(), UPGRADED_PKG, TEST_CLASS,
"testInstantApplicationWriteFile");
installFullApp(UPGRADED_APK);
Utils.runDeviceTestsAsCurrentUser(getDevice(), UPGRADED_PKG, TEST_CLASS,
"testFullApplicationReadPreferences");
Utils.runDeviceTestsAsCurrentUser(getDevice(), UPGRADED_PKG, TEST_CLASS,
"testFullApplicationReadFile");
}
@Test
public void testGetChangedPackages() throws Throwable {
if (mIsUnsupportedDevice) {
return;
}
Utils.runDeviceTestsAsCurrentUser(getDevice(), NORMAL_PKG, TEST_CLASS,
"testGetChangedPackages");
Utils.runDeviceTestsAsCurrentUser(getDevice(), EPHEMERAL_1_PKG, TEST_CLASS,
"testGetChangedPackages");
}
@Test
public void uninstall_userInstalledApp_shouldBeUserInitiated() throws Throwable {
assumeFalse("Device does not support instant app", mIsUnsupportedDevice);
installEphemeralApp(EPHEMERAL_1_APK, NORMAL_PKG);
Utils.runDeviceTestsAsCurrentUser(getDevice(), NORMAL_PKG, TEST_CLASS,
"uninstall_userInstalledApp_shouldBeUserInitiated");
}
@Test
public void uninstall_pruneInstantApp_shouldNotBeUserInitiated()
throws Throwable {
assumeFalse("Device does not support instant app", mIsUnsupportedDevice);
Utils.runDeviceTestsAsCurrentUser(getDevice(), NORMAL_PKG, TEST_CLASS,
"uninstall_pruneInstantApp_shouldNotBeUserInitiated");
}
private static final HashMap<String, String> makeArgs(
String action, String category, String mimeType) {
if (action == null || action.length() == 0) {
fail("action not specified");
}
final HashMap<String, String> testArgs = new HashMap<>();
testArgs.put("action", action);
if (category != null && category.length() > 0) {
testArgs.put("category", category);
}
if (mimeType != null && mimeType.length() > 0) {
testArgs.put("mime_type", mimeType);
}
return testArgs;
}
private static final String[] sUnsupportedFeatures = {
"feature:android.hardware.type.watch",
"android.hardware.type.embedded",
};
private boolean isDeviceUnsupported() throws Exception {
for (String unsupportedFeature : sUnsupportedFeatures) {
if (getDevice().hasFeature(unsupportedFeature)) {
return true;
}
}
return false;
}
private boolean isIntentExposed(Map<String, String> testArgs)
throws Exception {
final StringBuffer command = new StringBuffer();
command.append("cmd package query-activities");
command.append(" -a ").append(testArgs.get("action"));
if (testArgs.get("category") != null) {
command.append(" -c ").append(testArgs.get("category"));
}
if (testArgs.get("mime_type") != null) {
command.append(" -t ").append(testArgs.get("mime_type"));
}
final String output = getDevice().executeShellCommand(command.toString()).trim();
return !"No activities found".equals(output);
}
private static final String dumpArgs(Map<String, String> testArgs) {
final StringBuffer dump = new StringBuffer();
dump.append("action: ").append(testArgs.get("action"));
if (testArgs.get("category") != null) {
dump.append(", category: ").append(testArgs.get("category"));
}
if (testArgs.get("mime_type") != null) {
dump.append(", type: ").append(testArgs.get("mime_type"));
}
return dump.toString();
}
private void installApp(String apk) throws Exception {
new InstallMultiple(false /* instant */)
.addFile(apk)
.run();
}
private void installApp(String apk, String installer) throws Exception {
new InstallMultiple(false /* instant */)
.addFile(apk)
.addArg("-i " + installer)
.run();
}
private void installEphemeralApp(String apk) throws Exception {
new InstallMultiple(true /* instant */)
.addFile(apk)
.run();
}
private void installEphemeralApp(String apk, String installer) throws Exception {
new InstallMultiple(true /* instant */)
.addFile(apk)
.addArg("-i " + installer)
.run();
}
private void installFullApp(String apk) throws Exception {
new InstallMultiple(false /* instant */)
.addFile(apk)
.addArg("--full")
.run();
}
private void installTestPackages() throws Exception {
installApp(NORMAL_APK);
installApp(UNEXPOSED_APK);
installApp(IMPLICIT_APK);
installEphemeralApp(EPHEMERAL_1_APK);
installEphemeralApp(EPHEMERAL_2_APK);
}
private void uninstallTestPackages() throws Exception {
uninstallPackage(NORMAL_PKG);
uninstallPackage(UNEXPOSED_PKG);
uninstallPackage(IMPLICIT_PKG);
uninstallPackage(EPHEMERAL_1_PKG);
uninstallPackage(EPHEMERAL_2_PKG);
uninstallPackage(UPGRADED_PKG);
}
}