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