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