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