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