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