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