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