blob: 96d5713759485129f3edaa24e7cf0049f5b7073b [file] [log] [blame]
/*
* Copyright (C) 2014 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 com.android.cts.devicepolicy;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
import com.android.role.RoleProto;
import com.android.role.RoleServiceDumpProto;
import com.android.role.RoleUserStateProto;
import com.android.tradefed.config.Option;
import com.android.tradefed.device.CollectingByteOutputReceiver;
import com.android.tradefed.device.CollectingOutputReceiver;
import com.android.tradefed.device.DeviceNotAvailableException;
import com.android.tradefed.device.ITestDevice;
import com.android.tradefed.log.LogUtil.CLog;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
import com.google.common.base.Strings;
import com.google.common.io.ByteStreams;
import org.junit.After;
import org.junit.AssumptionViolatedException;
import org.junit.Before;
import org.junit.Rule;
import org.junit.runner.RunWith;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
/**
* Base class for device policy tests. It offers utility methods to run tests, set device or profile
* owner, etc.
*/
@RunWith(DeviceJUnit4ClassRunner.class)
public abstract class BaseDevicePolicyTest extends BaseHostJUnit4Test {
private static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
private static final String FEATURE_CAMERA = "android.hardware.camera";
private static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
private static final String FEATURE_FBE = "android.software.file_based_encryption";
private static final String FEATURE_LEANBACK = "android.software.leanback";
private static final String FEATURE_NFC = "android.hardware.nfc";
private static final String FEATURE_NFC_BEAM = "android.software.nfc.beam";
private static final String FEATURE_PRINT = "android.software.print";
private static final String FEATURE_TELEPHONY = "android.hardware.telephony";
private static final String FEATURE_SECURE_LOCK_SCREEN = "android.software.secure_lock_screen";
private static final String FEATURE_WIFI = "android.hardware.wifi";
//The maximum time to wait for user to be unlocked.
private static final long USER_UNLOCK_TIMEOUT_SEC = 30;
private static final String USER_STATE_UNLOCKED = "RUNNING_UNLOCKED";
protected static final String PERMISSION_INTERACT_ACROSS_USERS =
"android.permission.INTERACT_ACROSS_USERS";
@Option(
name = "skip-device-admin-feature-check",
description = "Flag that allows to skip the check for android.software.device_admin "
+ "and run the tests no matter what. This is useful for system that do not what "
+ "to expose that feature publicly."
)
private boolean mSkipDeviceAdminFeatureCheck = false;
private static final String RUNNER = "androidx.test.runner.AndroidJUnitRunner";
protected static final int USER_SYSTEM = 0; // From the UserHandle class.
protected static final int USER_OWNER = USER_SYSTEM;
private static final long TIMEOUT_USER_REMOVED_MILLIS = TimeUnit.SECONDS.toMillis(15);
private static final long WAIT_SAMPLE_INTERVAL_MILLIS = 200;
/**
* The defined timeout (in milliseconds) is used as a maximum waiting time when expecting the
* command output from the device. At any time, if the shell command does not output anything
* for a period longer than defined timeout the Tradefed run terminates.
*/
private static final long DEFAULT_SHELL_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(20);
/**
* Sets timeout (in milliseconds) that will be applied to each test. In the
* event of a test timeout it will log the results and proceed with executing the next test.
*/
private static final long DEFAULT_TEST_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(10);
/**
* The amount of milliseconds to wait for the remove user calls in {@link #tearDown}.
* This is a temporary measure until b/114057686 is fixed.
*/
private static final long USER_REMOVE_WAIT = TimeUnit.SECONDS.toMillis(5);
/**
* The amount of milliseconds to wait for the switch user calls in {@link #tearDown}.
*/
private static final long USER_SWITCH_WAIT = TimeUnit.SECONDS.toMillis(5);
// From the UserInfo class
protected static final int FLAG_GUEST = 0x00000004;
protected static final int FLAG_EPHEMERAL = 0x00000100;
protected static final int FLAG_MANAGED_PROFILE = 0x00000020;
/** Default password to use in tests. */
protected static final String TEST_PASSWORD = "1234";
/**
* The {@link android.os.BatteryManager} flags value representing all charging types; {@link
* android.os.BatteryManager#BATTERY_PLUGGED_AC}, {@link
* android.os.BatteryManager#BATTERY_PLUGGED_USB}, and {@link
* android.os.BatteryManager#BATTERY_PLUGGED_WIRELESS}.
*/
private static final int STAY_ON_WHILE_PLUGGED_IN_FLAGS = 7;
/**
* User ID for all users.
* The value is from the UserHandle class.
*/
protected static final int USER_ALL = -1;
private static final String TEST_UPDATE_LOCATION = "/data/local/tmp/cts/deviceowner";
/**
* Copied from {@link android.app.admin.DevicePolicyManager
* .InstallSystemUpdateCallback#UPDATE_ERROR_UPDATE_FILE_INVALID}
*/
protected static final int UPDATE_ERROR_UPDATE_FILE_INVALID = 3;
protected CompatibilityBuildHelper mBuildHelper;
private String mPackageVerifier;
private HashSet<String> mAvailableFeatures;
/** Packages installed as part of the tests */
private Set<String> mFixedPackages;
protected int mDeviceOwnerUserId;
protected int mPrimaryUserId;
/** Record the initial user ID. */
protected int mInitialUserId;
/** Whether multi-user is supported. */
private boolean mSupportsMultiUser;
/** Users we shouldn't delete in the tests */
private ArrayList<Integer> mFixedUsers;
private static final String VERIFY_CREDENTIAL_CONFIRMATION = "Lock credential verified";
@Rule
public final DeviceAdminFeaturesCheckerRule mFeaturesCheckerRule =
new DeviceAdminFeaturesCheckerRule(this);
@Before
public void setUp() throws Exception {
assertNotNull(getBuild()); // ensure build has been set before test is run.
if (!mSkipDeviceAdminFeatureCheck) {
// TODO(b/177965931): STOPSHIP must integrate mSkipDeviceAdminFeatureCheck into
// DeviceAdminFeaturesCheckerRule
}
mSupportsMultiUser = getMaxNumberOfUsersSupported() > 1;
mFixedPackages = getDevice().getInstalledPackageNames();
mBuildHelper = new CompatibilityBuildHelper(getBuild());
if (hasDeviceFeature(FEATURE_SECURE_LOCK_SCREEN)) {
ensurePrimaryUserHasNoPassword();
}
// disable the package verifier to avoid the dialog when installing an app
mPackageVerifier = getDevice().executeShellCommand(
"settings get global verifier_verify_adb_installs");
getDevice().executeShellCommand("settings put global verifier_verify_adb_installs 0");
mFixedUsers = new ArrayList<>();
// Set the value of initial user ID calls in {@link #setUp}.
if(mSupportsMultiUser) {
mInitialUserId = getDevice().getCurrentUser();
}
if (!isHeadlessSystemUserMode()) {
mDeviceOwnerUserId = mPrimaryUserId = getPrimaryUser();
} else {
// For headless system user, all tests will be executed on current user
// and therefore, initial user is set as primary user for test purpose.
mPrimaryUserId = mInitialUserId;
mDeviceOwnerUserId = USER_SYSTEM;
}
mFixedUsers.add(mPrimaryUserId);
if (mPrimaryUserId != USER_SYSTEM) {
mFixedUsers.add(USER_SYSTEM);
}
if (mFeaturesCheckerRule.hasRequiredFeatures()) {
// Switching to primary is only needed when we're testing device admin features.
switchUser(mPrimaryUserId);
} else {
// Otherwise, all the tests can be executed in any of the Android users, so remain in
// current user, and don't delete it. This enables testing in secondary users.
if (getDevice().getCurrentUser() != mPrimaryUserId) {
mFixedUsers.add(getDevice().getCurrentUser());
}
}
getDevice().executeShellCommand(" mkdir " + TEST_UPDATE_LOCATION);
removeOwners();
switchUser(mPrimaryUserId);
removeTestUsers();
// Unlock keyguard before test
wakeupAndDismissKeyguard();
stayAwake();
// Go to home.
executeShellCommand("input keyevent KEYCODE_HOME");
}
private void ensurePrimaryUserHasNoPassword() throws DeviceNotAvailableException {
if (!verifyUserCredentialIsCorrect(null, mPrimaryUserId)) {
changeUserCredential(null, TEST_PASSWORD, mPrimaryUserId);
}
}
/** If package manager is not available, e.g. after system crash, wait for it a little bit. */
private void ensurePackageManagerReady() throws Exception {
waitForOutput("Package manager didn't become available", "service check package",
s -> s.trim().equals("Service package: found"), 120 /* seconds */);
}
protected void waitForUserUnlock(int userId) throws Exception {
waitForOutput("User is not unlocked.",
String.format("am get-started-user-state %d", userId),
s -> s.startsWith(USER_STATE_UNLOCKED), USER_UNLOCK_TIMEOUT_SEC);
}
protected void waitForOutput(String message, String command, Predicate<String> predicate,
long timeoutSec) throws Exception {
final long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(timeoutSec);
while (!predicate.test(getDevice().executeShellCommand(command))) {
if (System.nanoTime() > deadline) {
fail(message);
}
Thread.sleep(1000);
}
}
@After
public void tearDown() throws Exception {
// reset the package verifier setting to its original value
getDevice().executeShellCommand("settings put global verifier_verify_adb_installs "
+ mPackageVerifier);
removeOwners();
// Switch back to initial user.
if (mSupportsMultiUser && getDevice().getCurrentUser() != mInitialUserId) {
switchUser(mInitialUserId);
}
removeTestUsers();
removeTestPackages();
getDevice().executeShellCommand(" rm -r " + TEST_UPDATE_LOCATION);
}
protected void installAppAsUser(String appFileName, int userId) throws FileNotFoundException,
DeviceNotAvailableException {
installAppAsUser(appFileName, true, userId);
}
protected void installAppAsUser(String appFileName, boolean grantPermissions, int userId)
throws FileNotFoundException, DeviceNotAvailableException {
installAppAsUser(appFileName, grantPermissions, /* dontKillApp */ false, userId);
}
protected void installAppAsUser(String appFileName, boolean grantPermissions,
boolean dontKillApp, int userId)
throws FileNotFoundException, DeviceNotAvailableException {
CLog.e("Installing app %s for user %d", appFileName, userId);
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
List<String> extraArgs = new LinkedList<>();
extraArgs.add("-t");
// Make the test app queryable by other apps via PackageManager APIs.
extraArgs.add("--force-queryable");
if (dontKillApp) extraArgs.add("--dont-kill");
String result = getDevice().installPackageForUser(
buildHelper.getTestFile(appFileName), true, grantPermissions, userId,
extraArgs.toArray(new String[extraArgs.size()]));
assertNull("Failed to install " + appFileName + " for user " + userId + ": " + result,
result);
}
protected void installAppIncremental(String appFileName)
throws FileNotFoundException, DeviceNotAvailableException {
final String signatureSuffix = ".idsig";
CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
final File apk = buildHelper.getTestFile(appFileName);
assertNotNull(apk);
final File idsig = buildHelper.getTestFile(appFileName + signatureSuffix);
assertNotNull(idsig);
final String remoteApkPath = TEST_UPDATE_LOCATION + "/" + apk.getName();
final String remoteIdsigPath = remoteApkPath + signatureSuffix;
assertTrue(getDevice().pushFile(apk, remoteApkPath));
assertTrue(getDevice().pushFile(idsig, remoteIdsigPath));
String installResult = getDevice().executeShellCommand(
"pm install-incremental -t -g " + remoteApkPath);
assertEquals("Success\n", installResult);
}
protected void installDeviceOwnerApp(String apk) throws Exception {
installAppAsUser(apk, mDeviceOwnerUserId);
if (isHeadlessSystemUserMode()) {
// Need to explicitly install the device owner app for the current user (rather than
// relying on DPMS) so it has the same privileges (like INTERACT_ACROSS_USERS) as the
// app running on system user, otherwise some tests might fail
installAppAsUser(apk, mPrimaryUserId);
}
}
protected void removeDeviceOwnerAdmin(String componentName) throws DeviceNotAvailableException {
// Don't fail as it could hide the real failure from the test method
if (!removeAdmin(componentName, mDeviceOwnerUserId)) {
CLog.e("Failed to remove device owner %s on user %d", componentName,
mDeviceOwnerUserId);
}
if (isHeadlessSystemUserMode() && !removeAdmin(componentName, mPrimaryUserId)) {
CLog.e("Failed to remove profile owner %s on user %d", componentName, mPrimaryUserId);
}
}
protected void forceStopPackageForUser(String packageName, int userId) throws Exception {
// TODO Move this logic to ITestDevice
executeShellCommand("am force-stop --user " + userId + " " + packageName);
}
protected String executeShellCommand(String commandTemplate, Object...args) throws Exception {
return executeShellCommand(String.format(commandTemplate, args));
}
protected String executeShellCommand(String command) throws Exception {
CLog.d("Starting command %s", command);
String commandOutput = getDevice().executeShellCommand(command);
CLog.d("Output for command %s: %s", command, commandOutput);
return commandOutput;
}
/** Initializes the user with the given id. This is required so that apps can run on it. */
protected void startUser(int userId) throws Exception {
CLog.d("Starting user %d", userId);
getDevice().startUser(userId);
}
/** Initializes the user with waitFlag. This is required so that apps can run on it. */
protected void startUserAndWait(int userId) throws Exception {
CLog.d("Starting user %d and waiting", userId);
getDevice().startUser(userId, /* waitFlag= */ true);
}
/**
* Initializes the user with the given id, and waits until the user has started and unlocked
* before continuing.
*
* <p>This is required so that apps can run on it.
*/
protected void startUser(int userId, boolean waitFlag) throws Exception {
getDevice().startUser(userId, waitFlag);
}
/**
* Starts switching to the user with the given ID.
*
* <p>This is not blocking. Some operations will be flaky if called immediately afterwards, such
* as {@link #wakeupAndDismissKeyguard()}. Call {@link #waitForBroadcastIdle()} between this
* method and those operations to ensure that switching the user has finished.
*/
protected void switchUser(int userId) throws Exception {
// TODO Move this logic to ITestDevice
int retries = 10;
CLog.i("switching to user %d", userId);
executeShellCommand("am switch-user " + userId);
while (getDevice().getCurrentUser() != userId && (--retries) >= 0) {
// am switch-user can be ignored if a previous user-switching operation
// is still in progress. In this case, sleep a bit and then retry
Thread.sleep(USER_SWITCH_WAIT);
executeShellCommand("am switch-user " + userId);
}
assertTrue("Failed to switch user after multiple retries", getDevice().getCurrentUser() == userId);
}
protected int getMaxNumberOfUsersSupported() throws DeviceNotAvailableException {
return getDevice().getMaxNumberOfUsersSupported();
}
protected int getMaxNumberOfRunningUsersSupported() throws DeviceNotAvailableException {
return getDevice().getMaxNumberOfRunningUsersSupported();
}
protected int getUserFlags(int userId) throws DeviceNotAvailableException {
String command = "pm list users";
String commandOutput = getDevice().executeShellCommand(command);
CLog.i("Output for command " + command + ": " + commandOutput);
String[] lines = commandOutput.split("\\r?\\n");
assertTrue(commandOutput + " should contain at least one line", lines.length >= 1);
for (int i = 1; i < lines.length; i++) {
// Individual user is printed out like this:
// \tUserInfo{$id$:$name$:$Integer.toHexString(flags)$} [running]
String[] tokens = lines[i].split("\\{|\\}|:");
assertTrue(lines[i] + " doesn't contain 4 or 5 tokens",
tokens.length == 4 || tokens.length == 5);
// If the user IDs match, return the flags.
if (Integer.parseInt(tokens[1]) == userId) {
return Integer.parseInt(tokens[3], 16);
}
}
fail("User not found");
return 0;
}
protected ArrayList<Integer> listUsers() throws DeviceNotAvailableException {
return getDevice().listUsers();
}
protected ArrayList<Integer> listRunningUsers() throws DeviceNotAvailableException {
ArrayList<Integer> runningUsers = new ArrayList<>();
for (int userId : listUsers()) {
if (getDevice().isUserRunning(userId)) {
runningUsers.add(userId);
}
}
return runningUsers;
}
protected int getFirstManagedProfileUserId() throws DeviceNotAvailableException {
for (int userId : listUsers()) {
if ((getUserFlags(userId) & FLAG_MANAGED_PROFILE) != 0) {
return userId;
}
}
fail("Managed profile not found");
return 0;
}
private void stopUserAsync(int userId) throws Exception {
String stopUserCommand = "am stop-user -f " + userId;
CLog.d("starting command \"" + stopUserCommand);
CLog.d("Output for command " + stopUserCommand + ": "
+ getDevice().executeShellCommand(stopUserCommand));
}
protected void stopUser(int userId) throws Exception {
String stopUserCommand = "am stop-user -w -f " + userId;
CLog.d("starting command \"" + stopUserCommand + "\" and waiting.");
CLog.d("Output for command " + stopUserCommand + ": "
+ getDevice().executeShellCommand(stopUserCommand));
}
protected void waitForBroadcastIdle() throws DeviceNotAvailableException, IOException {
final CollectingOutputReceiver receiver = new CollectingOutputReceiver();
// We allow 8min for the command to complete and 4min for the command to start to
// output something.
getDevice().executeShellCommand(
"am wait-for-broadcast-idle", receiver, 8, 4, TimeUnit.MINUTES, 0);
final String output = receiver.getOutput();
if (!output.contains("All broadcast queues are idle!")) {
CLog.e("Output from 'am wait-for-broadcast-idle': %s", output);
fail("'am wait-for-broadcase-idle' did not complete.");
}
}
protected void removeUser(int userId) throws Exception {
if (listUsers().contains(userId) && userId != USER_SYSTEM) {
// Don't log output, as tests sometimes set no debug user restriction, which
// causes this to fail, we should still continue and remove the user.
String stopUserCommand = "am stop-user -w -f " + userId;
CLog.d("stopping and removing user " + userId);
getDevice().executeShellCommand(stopUserCommand);
// TODO: Remove both sleeps and USER_REMOVE_WAIT constant when b/114057686 is fixed.
Thread.sleep(USER_REMOVE_WAIT);
// Ephemeral users may have already been removed after being stopped.
if (listUsers().contains(userId)) {
assertTrue("Couldn't remove user", getDevice().removeUser(userId));
Thread.sleep(USER_REMOVE_WAIT);
}
}
}
protected void removeTestUsers() throws Exception {
List<Integer> usersCreatedByTests = getUsersCreatedByTests();
// The time spent on stopUser is depend on how busy the broadcast queue is.
// To optimize the time to remove multiple test users, we mark all users as
// stopping first, so no more broadcasts will be sent to these users, which make the queue
// less busy.
for (int userId : usersCreatedByTests) {
stopUserAsync(userId);
}
for (int userId : usersCreatedByTests) {
removeTestAddedUser(userId);
}
}
private void removeTestAddedUser(int userId) throws Exception {
// Don't remove system user or initial user.
if (userId != USER_SYSTEM && userId != mInitialUserId) {
removeUser(userId);
}
}
/**
* Returns the users that have been created since running this class' setUp() method.
*/
protected List<Integer> getUsersCreatedByTests() throws Exception {
List<Integer> result = listUsers();
result.removeAll(mFixedUsers);
return result;
}
/** Removes any packages that were installed during the test. */
protected void removeTestPackages() throws Exception {
for (String packageName : getDevice().getUninstallablePackageNames()) {
if (mFixedPackages.contains(packageName)) {
continue;
}
CLog.w("removing leftover package: " + packageName);
getDevice().uninstallPackage(packageName);
}
}
protected void runDeviceTestsAsUser(
String pkgName, @Nullable String testClassName, int userId)
throws DeviceNotAvailableException {
runDeviceTestsAsUser(pkgName, testClassName, /* testMethodName= */ null, userId);
}
protected void runDeviceTestsAsUser(
String pkgName, @Nullable String testClassName, String testMethodName, int userId)
throws DeviceNotAvailableException {
Map<String, String> params = Collections.emptyMap();
runDeviceTestsAsUser(pkgName, testClassName, testMethodName, userId, params);
}
protected void runDeviceTestsAsUser(
String pkgName, @Nullable String testClassName,
@Nullable String testMethodName, int userId,
Map<String, String> params) throws DeviceNotAvailableException {
if (testClassName != null && testClassName.startsWith(".")) {
testClassName = pkgName + testClassName;
}
CLog.i("runDeviceTestsAsUser(): user=%d, pkg=%s class=%s, test=%s", userId, pkgName,
testClassName, testMethodName);
runDeviceTests(
getDevice(),
RUNNER,
pkgName,
testClassName,
testMethodName,
userId,
DEFAULT_TEST_TIMEOUT_MILLIS,
DEFAULT_SHELL_TIMEOUT_MILLIS,
0L /* maxInstrumentationTimeoutMs */,
true /* checkResults */,
false /* isHiddenApiCheckDisabled */,
params);
}
/** Reboots the device and block until the boot complete flag is set. */
protected void rebootAndWaitUntilReady() throws Exception {
getDevice().rebootUntilOnline();
assertTrue("Device failed to boot", getDevice().waitForBootComplete(120_000));
}
/** Returns a boolean value of the system property with the specified key. */
protected boolean getBooleanSystemProperty(String key, boolean defaultValue)
throws DeviceNotAvailableException {
final String[] positiveValues = {"1", "y", "yes", "true", "on"};
final String[] negativeValues = {"0", "n", "no", "false", "off"};
String propertyValue = getDevice().getProperty(key);
if (propertyValue == null || propertyValue.isEmpty()) {
return defaultValue;
}
if (Arrays.asList(positiveValues).contains(propertyValue)) {
return true;
}
if (Arrays.asList(negativeValues).contains(propertyValue)) {
return false;
}
fail("Unexpected value of boolean system property '" + key + "': " + propertyValue);
return false;
}
/** Checks whether it is possible to create the desired number of users. */
protected boolean canCreateAdditionalUsers(int numberOfUsers)
throws DeviceNotAvailableException {
return listUsers().size() + numberOfUsers <= getMaxNumberOfUsersSupported();
}
/**
* Throws a {@link org.junit.AssumptionViolatedException} if it's not possible to create the
* desired number of users.
*/
protected void assumeCanCreateAdditionalUsers(int numberOfUsers)
throws DeviceNotAvailableException {
int maxUsers = getDevice().getMaxNumberOfUsersSupported();
assumeTrue("Tests needs at least " + numberOfUsers + " extra users, but device supports "
+ "at most " + getMaxNumberOfUsersSupported(),
canCreateAdditionalUsers(numberOfUsers));
}
/** Checks whether it is possible to start the desired number of users. */
protected boolean canStartAdditionalUsers(int numberOfUsers)
throws DeviceNotAvailableException {
return listRunningUsers().size() + numberOfUsers <= getMaxNumberOfRunningUsersSupported();
}
protected void assumeCanStartNewUser() throws DeviceNotAvailableException {
assumeCanCreateOneManagedUser();
assumeTrue("Cannot start a new user", canStartAdditionalUsers(1));
}
protected int createUser() throws Exception {
int userId = createUser(0);
CLog.i("Created user with id %d", userId);
// TODO remove this and audit tests so they start users as necessary
startUser(userId);
return userId;
}
protected int createUserAndWaitStart() throws Exception {
int userId = createUser(0);
startUserAndWait(userId);
return userId;
}
protected int createUser(int flags) throws Exception {
boolean guest = FLAG_GUEST == (flags & FLAG_GUEST);
boolean ephemeral = FLAG_EPHEMERAL == (flags & FLAG_EPHEMERAL);
CLog.i("Creating user with flags %d: guest=%b, ephemeral=%b", flags, guest, ephemeral);
// TODO Use ITestDevice.createUser() when guest and ephemeral is available
String command ="pm create-user " + (guest ? "--guest " : "")
+ (ephemeral ? "--ephemeral " : "") + "TestUser_" + System.currentTimeMillis();
CLog.d("Starting command %s", command);
String commandOutput = getDevice().executeShellCommand(command);
CLog.d("Output for command %s: %s", command, commandOutput);
// Extract the id of the new user.
String[] tokens = commandOutput.split("\\s+");
assertTrue(tokens.length > 0);
assertEquals("Command '" + command + "' failed: " + commandOutput, "Success:", tokens[0]);
return Integer.parseInt(tokens[tokens.length-1]);
}
protected int createManagedProfile(int parentUserId) throws DeviceNotAvailableException {
String commandOutput = getCreateManagedProfileCommandOutput(parentUserId);
return getUserIdFromCreateUserCommandOutput(commandOutput);
}
protected void assertCannotCreateManagedProfile(int parentUserId)
throws Exception {
String commandOutput = getCreateManagedProfileCommandOutput(parentUserId);
if (commandOutput.startsWith("Error")) {
return;
}
int userId = getUserIdFromCreateUserCommandOutput(commandOutput);
removeUser(userId);
fail("Expected not to be able to create a managed profile. Output was: " + commandOutput);
}
private void assumeHasDeviceFeature(String feature) throws DeviceNotAvailableException {
assumeTrue("device doesn't have " + feature, hasDeviceFeature(feature));
}
private void assumeDoesNotHaveDeviceFeature(String feature) throws DeviceNotAvailableException {
assumeFalse("device has " + feature, hasDeviceFeature(feature));
}
/**
* Used by test cases to add additional checks priort to {@link #setUp()}, so that when it
* throws an {@link AssumptionViolatedException} exception nothing is run
* (even {@link #tearDown()}).
*/
protected void assumeTestEnabled() throws Exception {
}
protected final void assumeCanCreateOneManagedUser() throws DeviceNotAvailableException {
assumeSupportsMultiUser();
assumeCanCreateAdditionalUsers(1);
}
protected final void assumeSupportsMultiUser() throws DeviceNotAvailableException {
assumeTrue("device doesn't support multiple users", mSupportsMultiUser);
}
protected final void assumeHasWifiFeature() throws DeviceNotAvailableException {
assumeHasDeviceFeature(FEATURE_WIFI);
}
protected final void assumeHasTelephonyFeature() throws DeviceNotAvailableException {
assumeHasDeviceFeature(FEATURE_TELEPHONY);
}
protected final void assumeHasNfcFeatures() throws DeviceNotAvailableException {
assumeHasDeviceFeature(FEATURE_NFC);
assumeHasDeviceFeature(FEATURE_NFC_BEAM);
}
protected final void assumeHasTelephonyAndConnectionServiceFeatures()
throws DeviceNotAvailableException {
assumeHasTelephonyFeature();
assumeHasDeviceFeature(FEATURE_CONNECTION_SERVICE);
}
protected final void assumeHasSecureLockScreenFeature() throws DeviceNotAvailableException {
assumeHasDeviceFeature(FEATURE_SECURE_LOCK_SCREEN);
}
protected final void assumeDoesNotHaveSecureLockScreenFeature()
throws DeviceNotAvailableException {
assumeDoesNotHaveDeviceFeature(FEATURE_SECURE_LOCK_SCREEN);
}
protected final void assumeHasFileBasedEncryptionAndSecureLockScreenFeatures()
throws DeviceNotAvailableException {
assumeHasDeviceFeature(FEATURE_FBE);
assumeHasSecureLockScreenFeature();
}
protected final void assumeHasPrintFeature() throws DeviceNotAvailableException {
assumeHasDeviceFeature(FEATURE_PRINT);
}
protected final void assumeHasCameraFeature() throws DeviceNotAvailableException {
assumeHasDeviceFeature(FEATURE_CAMERA);
}
protected final void assumeHasBluetoothFeature() throws DeviceNotAvailableException {
assumeHasDeviceFeature(FEATURE_BLUETOOTH);
}
protected final void assumeApiLevel(int min) throws DeviceNotAvailableException {
assumeTrue("API level must be >=" + min, getDevice().getApiLevel() >= min);
}
private int getUserIdFromCreateUserCommandOutput(String commandOutput) {
// Extract the id of the new user.
String[] tokens = commandOutput.split("\\s+");
assertTrue(commandOutput + " expected to have format \"Success: {USER_ID}\"",
tokens.length > 0);
assertEquals(commandOutput, "Success:", tokens[0]);
return Integer.parseInt(tokens[tokens.length-1]);
}
private String getCreateManagedProfileCommandOutput(int parentUserId)
throws DeviceNotAvailableException {
String command = "pm create-user --profileOf " + parentUserId + " --managed "
+ "TestProfile_" + System.currentTimeMillis();
CLog.d("Starting command " + command);
String commandOutput = getDevice().executeShellCommand(command);
CLog.d("Output for command " + command + ": " + commandOutput);
return commandOutput;
}
protected int getPrimaryUser() throws DeviceNotAvailableException {
return getDevice().getPrimaryUserId();
}
protected int getCurrentUser() throws DeviceNotAvailableException {
return getDevice().getCurrentUser();
}
protected int getUserSerialNumber(int userId) throws DeviceNotAvailableException{
// TODO: Move this logic to ITestDevice.
// dumpsys user output contains lines like "UserInfo{0:Owner:13} serialNo=0 isPrimary=true"
final Pattern pattern =
Pattern.compile("UserInfo\\{" + userId + ":[^\\n]*\\sserialNo=(\\d+)\\s");
final String commandOutput = getDevice().executeShellCommand("dumpsys user");
final Matcher matcher = pattern.matcher(commandOutput);
if (matcher.find()) {
return Integer.parseInt(matcher.group(1));
}
fail("Couldn't find serial number for user " + userId);
return -1;
}
protected boolean setProfileOwner(String componentName, int userId, boolean expectFailure)
throws DeviceNotAvailableException {
String command = "dpm set-profile-owner --user " + userId + " '" + componentName + "'";
String commandOutput = getDevice().executeShellCommand(command);
boolean success = commandOutput.startsWith("Success:");
// If we succeeded always log, if we are expecting failure don't log failures
// as call stacks for passing tests confuse the logs.
if (success || !expectFailure) {
CLog.e("Output for command " + command + ": " + commandOutput);
} else {
CLog.e("Command Failed " + command);
}
return success;
}
protected void setProfileOwnerOrFail(String componentName, int userId)
throws Exception {
if (!setProfileOwner(componentName, userId, /*expectFailure*/ false)) {
// Don't remove system user or initial user that tests require to run on.
removeTestAddedUser(userId);
fail("Failed to set profile owner");
}
}
protected void setProfileOwnerExpectingFailure(String componentName, int userId)
throws Exception {
if (setProfileOwner(componentName, userId, /* expectFailure =*/ true)) {
removeTestAddedUser(userId);
fail("Setting profile owner should have failed.");
}
}
private String setDeviceAdminInner(String componentName, int userId)
throws DeviceNotAvailableException {
String command = "dpm set-active-admin --user " + userId + " '" + componentName + "'";
String commandOutput = getDevice().executeShellCommand(command);
return commandOutput;
}
protected void setDeviceAdmin(String componentName, int userId)
throws DeviceNotAvailableException {
String commandOutput = setDeviceAdminInner(componentName, userId);
CLog.d("Output for command " + commandOutput
+ ": " + commandOutput);
assertTrue(commandOutput + " expected to start with \"Success:\"",
commandOutput.startsWith("Success:"));
}
protected void setDeviceAdminExpectingFailure(String componentName, int userId,
String errorMessage) throws DeviceNotAvailableException {
String commandOutput = setDeviceAdminInner(componentName, userId);
if (!commandOutput.contains(errorMessage)) {
fail(commandOutput + " expected to contain \"" + errorMessage + "\"");
}
}
protected boolean setDeviceOwner(String componentName, int userId, boolean expectFailure)
throws DeviceNotAvailableException {
String command = "dpm set-device-owner --user " + userId + " '" + componentName + "'";
String commandOutput = getDevice().executeShellCommand(command);
boolean success = commandOutput.startsWith("Success:");
// If we succeeded always log, if we are expecting failure don't log failures
// as call stacks for passing tests confuse the logs.
if (success || !expectFailure) {
CLog.d("Output for command " + command + ": " + commandOutput);
} else {
CLog.d("Command Failed " + command);
}
return success;
}
protected void setDeviceOwnerOrFail(String componentName, int userId)
throws Exception {
assertTrue(setDeviceOwner(componentName, userId, /* expectFailure =*/ false));
}
protected void setDeviceOwnerExpectingFailure(String componentName, int userId)
throws Exception {
assertFalse(setDeviceOwner(componentName, userId, /* expectFailure =*/ true));
}
protected void affiliateUsers(String deviceAdminPkg, int userId1, int userId2)
throws Exception {
CLog.d("Affiliating users %d and %d on admin package %s", userId1, userId2, deviceAdminPkg);
runDeviceTestsAsUser(
deviceAdminPkg, ".AffiliationTest", "testSetAffiliationId1", userId1);
runDeviceTestsAsUser(
deviceAdminPkg, ".AffiliationTest", "testSetAffiliationId1", userId2);
}
protected String getSettings(String namespace, String name, int userId)
throws DeviceNotAvailableException {
String command = "settings --user " + userId + " get " + namespace + " " + name;
String commandOutput = getDevice().executeShellCommand(command);
CLog.d("Output for command " + command + ": " + commandOutput);
return commandOutput.replace("\n", "").replace("\r", "");
}
protected void putSettings(String namespace, String name, String value, int userId)
throws DeviceNotAvailableException {
String command = "settings --user " + userId + " put " + namespace + " " + name
+ " " + value;
String commandOutput = getDevice().executeShellCommand(command);
CLog.d("Output for command " + command + ": " + commandOutput);
}
protected boolean removeAdmin(String componentName, int userId)
throws DeviceNotAvailableException {
String command = "dpm remove-active-admin --user " + userId + " '" + componentName + "'";
String commandOutput = getDevice().executeShellCommand(command);
CLog.d("Output for command " + command + ": " + commandOutput);
return commandOutput.startsWith("Success:");
}
// Tries to remove and profile or device owners it finds.
protected void removeOwners() throws DeviceNotAvailableException {
String command = "dumpsys device_policy";
String commandOutput = getDevice().executeShellCommand(command);
String[] lines = commandOutput.split("\\r?\\n");
for (int i = 0; i < lines.length; ++i) {
String line = lines[i].trim();
if (line.contains("Profile Owner")) {
// Line is "Profile owner (User <id>):
String[] tokens = line.split("\\(|\\)| ");
int userId = Integer.parseInt(tokens[4]);
i++;
line = lines[i].trim();
// Line is admin=ComponentInfo{<component>}
tokens = line.split("\\{|\\}");
String componentName = tokens[1];
CLog.w("Cleaning up profile owner " + userId + " " + componentName);
removeAdmin(componentName, userId);
} else if (line.contains("Device Owner:")) {
i++;
line = lines[i].trim();
// Line is admin=ComponentInfo{<component>}
String[] tokens = line.split("\\{|\\}");
String componentName = tokens[1];
// Skip to user id line.
i += 4;
line = lines[i].trim();
// Line is User ID: <N>
tokens = line.split(":");
int userId = Integer.parseInt(tokens[1].trim());
CLog.w("Cleaning up device owner " + userId + " " + componentName);
removeAdmin(componentName, userId);
}
}
}
/**
* Runs pm enable command to enable a package or component. Returns the command result.
*/
protected String enableComponentOrPackage(int userId, String packageOrComponent)
throws DeviceNotAvailableException {
String command = "pm enable --user " + userId + " " + packageOrComponent;
String result = getDevice().executeShellCommand(command);
CLog.d("Output for command " + command + ": " + result);
return result;
}
/**
* Runs pm disable command to disable a package or component. Returns the command result.
*/
protected String disableComponentOrPackage(int userId, String packageOrComponent)
throws DeviceNotAvailableException {
String command = "pm disable --user " + userId + " " + packageOrComponent;
String result = getDevice().executeShellCommand(command);
CLog.d("Output for command " + command + ": " + result);
return result;
}
protected interface SuccessCondition {
boolean check() throws Exception;
}
protected void waitUntilUserRemoved(int userId) throws Exception {
tryWaitForSuccess(() -> !listUsers().contains(userId),
"The user " + userId + " has not been removed",
TIMEOUT_USER_REMOVED_MILLIS
);
}
protected void tryWaitForSuccess(SuccessCondition successCondition, String failureMessage,
long timeoutMillis) throws Exception {
long epoch = System.currentTimeMillis();
while (System.currentTimeMillis() - epoch <= timeoutMillis) {
Thread.sleep(WAIT_SAMPLE_INTERVAL_MILLIS);
if (successCondition.check()) {
return;
}
}
fail(failureMessage);
}
/**
* Sets a user restriction via SetPolicyActivity.
* <p>IMPORTANT: The package that contains SetPolicyActivity must have been installed prior to
* calling this method.
* @param key user restriction key
* @param value true if we should set the restriction, false if we should clear it
* @param userId userId to set/clear the user restriction on
* @param packageName package where SetPolicyActivity is installed
* @return The output of the command
* @throws DeviceNotAvailableException
*/
protected String changeUserRestriction(String key, boolean value, int userId,
String packageName) throws DeviceNotAvailableException {
return changePolicy(getUserRestrictionCommand(value),
" --es extra-restriction-key " + key, userId, packageName);
}
/**
* Same as {@link #changeUserRestriction(String, boolean, int, String)} but asserts that it
* succeeds.
*/
protected void changeUserRestrictionOrFail(String key, boolean value, int userId,
String packageName) throws DeviceNotAvailableException {
changePolicyOrFail(getUserRestrictionCommand(value), " --es extra-restriction-key " + key,
userId, packageName);
}
/**
* Sets some policy via SetPolicyActivity.
* <p>IMPORTANT: The package that contains SetPolicyActivity must have been installed prior to
* calling this method.
* @param command command to pass to SetPolicyActivity
* @param extras extras to pass to SetPolicyActivity
* @param userId the userId where we invoke SetPolicyActivity
* @param packageName where SetPolicyActivity is installed
* @return The output of the command
* @throws DeviceNotAvailableException
*/
protected String changePolicy(String command, String extras, int userId, String packageName)
throws DeviceNotAvailableException {
String adbCommand = "am start -W --user " + userId
+ " -c android.intent.category.DEFAULT "
+ " --es extra-command " + command
+ " " + extras
+ " " + packageName + "/.SetPolicyActivity";
String commandOutput = getDevice().executeShellCommand(adbCommand);
CLog.d("Output for command " + adbCommand + ": " + commandOutput);
return commandOutput;
}
/**
* Same as {@link #changePolicy(String, String, int, String)} but asserts that it succeeds.
*/
protected void changePolicyOrFail(String command, String extras, int userId,
String packageName) throws DeviceNotAvailableException {
String commandOutput = changePolicy(command, extras, userId, packageName);
assertTrue("Command was expected to succeed " + commandOutput,
commandOutput.contains("Status: ok"));
}
private String getUserRestrictionCommand(boolean setRestriction) {
if (setRestriction) {
return "add-restriction";
}
return "clear-restriction";
}
/**
* Set lockscreen password / work challenge for the given user, null or "" means clear
* IMPORTANT: prefer to use {@link #TEST_PASSWORD} for primary user, otherwise if the test
* terminates before cleaning password up, the device will be unusable for further testing.
*/
protected void changeUserCredential(String newCredential, String oldCredential, int userId)
throws DeviceNotAvailableException {
final String oldCredentialArgument = (oldCredential == null || oldCredential.isEmpty()) ? ""
: ("--old " + oldCredential);
if (newCredential != null && !newCredential.isEmpty()) {
String commandOutput = getDevice().executeShellCommand(String.format(
"cmd lock_settings set-password --user %d %s %s", userId, oldCredentialArgument,
newCredential));
if (!commandOutput.startsWith("Password set to")) {
fail("Failed to set user credential: " + commandOutput);
}
} else {
String commandOutput = getDevice().executeShellCommand(String.format(
"cmd lock_settings clear --user %d %s", userId, oldCredentialArgument));
if (!commandOutput.startsWith("Lock credential cleared")) {
fail("Failed to clear user credential: " + commandOutput);
}
}
}
/**
* Verifies the lock credential for the given user.
*
* @param credential The credential to verify.
* @param userId The id of the user.
*/
protected void verifyUserCredential(String credential, int userId)
throws DeviceNotAvailableException {
String commandOutput = verifyUserCredentialCommandOutput(credential, userId);
if (!commandOutput.startsWith(VERIFY_CREDENTIAL_CONFIRMATION)) {
fail("Failed to verify user credential: " + commandOutput);
}
}
/**
* Verifies the lock credential for the given user, which unlocks the user, and returns
* whether it was successful or not.
*
* @param credential The credential to verify.
* @param userId The id of the user.
*/
protected boolean verifyUserCredentialIsCorrect(String credential, int userId)
throws DeviceNotAvailableException {
String commandOutput = verifyUserCredentialCommandOutput(credential, userId);
return commandOutput.startsWith(VERIFY_CREDENTIAL_CONFIRMATION);
}
/**
* Verifies the lock credential for the given user, which unlocks the user. Returns the
* commandline output, which includes whether the verification was successful.
*
* @param credential The credential to verify.
* @param userId The id of the user.
* @return The command line output.
*/
protected String verifyUserCredentialCommandOutput(String credential, int userId)
throws DeviceNotAvailableException {
final String credentialArgument = (credential == null || credential.isEmpty())
? "" : ("--old " + credential);
String commandOutput = getDevice().executeShellCommand(String.format(
"cmd lock_settings verify --user %d %s", userId, credentialArgument));
return commandOutput;
}
protected void wakeupAndDismissKeyguard() throws Exception {
executeShellCommand("input keyevent KEYCODE_WAKEUP");
executeShellCommand("wm dismiss-keyguard");
}
protected void pressPowerButton() throws Exception {
executeShellCommand("input keyevent KEYCODE_POWER");
}
private void stayAwake() throws Exception {
executeShellCommand(
"settings put global stay_on_while_plugged_in " + STAY_ON_WHILE_PLUGGED_IN_FLAGS);
}
protected void startActivityAsUser(int userId, String packageName, String activityName)
throws Exception {
wakeupAndDismissKeyguard();
String command = "am start -W --user " + userId + " " + packageName + "/" + activityName;
getDevice().executeShellCommand(command);
}
protected String getDefaultLauncher() throws Exception {
final CollectingByteOutputReceiver receiver = new CollectingByteOutputReceiver();
getDevice().executeShellCommand("dumpsys role --proto", receiver);
RoleUserStateProto roleState = null;
final RoleServiceDumpProto dumpProto =
RoleServiceDumpProto.parser().parseFrom(receiver.getOutput());
for (RoleUserStateProto userState : dumpProto.getUserStatesList()) {
if (getDevice().getCurrentUser() == userState.getUserId()) {
roleState = userState;
break;
}
}
if (roleState != null) {
final List<RoleProto> roles = roleState.getRolesList();
// Iterate through the roles until we find the Home role
for (RoleProto roleProto : roles) {
if ("android.app.role.HOME".equals(roleProto.getName())) {
assertEquals(1, roleProto.getHoldersList().size());
return roleProto.getHoldersList().get(0);
}
}
}
throw new Exception("Default launcher not found");
}
void assumeIsDeviceAb() throws DeviceNotAvailableException {
final String result = getDevice().executeShellCommand("getprop ro.build.ab_update").trim();
assumeTrue("not device AB", "true".equalsIgnoreCase(result));
}
// TODO (b/174775905) remove after exposing the check from ITestDevice.
boolean isHeadlessSystemUserMode() throws DeviceNotAvailableException {
return isHeadlessSystemUserMode(getDevice());
}
// TODO (b/174775905) remove after exposing the check from ITestDevice.
public static boolean isHeadlessSystemUserMode(ITestDevice device)
throws DeviceNotAvailableException {
final String result = device
.executeShellCommand("getprop ro.fw.mu.headless_system_user").trim();
return "true".equalsIgnoreCase(result);
}
protected void assumeHeadlessSystemUserMode(String reason)
throws DeviceNotAvailableException {
assumeTrue("Skipping test on non-headless system user mode. Reason: " + reason,
isHeadlessSystemUserMode());
}
protected void grantDpmWrapperPermissions(String deviceAdminPkg, int userId) throws Exception {
// TODO(b/176993670): INTERACT_ACROSS_USERS is needed by DevicePolicyManagerWrapper to
// send ordered broadcasts to the test user. The permission is already available to the
// packages installed by the host side test (as they're installed with -g), but need to be
// granted for users created by the test, as the package is intalled by code
// (DPMS.manageUserUnchecked(), which doesn't grant it (as this is a privileged permission
// that's not available to 3rd party apps). If we get rid of DevicePolicyManagerWrapper,
// we won't need to grant it anymore.
grantPermission(deviceAdminPkg, PERMISSION_INTERACT_ACROSS_USERS, userId, "its PO needs to "
+ "send ordered broadcasts to user 0");
// Probably not needed anymore, but it doesn't hurt to keep...
allowTestApiAccess(deviceAdminPkg);
}
protected void allowTestApiAccess(String deviceAdminPkg) throws Exception {
CLog.i("Granting ALLOW_TEST_API_ACCESS to package %s", deviceAdminPkg);
executeShellCommand("am compat enable ALLOW_TEST_API_ACCESS %s", deviceAdminPkg);
}
protected void grantPermission(String pkg, String permission, int userId, String reason)
throws Exception {
CLog.i("Granting permission %s to package (%s) on user %d%s", pkg, permission, userId,
(reason == null ? "" : "(reason: " + reason + ")"));
executeShellCommand("pm grant --user %d %s %s", userId, pkg, permission);
}
protected void revokePermission(String pkg, String permission, int userId) throws Exception {
CLog.i("Revoking permission %s to package (%s) on user %d", pkg, permission, userId);
executeShellCommand("pm revoke --user %d %s %s", userId, pkg, permission);
}
/** Find effective restriction for user */
protected boolean isRestrictionSetOnUser(int userId, String restriction) throws Exception {
String commandOutput = getDevice().executeShellCommand("dumpsys user");
String[] outputLines = commandOutput.split("\\n");
Pattern userPattern = Pattern.compile("(^.*)UserInfo\\{" + userId + ":.*$");
Pattern restrictionPattern = Pattern.compile("(^.*)Effective\\srestrictions\\:.*$");
boolean userFound = false;
boolean restrictionsFound = false;
int lastIndent = -1;
for (String line : outputLines) {
// Starting a new block of user infos
if (!line.startsWith(Strings.repeat(" ", lastIndent + 1))) {
CLog.d("User %d restrictions found, no matched restriction.", userId);
return false;
}
//First, try matching user pattern
Matcher userMatcher = userPattern.matcher(line);
if (userMatcher.find()) {
CLog.d("User %d found in dumpsys, finding restrictions.", userId);
userFound = true;
lastIndent = userMatcher.group(1).length();
}
// Second, try matching restriction
Matcher restrictionMatcher = restrictionPattern.matcher(line);
if (userFound && restrictionMatcher.find()) {
CLog.d("User %d restrictions found, finding exact restriction.", userId);
restrictionsFound = true;
lastIndent = restrictionMatcher.group(1).length();
}
if (restrictionsFound && line.contains(restriction)) {
return true;
}
}
if (!userFound) {
CLog.e("User %d not found in dumpsys.", userId);
}
if (!restrictionsFound) {
CLog.d("User %d found in dumpsys, but restrictions not found.", userId);
}
return false;
}
/**
* Generates instrumentation arguments that indicate the device-side test is exercising device
* owner APIs.
*
* <p>This is needed for hostside tests that use the same class hierarchy for both device and
* profile owner tests, as on headless system user mode the test side must decide whether to
* use its "local DPC" or wrap the calls to the system user DPC.
*/
protected static Map<String, String> getParamsForDeviceOwnerTest() {
Map<String, String> params = new HashMap<>();
params.put("admin_type", "DeviceOwner");
return params;
}
boolean isTv() throws DeviceNotAvailableException {
return hasDeviceFeature(FEATURE_LEANBACK);
}
void pushUpdateFileToDevice(String fileName)
throws IOException, DeviceNotAvailableException {
File file = File.createTempFile(
fileName.split("\\.")[0], "." + fileName.split("\\.")[1]);
try (OutputStream outputStream = new FileOutputStream(file)) {
InputStream inputStream = getClass().getResourceAsStream("/" + fileName);
ByteStreams.copy(inputStream, outputStream);
}
getDevice().pushFile(file, TEST_UPDATE_LOCATION + "/" + fileName);
file.delete();
}
boolean hasService(String service) {
String command = "service check " + service;
try {
String commandOutput = getDevice().executeShellCommand(command);
return !commandOutput.contains("not found");
} catch (Exception e) {
CLog.w("Exception running '" + command + "': " + e);
return false;
}
}
void sleep(int timeMs) throws InterruptedException {
CLog.d("Sleeping %d ms");
Thread.sleep(timeMs);
}
}