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