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