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