1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.cts.devicepolicy;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertNotNull;
22 import static org.junit.Assert.assertNull;
23 import static org.junit.Assert.assertTrue;
24 import static org.junit.Assert.fail;
25 import static org.junit.Assume.assumeFalse;
26 import static org.junit.Assume.assumeNotNull;
27 import static org.junit.Assume.assumeTrue;
28 
29 import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
30 import com.android.tradefed.config.Option;
31 import com.android.tradefed.device.CollectingOutputReceiver;
32 import com.android.tradefed.device.DeviceNotAvailableException;
33 import com.android.tradefed.device.ITestDevice;
34 import com.android.tradefed.log.LogUtil.CLog;
35 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
36 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test;
37 import com.android.tradefed.util.RunUtil;
38 
39 import com.google.common.base.Strings;
40 import com.google.common.io.ByteStreams;
41 
42 import org.junit.After;
43 import org.junit.AssumptionViolatedException;
44 import org.junit.Before;
45 import org.junit.Rule;
46 import org.junit.runner.RunWith;
47 
48 import java.io.File;
49 import java.io.FileNotFoundException;
50 import java.io.FileOutputStream;
51 import java.io.IOException;
52 import java.io.InputStream;
53 import java.io.OutputStream;
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.Collections;
57 import java.util.HashMap;
58 import java.util.LinkedList;
59 import java.util.List;
60 import java.util.Map;
61 import java.util.Set;
62 import java.util.concurrent.TimeUnit;
63 import java.util.function.Predicate;
64 import java.util.regex.Matcher;
65 import java.util.regex.Pattern;
66 
67 import javax.annotation.Nullable;
68 
69 /**
70  * Base class for device policy tests. It offers utility methods to run tests, set device or profile
71  * owner, etc.
72  */
73 @RunWith(DeviceJUnit4ClassRunner.class)
74 public abstract class BaseDevicePolicyTest extends BaseHostJUnit4Test {
75 
76     private static final String FEATURE_AUTOMOTIVE = "android.hardware.type.automotive";
77     private static final String FEATURE_BLUETOOTH = "android.hardware.bluetooth";
78     private static final String FEATURE_CAMERA = "android.hardware.camera";
79     private static final String FEATURE_CONNECTION_SERVICE = "android.software.connectionservice";
80     private static final String FEATURE_FBE = "android.software.file_based_encryption";
81     private static final String FEATURE_LEANBACK = "android.software.leanback";
82     private static final String FEATURE_NFC = "android.hardware.nfc";
83     private static final String FEATURE_NFC_BEAM = "android.software.nfc.beam";
84     private static final String FEATURE_PRINT = "android.software.print";
85     private static final String FEATURE_TELEPHONY = "android.hardware.telephony";
86     private static final String FEATURE_SECURE_LOCK_SCREEN = "android.software.secure_lock_screen";
87     private static final String FEATURE_WIFI = "android.hardware.wifi";
88     private static final String FEATURE_WATCH = "android.hardware.type.watch";
89 
90     //The maximum time to wait for user to be unlocked.
91     private static final long USER_UNLOCK_TIMEOUT_SEC = 60;
92     private static final String USER_STATE_UNLOCKED = "RUNNING_UNLOCKED";
93 
94     protected static final String PERMISSION_INTERACT_ACROSS_USERS =
95             "android.permission.INTERACT_ACROSS_USERS";
96 
97     @Option(
98             name = "skip-device-admin-feature-check",
99             description = "Flag that allows to skip the check for android.software.device_admin "
100                 + "and run the tests no matter what. This is useful for system that do not what "
101                 + "to expose that feature publicly."
102     )
103     private boolean mSkipDeviceAdminFeatureCheck = false;
104 
105     private static final String RUNNER = "androidx.test.runner.AndroidJUnitRunner";
106 
107     protected static final int USER_SYSTEM = 0; // From the UserHandle class.
108 
109     protected static final int USER_OWNER = USER_SYSTEM;
110 
111     private static final long TIMEOUT_USER_REMOVED_MILLIS = TimeUnit.SECONDS.toMillis(15);
112     private static final long WAIT_SAMPLE_INTERVAL_MILLIS = 200;
113 
114     /**
115      * The defined timeout (in milliseconds) is used as a maximum waiting time when expecting the
116      * command output from the device. At any time, if the shell command does not output anything
117      * for a period longer than defined timeout the Tradefed run terminates.
118      */
119     private static final long DEFAULT_SHELL_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(20);
120 
121     /**
122      * Sets timeout (in milliseconds) that will be applied to each test. In the
123      * event of a test timeout it will log the results and proceed with executing the next test.
124      */
125     private static final long DEFAULT_TEST_TIMEOUT_MILLIS = TimeUnit.MINUTES.toMillis(10);
126 
127     /**
128      * The amount of milliseconds to wait for the switch user calls in {@link #tearDown}.
129      */
130     private static final long USER_SWITCH_WAIT = TimeUnit.SECONDS.toMillis(1);
131 
132     // From the UserInfo class
133     protected static final int FLAG_GUEST = 0x00000004;
134     protected static final int FLAG_EPHEMERAL = 0x00000100;
135     protected static final int FLAG_MANAGED_PROFILE = 0x00000020;
136     protected static final int FLAG_INITIALIZED = 0x00000010;
137 
138     /** Default password to use in tests. */
139     protected static final String TEST_PASSWORD = "1234";
140 
141     /**
142      * The {@link android.os.BatteryManager} flags value representing all charging types; {@link
143      * android.os.BatteryManager#BATTERY_PLUGGED_AC}, {@link
144      * android.os.BatteryManager#BATTERY_PLUGGED_USB}, and {@link
145      * android.os.BatteryManager#BATTERY_PLUGGED_WIRELESS}.
146      */
147     private static final int STAY_ON_WHILE_PLUGGED_IN_FLAGS = 7;
148 
149     /**
150      * User ID for all users.
151      * The value is from the UserHandle class.
152      */
153     protected static final int USER_ALL = -1;
154 
155     private static final String TEST_UPDATE_LOCATION = "/data/local/tmp/cts/deviceowner";
156 
157     /**
158      * Copied from {@link android.app.admin.DevicePolicyManager
159      * .InstallSystemUpdateCallback#UPDATE_ERROR_UPDATE_FILE_INVALID}
160      */
161     protected static final int UPDATE_ERROR_UPDATE_FILE_INVALID = 3;
162 
163     protected CompatibilityBuildHelper mBuildHelper;
164     private String mPackageVerifier;
165 
166     /** Packages installed as part of the tests */
167     private Set<String> mFixedPackages;
168 
169     protected int mDeviceOwnerUserId;
170     protected int mPrimaryUserId;
171 
172     /** Is test running on a watch */
173     protected boolean mIsWatch;
174 
175     /** Record the initial user ID. */
176     protected int mInitialUserId;
177 
178     /** Whether multi-user is supported. */
179     private boolean mSupportsMultiUser;
180 
181     /** Users we shouldn't delete in the tests */
182     private final ArrayList<Integer> mFixedUsers = new ArrayList<>();
183 
184     protected boolean mHasAttestation;
185 
186     private static final String VERIFY_CREDENTIAL_CONFIRMATION = "Lock credential verified";
187 
skipDeviceAdminFeatureCheck()188     public boolean skipDeviceAdminFeatureCheck() {
189         return mSkipDeviceAdminFeatureCheck;
190     }
191 
192     @Rule
193     public final DeviceAdminFeaturesCheckerRule mFeaturesCheckerRule =
194             new DeviceAdminFeaturesCheckerRule(this);
195 
196     @Before
setUp()197     public void setUp() throws Exception {
198         assertNotNull(getBuild());  // ensure build has been set before test is run.
199 
200         mSupportsMultiUser = getMaxNumberOfUsersSupported() > 1;
201         mFixedPackages = getDevice().getInstalledPackageNames();
202         mBuildHelper = new CompatibilityBuildHelper(getBuild());
203         mIsWatch = hasDeviceFeature(FEATURE_WATCH);
204 
205         String propertyValue = getDevice().getProperty("ro.product.first_api_level");
206         if (propertyValue != null && !propertyValue.isEmpty()) {
207             mHasAttestation = Integer.parseInt(propertyValue) >= 26;
208         }
209         if (hasDeviceFeature(FEATURE_SECURE_LOCK_SCREEN)) {
210             ensurePrimaryUserHasNoPassword();
211         }
212 
213         // disable the package verifier to avoid the dialog when installing an app
214         mPackageVerifier = getDevice().executeShellCommand(
215                 "settings get global verifier_verify_adb_installs");
216         getDevice().executeShellCommand("settings put global verifier_verify_adb_installs 0");
217 
218         // Set the value of initial user ID calls in {@link #setUp}.
219         if(mSupportsMultiUser) {
220             mInitialUserId = getDevice().getCurrentUser();
221         }
222 
223         mDeviceOwnerUserId = mPrimaryUserId = getMainUser();
224 
225         mFixedUsers.add(mPrimaryUserId);
226         if (mPrimaryUserId != USER_SYSTEM) {
227             mFixedUsers.add(USER_SYSTEM);
228         }
229 
230         if (mFeaturesCheckerRule.hasRequiredFeatures()) {
231             // Switching to primary is only needed when we're testing device admin features.
232             switchUser(mPrimaryUserId);
233         } else {
234             // Otherwise, all the tests can be executed in any of the Android users, so remain in
235             // current user, and don't delete it. This enables testing in secondary users.
236             if (getDevice().getCurrentUser() != mPrimaryUserId) {
237                 mFixedUsers.add(getDevice().getCurrentUser());
238             }
239         }
240         getDevice().executeShellCommand(" mkdir " + TEST_UPDATE_LOCATION);
241 
242         removeOwners();
243 
244         switchUser(mPrimaryUserId);
245 
246         removeTestUsers();
247         // Unlock keyguard before test
248         wakeupAndDismissKeyguard();
249         stayAwake();
250         // Go to home.
251         executeShellCommand("input keyevent KEYCODE_HOME");
252     }
253 
ensurePrimaryUserHasNoPassword()254     private void ensurePrimaryUserHasNoPassword() throws DeviceNotAvailableException {
255         if (!verifyUserCredentialIsCorrect(null, mPrimaryUserId)) {
256             changeUserCredential(null, TEST_PASSWORD, mPrimaryUserId);
257         }
258     }
259 
260     /** If package manager is not available, e.g. after system crash, wait for it a little bit. */
ensurePackageManagerReady()261     private void ensurePackageManagerReady() throws Exception {
262         waitForOutput("Package manager didn't become available", "service check package",
263                 s -> s.trim().equals("Service package: found"), 120 /* seconds */);
264     }
265 
waitForUserUnlock(int userId)266     protected void waitForUserUnlock(int userId) throws Exception {
267         waitForOutput("User is not unlocked.",
268                 String.format("am get-started-user-state %d", userId),
269                 s -> s.startsWith(USER_STATE_UNLOCKED), USER_UNLOCK_TIMEOUT_SEC);
270     }
271 
waitForOutput(String message, String command, Predicate<String> predicate, long timeoutSec)272     protected void waitForOutput(String message, String command, Predicate<String> predicate,
273             long timeoutSec) throws Exception {
274         final long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(timeoutSec);
275         while (!predicate.test(getDevice().executeShellCommand(command))) {
276             if (System.nanoTime() > deadline) {
277                 fail(message);
278             }
279             RunUtil.getDefault().sleep(1000);
280         }
281     }
282 
283     @After
tearDown()284     public void tearDown() throws Exception {
285         // reset the package verifier setting to its original value
286         getDevice().executeShellCommand("settings put global verifier_verify_adb_installs "
287                 + mPackageVerifier);
288         removeOwners();
289 
290         // Switch back to initial user.
291         if (mSupportsMultiUser && getDevice().getCurrentUser() != mInitialUserId) {
292             switchUser(mInitialUserId);
293         }
294         removeTestUsers();
295         removeTestPackages();
296         getDevice().executeShellCommand(" rm -r " + TEST_UPDATE_LOCATION);
297     }
298 
installAppAsUser(String appFileName, int userId)299     protected void installAppAsUser(String appFileName, int userId) throws FileNotFoundException,
300             DeviceNotAvailableException {
301         installAppAsUser(appFileName, true, userId);
302     }
303 
installAppAsUser(String appFileName, boolean grantPermissions, int userId)304     protected void installAppAsUser(String appFileName, boolean grantPermissions, int userId)
305             throws FileNotFoundException, DeviceNotAvailableException {
306         installAppAsUser(appFileName, grantPermissions, /* dontKillApp */ false, userId);
307     }
308 
installAppAsUser(String appFileName, boolean grantPermissions, boolean dontKillApp, int userId)309     protected void installAppAsUser(String appFileName, boolean grantPermissions,
310             boolean dontKillApp, int userId)
311                     throws FileNotFoundException, DeviceNotAvailableException {
312         CLog.e("Installing app %s for user %d", appFileName, userId);
313         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
314         List<String> extraArgs = new LinkedList<>();
315         extraArgs.add("-t");
316         // Make the test app queryable by other apps via PackageManager APIs.
317         extraArgs.add("--force-queryable");
318         if (getDevice().isBypassLowTargetSdkBlockSupported()) {
319             extraArgs.add("--bypass-low-target-sdk-block");
320         }
321         if (dontKillApp) extraArgs.add("--dont-kill");
322         String result = getDevice().installPackageForUser(
323                 buildHelper.getTestFile(appFileName), true, grantPermissions, userId,
324                 extraArgs.toArray(new String[extraArgs.size()]));
325         assertNull("Failed to install " + appFileName + " for user " + userId + ": " + result,
326                 result);
327     }
328 
installAppIncremental(String appFileName)329     protected void installAppIncremental(String appFileName)
330             throws FileNotFoundException, DeviceNotAvailableException {
331         final String signatureSuffix = ".idsig";
332         CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
333         final File apk = buildHelper.getTestFile(appFileName);
334         assertNotNull(apk);
335         final File idsig = buildHelper.getTestFile(appFileName + signatureSuffix);
336         assertNotNull(idsig);
337         final String remoteApkPath = TEST_UPDATE_LOCATION + "/" + apk.getName();
338         final String remoteIdsigPath = remoteApkPath + signatureSuffix;
339         assertTrue(getDevice().pushFile(apk, remoteApkPath));
340         assertTrue(getDevice().pushFile(idsig, remoteIdsigPath));
341         String installResult = getDevice().executeShellCommand(
342                 "pm install-incremental -t -g " + remoteApkPath);
343         assertEquals("Success\n", installResult);
344     }
345 
installDeviceOwnerApp(String apk)346     protected void installDeviceOwnerApp(String apk) throws Exception {
347         installAppAsUser(apk, mDeviceOwnerUserId);
348     }
349 
removeDeviceOwnerAdmin(String componentName)350     protected void removeDeviceOwnerAdmin(String componentName) throws DeviceNotAvailableException {
351         // Don't fail as it could hide the real failure from the test method
352         if (!removeAdmin(componentName, mDeviceOwnerUserId)) {
353             CLog.e("Failed to remove device owner %s on user %d", componentName,
354                     mDeviceOwnerUserId);
355         }
356         if (isHeadlessSystemUserMode() && !removeAdmin(componentName, mPrimaryUserId)) {
357             CLog.e("Failed to remove profile owner %s on user %d", componentName, mPrimaryUserId);
358         }
359     }
360 
forceStopPackageForUser(String packageName, int userId)361     protected void forceStopPackageForUser(String packageName, int userId) throws Exception {
362         // TODO Move this logic to ITestDevice
363         executeShellCommand("am force-stop --user " + userId + " " + packageName);
364     }
365 
fgsStopPackageForUser(String packageName, int userId)366     protected void fgsStopPackageForUser(String packageName, int userId) throws Exception {
367         // TODO Move this logic to ITestDevice
368         executeShellCommand("am stop-app --user " + userId + " " + packageName);
369     }
370 
executeShellCommand(String commandTemplate, Object...args)371     protected String executeShellCommand(String commandTemplate, Object...args) throws Exception {
372         return executeShellCommand(String.format(commandTemplate, args));
373     }
374 
executeShellCommand(String command)375     protected String executeShellCommand(String command) throws Exception {
376         CLog.d("Starting command %s", command);
377         String commandOutput = getDevice().executeShellCommand(command);
378         CLog.d("Output for command %s: %s", command, commandOutput);
379         return commandOutput;
380     }
381 
382     /** Initializes the user with the given id. This is required so that apps can run on it. */
startUser(int userId)383     protected void startUser(int userId) throws Exception {
384         CLog.d("Starting user %d", userId);
385         getDevice().startUser(userId);
386     }
387 
388     /** Initializes the user with waitFlag. This is required so that apps can run on it. */
startUserAndWait(int userId)389     protected void startUserAndWait(int userId) throws Exception {
390         CLog.d("Starting user %d and waiting", userId);
391         getDevice().startUser(userId, /* waitFlag= */ true);
392     }
393 
394     /**
395      * Initializes the user with the given id, and waits until the user has started and unlocked
396      * before continuing.
397      *
398      * <p>This is required so that apps can run on it.
399      */
startUser(int userId, boolean waitFlag)400     protected void startUser(int userId, boolean waitFlag) throws Exception {
401         getDevice().startUser(userId, waitFlag);
402     }
403 
404     /**
405      * Starts switching to the user with the given ID.
406      *
407      * <p>This is not blocking. Some operations will be flaky if called immediately afterwards, such
408      * as {@link #wakeupAndDismissKeyguard()}. Call {@link #waitForBroadcastIdle()} between this
409      * method and those operations to ensure that switching the user has finished.
410      */
switchUser(int userId)411     protected void switchUser(int userId) throws Exception {
412         // TODO Move this logic to ITestDevice
413         int retries = 15;
414         CLog.i("switching to user %d", userId);
415         executeShellCommand("am switch-user " + userId);
416         RunUtil.getDefault().sleep(USER_SWITCH_WAIT);
417         while (getDevice().getCurrentUser() != userId && (--retries) >= 0) {
418             // am switch-user can be ignored if a previous user-switching operation
419             // is still in progress. In this case, sleep a bit and then retry
420             RunUtil.getDefault().sleep(USER_SWITCH_WAIT);
421             executeShellCommand("am switch-user " + userId);
422         }
423         assertTrue("Failed to switch user after multiple retries", getDevice().getCurrentUser() == userId);
424     }
425 
getMaxNumberOfUsersSupported()426     protected int getMaxNumberOfUsersSupported() throws DeviceNotAvailableException {
427         return getDevice().getMaxNumberOfUsersSupported();
428     }
429 
getMaxNumberOfRunningUsersSupported()430     protected int getMaxNumberOfRunningUsersSupported() throws DeviceNotAvailableException {
431         return getDevice().getMaxNumberOfRunningUsersSupported();
432     }
433 
getUserFlags(int userId)434     protected int getUserFlags(int userId) throws DeviceNotAvailableException {
435         String command = "pm list users";
436         String commandOutput = getDevice().executeShellCommand(command);
437         CLog.i("Output for command " + command + ": " + commandOutput);
438 
439         String[] lines = commandOutput.split("\\r?\\n");
440         assertTrue(commandOutput + " should contain at least one line", lines.length >= 1);
441         for (int i = 1; i < lines.length; i++) {
442             // Individual user is printed out like this:
443             // \tUserInfo{$id$:$name$:$Integer.toHexString(flags)$} [running]
444             String[] tokens = lines[i].split("\\{|\\}|:");
445             assertTrue(lines[i] + " doesn't contain 4 or 5 tokens",
446                     tokens.length == 4 || tokens.length == 5);
447             // If the user IDs match, return the flags.
448             if (Integer.parseInt(tokens[1]) == userId) {
449                 return Integer.parseInt(tokens[3], 16);
450             }
451         }
452         fail("User not found");
453         return 0;
454     }
455 
listUsers()456     protected ArrayList<Integer> listUsers() throws DeviceNotAvailableException {
457         return getDevice().listUsers();
458     }
459 
listRunningUsers()460     protected  ArrayList<Integer> listRunningUsers() throws DeviceNotAvailableException {
461         ArrayList<Integer> runningUsers = new ArrayList<>();
462         for (int userId : listUsers()) {
463             if (getDevice().isUserRunning(userId)) {
464                 runningUsers.add(userId);
465             }
466         }
467         return runningUsers;
468     }
469 
getFirstManagedProfileUserId()470     protected int getFirstManagedProfileUserId() throws DeviceNotAvailableException {
471         for (int userId : listUsers()) {
472             if ((getUserFlags(userId) & FLAG_MANAGED_PROFILE) != 0) {
473                 return userId;
474             }
475         }
476         fail("Managed profile not found");
477         return 0;
478     }
479 
stopUserAsync(int userId)480     private void stopUserAsync(int userId) throws Exception {
481         String stopUserCommand = "am stop-user -f " + userId;
482         CLog.d("starting command \"" + stopUserCommand);
483         CLog.d("Output for command " + stopUserCommand + ": "
484                 + getDevice().executeShellCommand(stopUserCommand));
485     }
486 
stopUser(int userId)487     protected void stopUser(int userId) throws Exception {
488         String stopUserCommand = "am stop-user -w -f " + userId;
489         CLog.d("starting command \"" + stopUserCommand + "\" and waiting.");
490         CLog.d("Output for command " + stopUserCommand + ": "
491                 + getDevice().executeShellCommand(stopUserCommand));
492     }
493 
waitForBroadcastIdle()494     protected void waitForBroadcastIdle() throws DeviceNotAvailableException, IOException {
495         final CollectingOutputReceiver receiver = new CollectingOutputReceiver();
496         // We allow 8min for the command to complete and 4min for the command to start to
497         // output something.
498         getDevice().executeShellCommand(
499                 "am wait-for-broadcast-idle", receiver, 8, 4, TimeUnit.MINUTES, 0);
500         final String output = receiver.getOutput();
501         if (!output.contains("All broadcast queues are idle!")) {
502             CLog.e("Output from 'am wait-for-broadcast-idle': %s", output);
503             fail("'am wait-for-broadcase-idle' did not complete.");
504         }
505     }
506 
removeUser(int userId)507     protected void removeUser(int userId) throws Exception  {
508         if (listUsers().contains(userId) && userId != USER_SYSTEM) {
509             // Don't log output, as tests sometimes set no debug user restriction, which
510             // causes this to fail, we should still continue and remove the user.
511             String stopUserCommand = "am stop-user -w -f " + userId;
512             CLog.d("stopping and removing user " + userId);
513             getDevice().executeShellCommand(stopUserCommand);
514             // Ephemeral users may have already been removed after being stopped.
515             if (listUsers().contains(userId)) {
516                 assertTrue("Couldn't remove user", getDevice().removeUser(userId));
517             }
518         }
519     }
520 
removeTestUsers()521     protected void removeTestUsers() throws Exception {
522         List<Integer> usersCreatedByTests = getUsersCreatedByTests();
523 
524         // The time spent on stopUser is depend on how busy the broadcast queue is.
525         // To optimize the time to remove multiple test users, we mark all users as
526         // stopping first, so no more broadcasts will be sent to these users, which make the queue
527         // less busy.
528         for (int userId : usersCreatedByTests) {
529             stopUserAsync(userId);
530         }
531         for (int userId : usersCreatedByTests) {
532             removeTestAddedUser(userId);
533         }
534     }
535 
removeTestAddedUser(int userId)536     private void removeTestAddedUser(int userId) throws Exception  {
537         // Don't remove system user or initial user.
538         if (userId != USER_SYSTEM && userId != mInitialUserId) {
539             removeUser(userId);
540         }
541     }
542 
543     /**
544      * Returns the users that have been created since running this class' setUp() method.
545      */
getUsersCreatedByTests()546     protected List<Integer> getUsersCreatedByTests() throws Exception {
547         List<Integer> result = listUsers();
548         result.removeAll(mFixedUsers);
549         return result;
550     }
551 
552     /** Removes any packages that were installed during the test. */
removeTestPackages()553     protected void removeTestPackages() throws Exception {
554         for (String packageName : getDevice().getUninstallablePackageNames()) {
555             if (mFixedPackages.contains(packageName)) {
556                 continue;
557             }
558             CLog.w("removing leftover package: " + packageName);
559             getDevice().uninstallPackage(packageName);
560         }
561     }
562 
runDeviceTestsAsUser( String pkgName, @Nullable String testClassName, int userId)563     protected void runDeviceTestsAsUser(
564             String pkgName, @Nullable String testClassName, int userId)
565             throws DeviceNotAvailableException {
566         runDeviceTestsAsUser(pkgName, testClassName, /* testMethodName= */ null, userId);
567     }
568 
runDeviceTestsAsUser( String pkgName, @Nullable String testClassName, String testMethodName, int userId)569     protected void runDeviceTestsAsUser(
570             String pkgName, @Nullable String testClassName, String testMethodName, int userId)
571             throws DeviceNotAvailableException {
572         Map<String, String> params = Collections.emptyMap();
573         runDeviceTestsAsUser(pkgName, testClassName, testMethodName, userId, params);
574     }
575 
runDeviceTestsAsUser( String pkgName, @Nullable String testClassName, @Nullable String testMethodName, int userId, Map<String, String> params)576     protected void runDeviceTestsAsUser(
577             String pkgName, @Nullable String testClassName,
578             @Nullable String testMethodName, int userId,
579             Map<String, String> params) throws DeviceNotAvailableException {
580         if (testClassName != null && testClassName.startsWith(".")) {
581             testClassName = pkgName + testClassName;
582         }
583 
584         CLog.i("runDeviceTestsAsUser(): user=%d, pkg=%s class=%s, test=%s", userId, pkgName,
585                 testClassName, testMethodName);
586         runDeviceTests(
587                 getDevice(),
588                 RUNNER,
589                 pkgName,
590                 testClassName,
591                 testMethodName,
592                 userId,
593                 DEFAULT_TEST_TIMEOUT_MILLIS,
594                 DEFAULT_SHELL_TIMEOUT_MILLIS,
595                 0L /* maxInstrumentationTimeoutMs */,
596                 true /* checkResults */,
597                 false /* isHiddenApiCheckDisabled */,
598                 params);
599     }
600 
601     /** Reboots the device and block until the boot complete flag is set. */
rebootAndWaitUntilReady()602     protected void rebootAndWaitUntilReady() throws Exception {
603         getDevice().rebootUntilOnline();
604         assertTrue("Device failed to boot", getDevice().waitForBootComplete(120_000));
605     }
606 
607     /** Returns a boolean value of the system property with the specified key. */
getBooleanSystemProperty(String key, boolean defaultValue)608     protected boolean getBooleanSystemProperty(String key, boolean defaultValue)
609             throws DeviceNotAvailableException {
610         final String[] positiveValues = {"1", "y", "yes", "true", "on"};
611         final String[] negativeValues = {"0", "n", "no", "false", "off"};
612         String propertyValue = getDevice().getProperty(key);
613         if (propertyValue == null || propertyValue.isEmpty()) {
614             return defaultValue;
615         }
616         if (Arrays.asList(positiveValues).contains(propertyValue)) {
617             return true;
618         }
619         if (Arrays.asList(negativeValues).contains(propertyValue)) {
620             return false;
621         }
622         fail("Unexpected value of boolean system property '" + key + "': " + propertyValue);
623         return false;
624     }
625 
626     /** Checks whether it is possible to create the desired number of users. */
canCreateAdditionalUsers(int numberOfUsers)627     protected boolean canCreateAdditionalUsers(int numberOfUsers)
628             throws DeviceNotAvailableException {
629         return listUsers().size() + numberOfUsers <= getMaxNumberOfUsersSupported();
630     }
631 
632     /**
633      * Throws a {@link org.junit.AssumptionViolatedException} if it's not possible to create the
634      * desired number of users.
635      */
assumeCanCreateAdditionalUsers(int numberOfUsers)636     protected void assumeCanCreateAdditionalUsers(int numberOfUsers)
637             throws DeviceNotAvailableException {
638         int maxUsers = getDevice().getMaxNumberOfUsersSupported();
639         assumeTrue("Tests needs at least " + numberOfUsers + " extra users, but device supports "
640                 + "at most " + getMaxNumberOfUsersSupported(),
641                 canCreateAdditionalUsers(numberOfUsers));
642     }
643 
644     /** Checks whether it is possible to start the desired number of users. */
canStartAdditionalUsers(int numberOfUsers)645     protected boolean canStartAdditionalUsers(int numberOfUsers)
646             throws DeviceNotAvailableException {
647         return listRunningUsers().size() + numberOfUsers <= getMaxNumberOfRunningUsersSupported();
648     }
649 
assumeCanStartNewUser()650     protected void assumeCanStartNewUser() throws DeviceNotAvailableException {
651         assumeCanCreateOneManagedUser();
652         assumeTrue("Cannot start a new user", canStartAdditionalUsers(1));
653     }
654 
createUser()655     protected int createUser() throws Exception {
656         int userId = createUser(0);
657         CLog.i("Created user with id %d", userId);
658         // TODO remove this and audit tests so they start users as necessary
659         startUser(userId);
660         return userId;
661     }
662 
createUserAndWaitStart()663     protected int createUserAndWaitStart() throws Exception {
664         int userId = createUser(0);
665         startUserAndWait(userId);
666         return userId;
667     }
668 
createUser(int flags)669     protected int createUser(int flags) throws Exception {
670         boolean guest = FLAG_GUEST == (flags & FLAG_GUEST);
671         boolean ephemeral = FLAG_EPHEMERAL == (flags & FLAG_EPHEMERAL);
672         CLog.i("Creating user with flags %d: guest=%b, ephemeral=%b", flags, guest, ephemeral);
673         // TODO Use ITestDevice.createUser() when guest and ephemeral is available
674         String command ="pm create-user " + (guest ? "--guest " : "")
675                 + (ephemeral ? "--ephemeral " : "") + "TestUser_" + System.currentTimeMillis();
676         CLog.d("Starting command %s", command);
677         String commandOutput = getDevice().executeShellCommand(command);
678         CLog.d("Output for command %s: %s", command, commandOutput);
679 
680         // Extract the id of the new user.
681         String[] tokens = commandOutput.split("\\s+");
682         assertTrue(tokens.length > 0);
683         assertEquals("Command '" + command + "' failed: " + commandOutput, "Success:", tokens[0]);
684         return Integer.parseInt(tokens[tokens.length-1]);
685     }
686 
createManagedProfile(int parentUserId)687     protected int createManagedProfile(int parentUserId) throws DeviceNotAvailableException {
688         String commandOutput = getCreateManagedProfileCommandOutput(parentUserId);
689         return getUserIdFromCreateUserCommandOutput(commandOutput);
690     }
691 
assertCannotCreateManagedProfile(int parentUserId)692     protected void assertCannotCreateManagedProfile(int parentUserId)
693             throws Exception {
694         String commandOutput = getCreateManagedProfileCommandOutput(parentUserId);
695         if (commandOutput.startsWith("Error")) {
696             return;
697         }
698         int userId = getUserIdFromCreateUserCommandOutput(commandOutput);
699         removeUser(userId);
700         fail("Expected not to be able to create a managed profile. Output was: " + commandOutput);
701     }
702 
assumeHasDeviceFeature(String feature)703     private void assumeHasDeviceFeature(String feature) throws DeviceNotAvailableException {
704         assumeTrue("device doesn't have " + feature, hasDeviceFeature(feature));
705     }
706 
assumeDoesNotHaveDeviceFeature(String feature)707     private void assumeDoesNotHaveDeviceFeature(String feature) throws DeviceNotAvailableException {
708         assumeFalse("device has " + feature, hasDeviceFeature(feature));
709     }
710 
711     /**
712      * Used by test cases to add additional checks priort to {@link #setUp()}, so that when it
713      * throws an {@link AssumptionViolatedException} exception nothing is run
714      * (even {@link #tearDown()}).
715      */
assumeTestEnabled()716     protected void assumeTestEnabled() throws Exception {
717     }
718 
assumeCanCreateOneManagedUser()719     protected final void assumeCanCreateOneManagedUser() throws DeviceNotAvailableException {
720         assumeSupportsMultiUser();
721         assumeCanCreateAdditionalUsers(1);
722     }
723 
assumeSupportsMultiUser()724     protected final void assumeSupportsMultiUser() throws DeviceNotAvailableException {
725         // setup isn't always called before this method
726         mSupportsMultiUser = getMaxNumberOfUsersSupported() > 1;
727         assumeTrue("device doesn't support multiple users", mSupportsMultiUser);
728     }
729 
assumeHasMainUser()730     protected final void assumeHasMainUser() throws DeviceNotAvailableException {
731         Integer user = getDevice().getMainUserId();
732         assumeTrue("device doesn't have a main user", user != null);
733     }
assumeHasWifiFeature()734     protected final void assumeHasWifiFeature() throws DeviceNotAvailableException {
735         assumeHasDeviceFeature(FEATURE_WIFI);
736     }
737 
assumeHasTelephonyFeature()738     protected final void assumeHasTelephonyFeature() throws DeviceNotAvailableException {
739         assumeHasDeviceFeature(FEATURE_TELEPHONY);
740     }
741 
assumeSupportsSms()742     protected final void assumeSupportsSms() throws Exception {
743         assumeTrue("device doesn't support SMS", isSmsCapable());
744     }
745 
assumeHasNfcFeatures()746     protected final void assumeHasNfcFeatures() throws DeviceNotAvailableException {
747         assumeHasDeviceFeature(FEATURE_NFC);
748         assumeHasDeviceFeature(FEATURE_NFC_BEAM);
749     }
750 
assumeHasTelephonyAndConnectionServiceFeatures()751     protected final void assumeHasTelephonyAndConnectionServiceFeatures()
752             throws DeviceNotAvailableException {
753         assumeHasTelephonyFeature();
754         assumeHasDeviceFeature(FEATURE_CONNECTION_SERVICE);
755     }
756 
assumeHasSecureLockScreenFeature()757     protected final void assumeHasSecureLockScreenFeature() throws DeviceNotAvailableException {
758         assumeHasDeviceFeature(FEATURE_SECURE_LOCK_SCREEN);
759     }
760 
assumeDoesNotHaveSecureLockScreenFeature()761     protected final void assumeDoesNotHaveSecureLockScreenFeature()
762             throws DeviceNotAvailableException {
763         assumeDoesNotHaveDeviceFeature(FEATURE_SECURE_LOCK_SCREEN);
764     }
765 
assumeHasFileBasedEncryptionAndSecureLockScreenFeatures()766     protected final void assumeHasFileBasedEncryptionAndSecureLockScreenFeatures()
767             throws DeviceNotAvailableException {
768         assumeHasDeviceFeature(FEATURE_FBE);
769         assumeHasSecureLockScreenFeature();
770     }
771 
assumeHasPrintFeature()772     protected final void assumeHasPrintFeature() throws DeviceNotAvailableException {
773         assumeHasDeviceFeature(FEATURE_PRINT);
774     }
775 
assumeHasCameraFeature()776     protected final void assumeHasCameraFeature() throws DeviceNotAvailableException {
777         assumeHasDeviceFeature(FEATURE_CAMERA);
778     }
779 
assumeHasBluetoothFeature()780     protected final void assumeHasBluetoothFeature() throws DeviceNotAvailableException {
781         assumeHasDeviceFeature(FEATURE_BLUETOOTH);
782     }
783 
assumeApiLevel(int min)784     protected final void assumeApiLevel(int min) throws DeviceNotAvailableException {
785         assumeTrue("API level must be >=" + min, getDevice().getApiLevel() >= min);
786     }
787 
getUserIdFromCreateUserCommandOutput(String commandOutput)788     private int getUserIdFromCreateUserCommandOutput(String commandOutput) {
789         // Extract the id of the new user.
790         String[] tokens = commandOutput.split("\\s+");
791         assertTrue(commandOutput + " expected to have format \"Success: {USER_ID}\"",
792                 tokens.length > 0);
793         assertEquals(commandOutput, "Success:", tokens[0]);
794         return Integer.parseInt(tokens[tokens.length-1]);
795     }
796 
getCreateManagedProfileCommandOutput(int parentUserId)797     private String getCreateManagedProfileCommandOutput(int parentUserId)
798             throws DeviceNotAvailableException {
799         String command = "pm create-user --profileOf " + parentUserId + " --managed "
800                 + "TestProfile_" + System.currentTimeMillis();
801         CLog.d("Starting command " + command);
802         String commandOutput = getDevice().executeShellCommand(command);
803         CLog.d("Output for command " + command + ": " + commandOutput);
804         return commandOutput;
805     }
806 
getMainUser()807     protected int getMainUser() throws DeviceNotAvailableException {
808         Integer user = getDevice().getMainUserId();
809         if (user == null) {
810             user = getDevice().getPrimaryUserId();
811             if (user == null) {
812                 user = 0;
813             }
814         }
815         return user;
816     }
817 
getCurrentUser()818     protected int getCurrentUser() throws DeviceNotAvailableException {
819         return getDevice().getCurrentUser();
820     }
821 
getUserSerialNumber(int userId)822     protected int getUserSerialNumber(int userId) throws DeviceNotAvailableException{
823         // TODO: Move this logic to ITestDevice.
824         // dumpsys user output contains lines like "UserInfo{0:Owner:13} serialNo=0 isPrimary=true"
825         final Pattern pattern =
826                 Pattern.compile("UserInfo\\{" + userId + ":[^\\n]*\\sserialNo=(\\d+)\\s");
827         final String commandOutput = getDevice().executeShellCommand("dumpsys user");
828         final Matcher matcher = pattern.matcher(commandOutput);
829         if (matcher.find()) {
830             return Integer.parseInt(matcher.group(1));
831         }
832         fail("Couldn't find serial number for user " + userId);
833         return -1;
834     }
835 
setProfileOwner(String componentName, int userId, boolean expectFailure)836     protected boolean setProfileOwner(String componentName, int userId, boolean expectFailure)
837             throws DeviceNotAvailableException {
838         String command = "dpm set-profile-owner --user " + userId + " '" + componentName + "'";
839         String commandOutput = getDevice().executeShellCommand(command);
840         boolean success = commandOutput.startsWith("Success:");
841         // If we succeeded always log, if we are expecting failure don't log failures
842         // as call stacks for passing tests confuse the logs.
843         if (success || !expectFailure) {
844             CLog.e("Output for command " + command + ": " + commandOutput);
845         } else {
846             CLog.e("Command Failed " + command);
847         }
848         return success;
849     }
850 
setProfileOwnerOrFail(String componentName, int userId)851     protected void setProfileOwnerOrFail(String componentName, int userId)
852             throws Exception {
853         if (!setProfileOwner(componentName, userId, /*expectFailure*/ false)) {
854             // Don't remove system user or initial user that tests require to run on.
855             removeTestAddedUser(userId);
856             fail("Failed to set profile owner");
857         }
858     }
859 
setProfileOwnerExpectingFailure(String componentName, int userId)860     protected void setProfileOwnerExpectingFailure(String componentName, int userId)
861             throws Exception {
862         if (setProfileOwner(componentName, userId, /* expectFailure =*/ true)) {
863             removeTestAddedUser(userId);
864             fail("Setting profile owner should have failed.");
865         }
866     }
867 
setDeviceAdminInner(String componentName, int userId)868     private String setDeviceAdminInner(String componentName, int userId)
869             throws DeviceNotAvailableException {
870         String command = "dpm set-active-admin --user " + userId + " '" + componentName + "'";
871         String commandOutput = getDevice().executeShellCommand(command);
872         return commandOutput;
873     }
874 
setDeviceAdmin(String componentName, int userId)875     protected void setDeviceAdmin(String componentName, int userId)
876             throws DeviceNotAvailableException {
877         String commandOutput = setDeviceAdminInner(componentName, userId);
878         CLog.d("Output for command " + commandOutput
879                 + ": " + commandOutput);
880         assertTrue(commandOutput + " expected to start with \"Success:\"",
881                 commandOutput.startsWith("Success:"));
882     }
883 
setDeviceAdminExpectingFailure(String componentName, int userId, String errorMessage)884     protected void setDeviceAdminExpectingFailure(String componentName, int userId,
885             String errorMessage) throws DeviceNotAvailableException {
886         String commandOutput = setDeviceAdminInner(componentName, userId);
887         if (!commandOutput.contains(errorMessage)) {
888             fail(commandOutput + " expected to contain \"" + errorMessage + "\"");
889         }
890     }
891 
setDeviceOwner(String componentName, int userId, boolean expectFailure)892     protected boolean setDeviceOwner(String componentName, int userId, boolean expectFailure)
893             throws DeviceNotAvailableException {
894         if (isHeadlessSystemUserMode()) {
895             assumeNotNull("Devices in headles system user mode require a main user to set a device "
896                     + "owner.", getDevice().getMainUserId());
897         }
898         String command = "dpm set-device-owner --user " + userId + " '" + componentName + "'";
899         String commandOutput = getDevice().executeShellCommand(command);
900         boolean success = commandOutput.startsWith("Success:");
901         // If we succeeded always log, if we are expecting failure don't log failures
902         // as call stacks for passing tests confuse the logs.
903         if (success || !expectFailure) {
904             CLog.d("Output for command " + command + ": " + commandOutput);
905         } else {
906             CLog.d("Command Failed " + command);
907         }
908         return success;
909     }
910 
setDeviceOwnerOrFail(String componentName, int userId)911     protected void setDeviceOwnerOrFail(String componentName, int userId)
912             throws Exception {
913         assertTrue(setDeviceOwner(componentName, userId, /* expectFailure =*/ false));
914     }
915 
setDeviceOwnerExpectingFailure(String componentName, int userId)916     protected void setDeviceOwnerExpectingFailure(String componentName, int userId)
917             throws Exception {
918         assertFalse(setDeviceOwner(componentName, userId, /* expectFailure =*/ true));
919     }
920 
921 
affiliateUsers(String deviceAdminPkg, int userId1, int userId2)922     protected void affiliateUsers(String deviceAdminPkg, int userId1, int userId2)
923             throws Exception {
924         CLog.d("Affiliating users %d and %d on admin package %s", userId1, userId2, deviceAdminPkg);
925         runDeviceTestsAsUser(
926                 deviceAdminPkg, ".AffiliationTest", "testSetAffiliationId1", userId1);
927         runDeviceTestsAsUser(
928                 deviceAdminPkg, ".AffiliationTest", "testSetAffiliationId1", userId2);
929     }
930 
getSettings(String namespace, String name, int userId)931     protected String getSettings(String namespace, String name, int userId)
932             throws DeviceNotAvailableException {
933         String command = "settings --user " + userId + " get " + namespace + " " + name;
934         String commandOutput = getDevice().executeShellCommand(command);
935         CLog.d("Output for command " + command + ": " + commandOutput);
936         return commandOutput.replace("\n", "").replace("\r", "");
937     }
938 
putSettings(String namespace, String name, String value, int userId)939     protected void putSettings(String namespace, String name, String value, int userId)
940             throws DeviceNotAvailableException {
941         String command = "settings --user " + userId + " put " + namespace + " " + name
942                 + " " + value;
943         String commandOutput = getDevice().executeShellCommand(command);
944         CLog.d("Output for command " + command + ": " + commandOutput);
945     }
946 
removeAdmin(String componentName, int userId)947     protected boolean removeAdmin(String componentName, int userId)
948             throws DeviceNotAvailableException {
949         String command = "dpm remove-active-admin --user " + userId + " '" + componentName + "'";
950         String commandOutput = getDevice().executeShellCommand(command);
951         CLog.d("Output for command " + command + ": " + commandOutput);
952         return commandOutput.startsWith("Success:");
953     }
954 
955     // Tries to remove and profile or device owners it finds.
removeOwners()956     protected void removeOwners() throws DeviceNotAvailableException {
957         String command = "dumpsys device_policy";
958         String commandOutput = getDevice().executeShellCommand(command);
959         String[] lines = commandOutput.split("\\r?\\n");
960         for (int i = 0; i < lines.length; ++i) {
961             String line = lines[i].trim();
962             if (line.contains("Profile Owner")) {
963                 // Line is "Profile owner (User <id>):
964                 String[] tokens = line.split("\\(|\\)| ");
965                 int userId = Integer.parseInt(tokens[4]);
966                 i++;
967                 line = lines[i].trim();
968                 // Line is admin=ComponentInfo{<component>}
969                 tokens = line.split("\\{|\\}");
970                 String componentName = tokens[1];
971                 CLog.w("Cleaning up profile owner " + userId + " " + componentName);
972                 removeAdmin(componentName, userId);
973             } else if (line.contains("Device Owner:")) {
974                 i++;
975                 line = lines[i].trim();
976                 // Line is admin=ComponentInfo{<component>}
977                 String[] tokens = line.split("\\{|\\}");
978                 String componentName = tokens[1];
979                 // Skip to user id line.
980                 for (int j = i + 1; j < lines.length; ++j) {
981                     line = lines[j].trim();
982                     // Line is User ID: <N>
983                     if (line.contains("User ID:")) {
984                         tokens = line.split(":");
985                         int userId = Integer.parseInt(tokens[1].trim());
986                         CLog.w("Cleaning up device owner " + userId + " " + componentName);
987                         removeAdmin(componentName, userId);
988                         return;
989                     }
990                 }
991                 throw new RuntimeException(
992                         "Error finding a user id for this device owner in dumpsys.");
993             }
994         }
995     }
996 
997     /**
998      * Runs pm enable command to enable a package or component. Returns the command result.
999      */
enableComponentOrPackage(int userId, String packageOrComponent)1000     protected String enableComponentOrPackage(int userId, String packageOrComponent)
1001             throws DeviceNotAvailableException {
1002         String command = "pm enable --user " + userId + " " + packageOrComponent;
1003         String result = getDevice().executeShellCommand(command);
1004         CLog.d("Output for command " + command + ": " + result);
1005         return result;
1006     }
1007 
1008     /**
1009      * Runs pm disable command to disable a package or component. Returns the command result.
1010      */
disableComponentOrPackage(int userId, String packageOrComponent)1011     protected String disableComponentOrPackage(int userId, String packageOrComponent)
1012             throws DeviceNotAvailableException {
1013         String command = "pm disable --user " + userId + " " + packageOrComponent;
1014         String result = getDevice().executeShellCommand(command);
1015         CLog.d("Output for command " + command + ": " + result);
1016         return result;
1017     }
1018 
1019     protected interface SuccessCondition {
check()1020         boolean check() throws Exception;
1021     }
1022 
waitUntilUserRemoved(int userId)1023     protected void waitUntilUserRemoved(int userId) throws Exception {
1024         tryWaitForSuccess(() -> !listUsers().contains(userId),
1025                 "The user " + userId + " has not been removed",
1026                 TIMEOUT_USER_REMOVED_MILLIS
1027                 );
1028     }
1029 
tryWaitForSuccess(SuccessCondition successCondition, String failureMessage, long timeoutMillis)1030     protected void tryWaitForSuccess(SuccessCondition successCondition, String failureMessage,
1031             long timeoutMillis) throws Exception {
1032         long epoch = System.currentTimeMillis();
1033         while (System.currentTimeMillis() - epoch <= timeoutMillis) {
1034             RunUtil.getDefault().sleep(WAIT_SAMPLE_INTERVAL_MILLIS);
1035             if (successCondition.check()) {
1036                 return;
1037             }
1038         }
1039         fail(failureMessage);
1040     }
1041 
1042     /**
1043      * Sets a user restriction via SetPolicyActivity.
1044      * <p>IMPORTANT: The package that contains SetPolicyActivity must have been installed prior to
1045      * calling this method.
1046      * @param key user restriction key
1047      * @param value true if we should set the restriction, false if we should clear it
1048      * @param userId userId to set/clear the user restriction on
1049      * @param packageName package where SetPolicyActivity is installed
1050      * @return The output of the command
1051      * @throws DeviceNotAvailableException
1052      */
changeUserRestriction(String key, boolean value, int userId, String packageName)1053     protected String changeUserRestriction(String key, boolean value, int userId,
1054             String packageName) throws DeviceNotAvailableException {
1055         return changePolicy(getUserRestrictionCommand(value),
1056                 " --es extra-restriction-key " + key, userId, packageName);
1057     }
1058 
1059     /**
1060      * Same as {@link #changeUserRestriction(String, boolean, int, String)} but asserts that it
1061      * succeeds.
1062      */
changeUserRestrictionOrFail(String key, boolean value, int userId, String packageName)1063     protected void changeUserRestrictionOrFail(String key, boolean value, int userId,
1064             String packageName) throws DeviceNotAvailableException {
1065         changePolicyOrFail(getUserRestrictionCommand(value), " --es extra-restriction-key " + key,
1066                 userId, packageName);
1067     }
1068 
1069     /**
1070      * Sets some policy via SetPolicyActivity.
1071      * <p>IMPORTANT: The package that contains SetPolicyActivity must have been installed prior to
1072      * calling this method.
1073      * @param command command to pass to SetPolicyActivity
1074      * @param extras extras to pass to SetPolicyActivity
1075      * @param userId the userId where we invoke SetPolicyActivity
1076      * @param packageName where SetPolicyActivity is installed
1077      * @return The output of the command
1078      * @throws DeviceNotAvailableException
1079      */
changePolicy(String command, String extras, int userId, String packageName)1080     protected String changePolicy(String command, String extras, int userId, String packageName)
1081             throws DeviceNotAvailableException {
1082         String adbCommand = "am start -W --user " + userId
1083                 + " -c android.intent.category.DEFAULT "
1084                 + " --es extra-command " + command
1085                 + " " + extras
1086                 + getAdditionalExtrasForSetPolicyActivity()
1087                 + " " + packageName + "/.SetPolicyActivity";
1088         String commandOutput = getDevice().executeShellCommand(adbCommand);
1089         CLog.d("Output for command " + adbCommand + ": " + commandOutput);
1090         return commandOutput;
1091     }
1092 
getAdditionalExtrasForSetPolicyActivity()1093     protected String getAdditionalExtrasForSetPolicyActivity() {
1094         return "";
1095     }
1096 
1097     /**
1098      * Same as {@link #changePolicy(String, String, int, String)} but asserts that it succeeds.
1099      */
changePolicyOrFail(String command, String extras, int userId, String packageName)1100     protected void changePolicyOrFail(String command, String extras, int userId,
1101             String packageName) throws DeviceNotAvailableException {
1102         String commandOutput = changePolicy(command, extras, userId, packageName);
1103         assertTrue("Command was expected to succeed " + commandOutput,
1104                 commandOutput.contains("Status: ok"));
1105     }
1106 
getUserRestrictionCommand(boolean setRestriction)1107     private String getUserRestrictionCommand(boolean setRestriction) {
1108         if (setRestriction) {
1109             return "add-restriction";
1110         }
1111         return "clear-restriction";
1112     }
1113 
1114     /**
1115      * Set lockscreen password / work challenge for the given user, null or "" means clear
1116      * IMPORTANT: prefer to use {@link #TEST_PASSWORD} for primary user, otherwise if the test
1117      * terminates before cleaning password up, the device will be unusable for further testing.
1118      */
changeUserCredential(String newCredential, String oldCredential, int userId)1119     protected void changeUserCredential(String newCredential, String oldCredential, int userId)
1120             throws DeviceNotAvailableException {
1121         final String oldCredentialArgument = (oldCredential == null || oldCredential.isEmpty()) ? ""
1122                 : ("--old " + oldCredential);
1123         if (newCredential != null && !newCredential.isEmpty()) {
1124             String commandOutput = getDevice().executeShellCommand(String.format(
1125                     "cmd lock_settings set-password --user %d %s %s", userId, oldCredentialArgument,
1126                     newCredential));
1127             if (!commandOutput.startsWith("Password set to")) {
1128                 fail("Failed to set user credential: " + commandOutput);
1129             }
1130         } else {
1131             String commandOutput = getDevice().executeShellCommand(String.format(
1132                     "cmd lock_settings clear --user %d %s", userId, oldCredentialArgument));
1133             if (!commandOutput.startsWith("Lock credential cleared")) {
1134                 fail("Failed to clear user credential: " + commandOutput);
1135             }
1136         }
1137     }
1138 
1139     /**
1140      * Verifies the lock credential for the given user.
1141      *
1142      * @param credential The credential to verify.
1143      * @param userId The id of the user.
1144      */
verifyUserCredential(String credential, int userId)1145     protected void verifyUserCredential(String credential, int userId)
1146             throws DeviceNotAvailableException {
1147         String commandOutput = verifyUserCredentialCommandOutput(credential, userId);
1148         if (!commandOutput.startsWith(VERIFY_CREDENTIAL_CONFIRMATION)) {
1149             fail("Failed to verify user credential: " + commandOutput);
1150         }
1151      }
1152 
1153     /**
1154      * Verifies the lock credential for the given user, which unlocks the user, and returns
1155      * whether it was successful or not.
1156      *
1157      * @param credential The credential to verify.
1158      * @param userId The id of the user.
1159      */
verifyUserCredentialIsCorrect(String credential, int userId)1160     protected boolean verifyUserCredentialIsCorrect(String credential, int userId)
1161             throws DeviceNotAvailableException {
1162         String commandOutput = verifyUserCredentialCommandOutput(credential, userId);
1163         return commandOutput.startsWith(VERIFY_CREDENTIAL_CONFIRMATION);
1164     }
1165 
1166     /**
1167      * Verifies the lock credential for the given user, which unlocks the user. Returns the
1168      * commandline output, which includes whether the verification was successful.
1169      *
1170      * @param credential The credential to verify.
1171      * @param userId The id of the user.
1172      * @return The command line output.
1173      */
verifyUserCredentialCommandOutput(String credential, int userId)1174     protected String verifyUserCredentialCommandOutput(String credential, int userId)
1175             throws DeviceNotAvailableException {
1176         final String credentialArgument = (credential == null || credential.isEmpty())
1177                 ? "" : ("--old " + credential);
1178         String commandOutput = getDevice().executeShellCommand(String.format(
1179                 "cmd lock_settings verify --user %d %s", userId, credentialArgument));
1180         return commandOutput;
1181     }
1182 
wakeupAndDismissKeyguard()1183     protected void wakeupAndDismissKeyguard() throws Exception {
1184         executeShellCommand("input keyevent KEYCODE_WAKEUP");
1185         executeShellCommand("wm dismiss-keyguard");
1186     }
1187 
pressPowerButton()1188     protected void pressPowerButton() throws Exception {
1189         executeShellCommand("input keyevent KEYCODE_POWER");
1190     }
1191 
stayAwake()1192     private void stayAwake() throws Exception {
1193         executeShellCommand(
1194                 "settings put global stay_on_while_plugged_in " + STAY_ON_WHILE_PLUGGED_IN_FLAGS);
1195     }
1196 
startActivityAsUser(int userId, String packageName, String activityName)1197     protected void startActivityAsUser(int userId, String packageName, String activityName)
1198         throws Exception {
1199         wakeupAndDismissKeyguard();
1200         String command = "am start -W --user " + userId + " " + packageName + "/" + activityName;
1201         getDevice().executeShellCommand(command);
1202     }
1203 
getDefaultLauncher()1204     protected String getDefaultLauncher() throws Exception {
1205         return getDevice().executeShellCommand("cmd role get-role-holders --user "
1206                 + getDevice().getCurrentUser() + " android.app.role.HOME").trim();
1207     }
1208 
assumeIsDeviceAb()1209     void assumeIsDeviceAb() throws DeviceNotAvailableException {
1210         final String result = getDevice().executeShellCommand("getprop ro.build.ab_update").trim();
1211         assumeTrue("not device AB", "true".equalsIgnoreCase(result));
1212     }
1213 
isHeadlessSystemUserMode()1214     boolean isHeadlessSystemUserMode() throws DeviceNotAvailableException {
1215         return isHeadlessSystemUserMode(getDevice());
1216     }
1217 
isHeadlessSystemUserMode(ITestDevice device)1218     public static boolean isHeadlessSystemUserMode(ITestDevice device)
1219             throws DeviceNotAvailableException {
1220         return device.isHeadlessSystemUserMode();
1221     }
1222 
assumeHeadlessSystemUserMode(String reason)1223     protected void assumeHeadlessSystemUserMode(String reason)
1224             throws DeviceNotAvailableException {
1225         assumeTrue("Skipping test on non-headless system user mode. Reason: " + reason,
1226                 isHeadlessSystemUserMode());
1227     }
1228 
grantDpmWrapperPermissions(String deviceAdminPkg, int userId)1229     protected void grantDpmWrapperPermissions(String deviceAdminPkg, int userId) throws Exception {
1230         // TODO(b/176993670): INTERACT_ACROSS_USERS is needed by DevicePolicyManagerWrapper to
1231         // send ordered broadcasts to the test user. The permission is already available to the
1232         // packages installed by the host side test (as they're installed with -g), but need to be
1233         // granted for users created by the test, as the package is intalled by code
1234         // (DPMS.manageUserUnchecked(), which doesn't grant it (as this is a privileged permission
1235         // that's not available to 3rd party apps). If we get rid of DevicePolicyManagerWrapper,
1236         // we won't need to grant it anymore.
1237         grantPermission(deviceAdminPkg, PERMISSION_INTERACT_ACROSS_USERS, userId, "its PO needs to "
1238                 + "send ordered broadcasts to user 0");
1239 
1240         // Probably not needed anymore, but it doesn't hurt to keep...
1241         allowTestApiAccess(deviceAdminPkg);
1242     }
1243 
1244     /**
1245      * Grants access to APIs marked as {@code @TestApi}.
1246      *
1247      * <p><b>Note:</b> the {@code application} tag of the app's manifest must contain
1248      * {@code android:debuggable="true"}, otherwise it won't work on {@code user} builds.
1249      */
allowTestApiAccess(String pgkName)1250     protected void allowTestApiAccess(String pgkName) throws Exception {
1251         CLog.i("Granting ALLOW_TEST_API_ACCESS to package %s", pgkName);
1252         executeShellCommand("am compat enable ALLOW_TEST_API_ACCESS %s", pgkName);
1253     }
1254 
grantPermission(String pkg, String permission, int userId, String reason)1255     protected void grantPermission(String pkg, String permission, int userId, String reason)
1256             throws Exception {
1257         CLog.i("Granting permission %s to package (%s) on user %d%s", pkg, permission, userId,
1258                 (reason == null ? "" : "(reason: " + reason + ")"));
1259         executeShellCommand("pm grant --user %d %s %s", userId, pkg, permission);
1260     }
1261 
revokePermission(String pkg, String permission, int userId)1262     protected void revokePermission(String pkg, String permission, int userId) throws Exception {
1263         CLog.i("Revoking permission %s to package (%s) on user %d", pkg, permission, userId);
1264         executeShellCommand("pm revoke --user %d %s %s", userId, pkg, permission);
1265     }
1266 
1267     /** Find effective restriction for user */
isRestrictionSetOnUser(int userId, String restriction)1268     protected boolean isRestrictionSetOnUser(int userId, String restriction) throws Exception {
1269         String commandOutput = getDevice().executeShellCommand("dumpsys user");
1270         String[] outputLines = commandOutput.split("\\n");
1271         Pattern userPattern = Pattern.compile("(^.*)UserInfo\\{" + userId + ":.*$");
1272         Pattern restrictionPattern = Pattern.compile("(^.*)Effective\\srestrictions\\:.*$");
1273 
1274         boolean userFound = false;
1275         boolean restrictionsFound = false;
1276         int lastIndent = -1;
1277 
1278         for (String line : outputLines) {
1279             // Starting a new block of user infos
1280             if (!line.startsWith(Strings.repeat(" ", lastIndent + 1))) {
1281                 CLog.d("User %d restrictions found, no matched restriction.", userId);
1282                 return false;
1283             }
1284             //First, try matching user pattern
1285             Matcher userMatcher = userPattern.matcher(line);
1286             if (userMatcher.find()) {
1287                 CLog.d("User %d found in dumpsys, finding restrictions.", userId);
1288                 userFound = true;
1289                 lastIndent = userMatcher.group(1).length();
1290             }
1291 
1292             // Second, try matching restriction
1293             Matcher restrictionMatcher = restrictionPattern.matcher(line);
1294             if (userFound && restrictionMatcher.find()) {
1295                 CLog.d("User %d restrictions found, finding exact restriction.", userId);
1296                 restrictionsFound = true;
1297                 lastIndent = restrictionMatcher.group(1).length();
1298             }
1299 
1300             if (restrictionsFound && line.contains(restriction)) {
1301                 return true;
1302             }
1303         }
1304         if (!userFound) {
1305             CLog.e("User %d not found in dumpsys.", userId);
1306         }
1307         if (!restrictionsFound) {
1308             CLog.d("User %d found in dumpsys, but restrictions not found.", userId);
1309         }
1310         return false;
1311     }
1312 
1313     /**
1314      * Generates instrumentation arguments that indicate the device-side test is exercising device
1315      * owner APIs.
1316      *
1317      * <p>This is needed for hostside tests that use the same class hierarchy for both device and
1318      * profile owner tests, as on headless system user mode the test side must decide whether to
1319      * use its "local DPC" or wrap the calls to the system user DPC.
1320      */
getParamsForDeviceOwnerTest()1321     protected static Map<String, String> getParamsForDeviceOwnerTest() {
1322         Map<String, String> params = new HashMap<>();
1323         params.put("admin_type", "DeviceOwner");
1324         return params;
1325     }
1326 
isTv()1327     boolean isTv() throws DeviceNotAvailableException {
1328         return hasDeviceFeature(FEATURE_LEANBACK);
1329     }
1330 
isAutomotive()1331     boolean isAutomotive() throws DeviceNotAvailableException {
1332         return hasDeviceFeature(FEATURE_AUTOMOTIVE);
1333     }
1334 
pushUpdateFileToDevice(String fileName)1335     void pushUpdateFileToDevice(String fileName)
1336             throws IOException, DeviceNotAvailableException {
1337         File file = File.createTempFile(
1338                 fileName.split("\\.")[0], "." + fileName.split("\\.")[1]);
1339         try (OutputStream outputStream = new FileOutputStream(file)) {
1340             InputStream inputStream = getClass().getResourceAsStream("/" + fileName);
1341             ByteStreams.copy(inputStream, outputStream);
1342         }
1343 
1344         getDevice().pushFile(file, TEST_UPDATE_LOCATION + "/" + fileName);
1345         file.delete();
1346     }
1347 
hasService(String service)1348     boolean hasService(String service) {
1349         String command = "service check " + service;
1350         try {
1351             String commandOutput = getDevice().executeShellCommand(command);
1352             return !commandOutput.contains("not found");
1353         } catch (Exception e) {
1354             CLog.w("Exception running '" + command + "': " + e);
1355             return false;
1356         }
1357     }
1358 
sleep(int timeMs)1359     void sleep(int timeMs) throws InterruptedException {
1360         CLog.d("Sleeping %d ms", timeMs);
1361         RunUtil.getDefault().sleep(timeMs);
1362     }
1363 
isSmsCapable()1364     private boolean isSmsCapable() throws Exception {
1365         String output = getDevice().executeShellCommand("dumpsys phone");
1366         if (output.contains("isSmsCapable=true")) {
1367             CLog.d("Device is SMS capable");
1368             return true;
1369         }
1370         CLog.d("Device is not SMS capable");
1371         return false;
1372     }
1373 }
1374