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