1 /* 2 * Copyright (C) 2020 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 android.appsecurity.cts; 18 19 import static android.appsecurity.cts.Utils.waitForBootCompleted; 20 21 import static org.hamcrest.CoreMatchers.is; 22 import static org.hamcrest.Matchers.greaterThan; 23 import static org.junit.Assert.assertNotNull; 24 import static org.junit.Assert.assertThat; 25 import static org.junit.Assert.fail; 26 import static org.junit.Assume.assumeTrue; 27 28 import android.cts.host.utils.DisableDeviceConfigSyncRule; 29 30 import com.android.compatibility.common.util.ApiLevelUtil; 31 import com.android.compatibility.common.util.HostSideTestUtils; 32 import com.android.tradefed.device.DeviceNotAvailableException; 33 import com.android.tradefed.log.LogUtil.CLog; 34 import com.android.tradefed.testtype.DeviceJUnit4ClassRunner; 35 import com.android.tradefed.testtype.junit4.BaseHostJUnit4Test; 36 import com.android.tradefed.util.CommandResult; 37 import com.android.tradefed.util.CommandStatus; 38 import com.android.tradefed.util.RunUtil; 39 40 import org.junit.After; 41 import org.junit.Before; 42 import org.junit.Rule; 43 import org.junit.Test; 44 import org.junit.runner.RunWith; 45 46 import java.util.ArrayList; 47 import java.util.concurrent.TimeUnit; 48 import java.util.regex.Matcher; 49 import java.util.regex.Pattern; 50 51 /** 52 * Set of tests that verify behavior of Resume on Reboot, if supported. 53 * <p> 54 * Note that these tests drive PIN setup manually instead of relying on device 55 * administrators, which are not supported by all devices. 56 */ 57 @RunWith(DeviceJUnit4ClassRunner.class) 58 public class ResumeOnRebootHostTest extends BaseHostJUnit4Test { 59 private static final String TAG = "ResumeOnRebootTest"; 60 61 private static final String PKG = "com.android.cts.encryptionapp"; 62 private static final String CLASS = PKG + ".EncryptionAppTest"; 63 private static final String APK = "CtsEncryptionApp.apk"; 64 65 private static final String OTHER_APK = "CtsSplitApp.apk"; 66 private static final String OTHER_PKG = "com.android.cts.splitapp"; 67 68 private static final String FEATURE_REBOOT_ESCROW = "feature:android.hardware.reboot_escrow"; 69 private static final String FEATURE_DEVICE_ADMIN = "feature:android.software.device_admin"; 70 private static final String FEATURE_SECURE_LOCK_SCREEN = 71 "feature:android.software.secure_lock_screen"; 72 private static final String FEATURE_WATCH = "android.hardware.type.watch"; 73 74 private static final long SHUTDOWN_TIME_MS = TimeUnit.SECONDS.toMillis(30); 75 private static final int USER_SYSTEM = 0; 76 77 private static final int USER_SWITCH_TIMEOUT_SECONDS = 10; 78 private static final long USER_SWITCH_WAIT = TimeUnit.SECONDS.toMillis(10); 79 private static final int UNLOCK_BROADCAST_WAIT_SECONDS = 10; 80 81 // This is the PIN set in EncryptionAppTest.testSetUp() 82 private static final String DEFAULT_PIN = "1234"; 83 84 private boolean mSupportsMultiUser; 85 private String mOriginalVerifyAdbInstallerSetting = null; 86 87 @Rule(order = 0) 88 public BootCountTrackerRule mBootCountTrackingRule = new BootCountTrackerRule(this, 0); 89 90 @Rule(order = 1) 91 public NormalizeScreenStateRule mNoDozeRule = new NormalizeScreenStateRule(this); 92 93 @Rule(order = 2) 94 public DisableDeviceConfigSyncRule mDisableDeviceConfigSync = 95 new DisableDeviceConfigSyncRule(this); 96 97 @Before setUp()98 public void setUp() throws Exception { 99 assertNotNull(getAbi()); 100 assertNotNull(getBuild()); 101 102 mSupportsMultiUser = getDevice().getMaxNumberOfUsersSupported() > 1; 103 104 normalizeUserStates(); 105 setScreenStayOnValue(true); 106 mOriginalVerifyAdbInstallerSetting = 107 getDevice().getSetting("global", "verifier_verify_adb_installs"); 108 getDevice().setSetting("global", "verifier_verify_adb_installs", "0"); 109 removeTestPackages(); 110 deviceSetupServerBasedParameter(); 111 if (getDevice().hasFeature(FEATURE_WATCH)) { 112 // Prevent charging screen from causing keyguard to report as occluded when device is 113 // locked. 114 unplugBattery(); 115 } 116 } 117 118 @After tearDown()119 public void tearDown() throws Exception { 120 removeTestPackages(); 121 deviceCleanupServerBasedParameter(); 122 if (mOriginalVerifyAdbInstallerSetting != null) { 123 getDevice().setSetting( 124 "global", "verifier_verify_adb_installs", 125 mOriginalVerifyAdbInstallerSetting); 126 } 127 setScreenStayOnValue(false); 128 resetBatteryOverride(); 129 } 130 131 @Test resumeOnReboot_ManagedProfile_Success()132 public void resumeOnReboot_ManagedProfile_Success() throws Exception { 133 assumeTrue("Device isn't at least S or has no lock screen", isSupportedSDevice()); 134 assumeTrue("Device does not support file-based encryption", supportFileBasedEncryption()); 135 136 if (!getDevice().hasFeature("android.software.managed_users")) { 137 CLog.v(TAG, "Device doesn't support managed users; skipping test"); 138 return; 139 } 140 141 int[] users = prepareUsers(1); 142 int initialUser = users[0]; 143 144 int managedUserId = createManagedProfile(initialUser); 145 146 try { 147 // Set up test app and secure lock screens 148 installTestPackages(); 149 150 deviceSetup(initialUser); 151 deviceRequestLskf(); 152 deviceLock(initialUser); 153 deviceEnterLskf(initialUser); 154 deviceRebootAndApply(); 155 156 runDeviceTestsAsUser("testVerifyUnlockedAndDismiss", initialUser); 157 } finally { 158 stopUserAsync(managedUserId); 159 removeUser(managedUserId); 160 161 // Remove secure lock screens and tear down test app 162 runDeviceTestsAsUser("testTearDown", initialUser); 163 164 deviceClearLskf(); 165 } 166 } 167 168 @Test resumeOnReboot_TwoUsers_SingleUserUnlock_Success()169 public void resumeOnReboot_TwoUsers_SingleUserUnlock_Success() throws Exception { 170 assumeTrue("Device isn't at least S or has no lock screen", isSupportedSDevice()); 171 assumeTrue("Device does not support file-based encryption", supportFileBasedEncryption()); 172 173 if (!mSupportsMultiUser) { 174 CLog.v(TAG, "Device doesn't support multi-user; skipping test"); 175 return; 176 } 177 178 int[] users = prepareUsers(2); 179 int initialUser = users[0]; 180 int secondaryUser = users[1]; 181 182 try { 183 // Set up test app and secure lock screens 184 installTestPackages(); 185 186 switchUser(secondaryUser); 187 deviceSetup(secondaryUser); 188 189 switchUser(initialUser); 190 deviceSetup(initialUser); 191 192 deviceRequestLskf(); 193 194 deviceLock(initialUser); 195 deviceEnterLskf(initialUser); 196 197 deviceRebootAndApply(); 198 199 // Try to start early to calm down broadcast storms. 200 getDevice().startUser(secondaryUser); 201 202 switchUser(initialUser); 203 runDeviceTestsAsUser("testVerifyUnlockedAndDismiss", initialUser); 204 205 switchUser(secondaryUser); 206 runDeviceTestsAsUser("testVerifyLockedAndDismiss", secondaryUser); 207 } finally { 208 // Remove secure lock screens and tear down test app 209 switchUser(secondaryUser); 210 runDeviceTestsAsUser("testTearDown", secondaryUser); 211 switchUser(initialUser); 212 runDeviceTestsAsUser("testTearDown", initialUser); 213 214 deviceClearLskf(); 215 } 216 } 217 218 @Test resumeOnReboot_TwoUsers_BothUserUnlock_Success()219 public void resumeOnReboot_TwoUsers_BothUserUnlock_Success() throws Exception { 220 assumeTrue("Device isn't at least S or has no lock screen", isSupportedSDevice()); 221 assumeTrue("Device does not support file-based encryption", supportFileBasedEncryption()); 222 223 if (!mSupportsMultiUser) { 224 CLog.v(TAG, "Device doesn't support multi-user; skipping test"); 225 return; 226 } 227 228 int[] users = prepareUsers(2); 229 int initialUser = users[0]; 230 int secondaryUser = users[1]; 231 232 try { 233 installTestPackages(); 234 235 switchUser(secondaryUser); 236 deviceSetup(secondaryUser); 237 238 switchUser(initialUser); 239 deviceSetup(initialUser); 240 241 deviceRequestLskf(); 242 243 deviceLock(initialUser); 244 deviceEnterLskf(initialUser); 245 246 switchUser(secondaryUser); 247 deviceEnterLskf(secondaryUser); 248 249 deviceRebootAndApply(); 250 251 // Try to start early to calm down broadcast storms. 252 getDevice().startUser(secondaryUser); 253 254 switchUser(initialUser); 255 runDeviceTestsAsUser("testVerifyUnlockedAndDismiss", initialUser); 256 257 switchUser(secondaryUser); 258 runDeviceTestsAsUser("testVerifyUnlockedAndDismiss", secondaryUser); 259 } finally { 260 // Remove secure lock screens and tear down test app 261 switchUser(secondaryUser); 262 runDeviceTestsAsUser("testTearDown", secondaryUser); 263 switchUser(initialUser); 264 runDeviceTestsAsUser("testTearDown", initialUser); 265 266 deviceClearLskf(); 267 } 268 } 269 270 @Test resumeOnReboot_SingleUser_ServerBased_Success()271 public void resumeOnReboot_SingleUser_ServerBased_Success() throws Exception { 272 assumeTrue("Device isn't at least S or has no lock screen", isSupportedSDevice()); 273 assumeTrue("Device does not support file-based encryption", supportFileBasedEncryption()); 274 275 int[] users = prepareUsers(1); 276 int initialUser = users[0]; 277 278 try { 279 installTestPackages(); 280 281 deviceSetup(initialUser); 282 deviceRequestLskf(); 283 deviceLock(initialUser); 284 deviceEnterLskf(initialUser); 285 deviceRebootAndApply(); 286 287 runDeviceTestsAsUser("testVerifyUnlockedAndDismiss", initialUser); 288 // Check service interaction as user 0 since RoR always runs as system user. 289 runDeviceTestsAsUser("testCheckServiceInteraction", /* userId= */ 0); 290 } finally { 291 // Remove secure lock screens and tear down test app 292 runDeviceTestsAsUser("testTearDown", initialUser); 293 294 deviceClearLskf(); 295 } 296 } 297 298 @Test resumeOnReboot_SingleUser_MultiClient_ClientASuccess()299 public void resumeOnReboot_SingleUser_MultiClient_ClientASuccess() throws Exception { 300 assumeTrue("Device isn't at least S or has no lock screen", isSupportedSDevice()); 301 assumeTrue("Device does not support file-based encryption", supportFileBasedEncryption()); 302 303 int[] users = prepareUsers(1); 304 int initialUser = users[0]; 305 306 final String clientA = "ClientA"; 307 final String clientB = "ClientB"; 308 try { 309 installTestPackages(); 310 311 deviceSetup(initialUser); 312 deviceRequestLskf(clientA); 313 deviceRequestLskf(clientB); 314 315 deviceLock(initialUser); 316 deviceEnterLskf(initialUser); 317 318 // Client B's clear shouldn't affect client A's preparation. 319 deviceClearLskf(clientB); 320 deviceRebootAndApply(clientA); 321 322 runDeviceTestsAsUser("testVerifyUnlockedAndDismiss", initialUser); 323 // Check service interaction as user 0 since RoR always runs as system user. 324 runDeviceTestsAsUser("testCheckServiceInteraction", /* userId= */ 0); 325 } finally { 326 // Remove secure lock screens and tear down test app 327 runDeviceTestsAsUser("testTearDown", initialUser); 328 329 deviceClearLskf(); 330 } 331 } 332 333 @Test resumeOnReboot_SingleUser_MultiClient_ClientBSuccess()334 public void resumeOnReboot_SingleUser_MultiClient_ClientBSuccess() throws Exception { 335 assumeTrue("Device isn't at least S or has no lock screen", isSupportedSDevice()); 336 assumeTrue("Device does not support file-based encryption", supportFileBasedEncryption()); 337 338 int[] users = prepareUsers(1); 339 int initialUser = users[0]; 340 341 final String clientA = "ClientA"; 342 final String clientB = "ClientB"; 343 try { 344 installTestPackages(); 345 346 deviceSetup(initialUser); 347 deviceRequestLskf(clientA); 348 349 deviceLock(initialUser); 350 deviceEnterLskf(initialUser); 351 352 // Both clients have prepared 353 deviceRequestLskf(clientB); 354 deviceRebootAndApply(clientB); 355 356 runDeviceTestsAsUser("testVerifyUnlockedAndDismiss", initialUser); 357 // Check service interaction as user 0 since RoR always runs as system user. 358 runDeviceTestsAsUser("testCheckServiceInteraction", /* userId= */ 0); 359 } finally { 360 // Remove secure lock screens and tear down test app 361 runDeviceTestsAsUser("testTearDown", initialUser); 362 363 deviceClearLskf(); 364 } 365 } 366 deviceSetupServerBasedParameter()367 private void deviceSetupServerBasedParameter() throws Exception { 368 getDevice().executeShellCommand("device_config put ota server_based_ror_enabled true"); 369 String res = getDevice().executeShellCommand( 370 "device_config get ota server_based_ror_enabled"); 371 if (res == null || !res.contains("true")) { 372 fail("could not set up server based ror"); 373 } 374 375 getDevice().executeShellCommand( 376 "cmd lock_settings set-resume-on-reboot-provider-package " + PKG); 377 } 378 deviceCleanupServerBasedParameter()379 private void deviceCleanupServerBasedParameter() throws Exception { 380 getDevice().executeShellCommand("device_config put ota server_based_ror_enabled false"); 381 String res = getDevice().executeShellCommand( 382 "device_config get ota server_based_ror_enabled"); 383 if (res == null || !res.contains("false")) { 384 fail("could not clean up server based ror"); 385 } 386 387 getDevice().executeShellCommand( 388 "cmd lock_settings set-resume-on-reboot-provider-package "); 389 } 390 deviceSetup(int userId)391 private void deviceSetup(int userId) throws Exception { 392 // To receive boot broadcasts, kick our other app out of stopped state 393 getDevice().executeShellCommand("am start -a android.intent.action.MAIN" 394 + " --user " + userId 395 + " -c android.intent.category.LAUNCHER com.android.cts.splitapp/.MyActivity"); 396 397 // Give enough time for PackageManager to persist stopped state 398 RunUtil.getDefault().sleep(15000); 399 400 runDeviceTestsAsUser("testSetUp", userId); 401 402 // Give enough time for vold to update keys 403 RunUtil.getDefault().sleep(15000); 404 } 405 deviceRequestLskf()406 private void deviceRequestLskf() throws Exception { 407 deviceRequestLskf(PKG); 408 } 409 deviceRequestLskf(String clientName)410 private void deviceRequestLskf(String clientName) throws Exception { 411 String res = getDevice().executeShellCommand("cmd recovery request-lskf " + clientName); 412 if (res == null || !res.contains("success")) { 413 fail("could not set up recovery request-lskf"); 414 } 415 } 416 deviceClearLskf()417 private void deviceClearLskf() throws Exception { 418 deviceClearLskf(PKG); 419 } 420 deviceClearLskf(String clientName)421 private void deviceClearLskf(String clientName) throws Exception { 422 String res = getDevice().executeShellCommand("cmd recovery clear-lskf " + clientName); 423 if (res == null || !res.contains("success")) { 424 fail("could not clear-lskf"); 425 } 426 } 427 deviceLock(int userId)428 private void deviceLock(int userId) throws Exception { 429 int retriesLeft = 3; 430 boolean retry = false; 431 do { 432 if (retry) { 433 CLog.i("Retrying to summon lockscreen..."); 434 RunUtil.getDefault().sleep(500); 435 } 436 runDeviceTestsAsUser("testLockScreen", userId); 437 retry = !LockScreenInspector.newInstance(getDevice()).isDisplayedAndNotOccluded(); 438 } while (retriesLeft-- > 0 && retry); 439 440 if (retry) { 441 CLog.e("Could not summon lockscreen..."); 442 fail("Device could not be locked"); 443 } 444 } 445 deviceEnterLskf(int userId)446 private void deviceEnterLskf(int userId) throws Exception { 447 runDeviceTestsAsUser("testUnlockScreen", userId); 448 } 449 verifyLskfCaptured(String clientName)450 private void verifyLskfCaptured(String clientName) throws Exception { 451 HostSideTestUtils.waitUntil("Lskf isn't captured after " 452 + UNLOCK_BROADCAST_WAIT_SECONDS + " seconds for " + clientName, 453 UNLOCK_BROADCAST_WAIT_SECONDS, () -> isLskfCapturedForClient(clientName)); 454 } 455 isLskfCapturedForClient(String clientName)456 private boolean isLskfCapturedForClient(String clientName) throws Exception { 457 Pattern pattern = Pattern.compile(".*LSKF capture status: (\\w+)"); 458 String status = getDevice().executeShellCommand( 459 "cmd recovery is-lskf-captured " + clientName); 460 Matcher matcher = pattern.matcher(status); 461 if (!matcher.find()) { 462 CLog.i(TAG, "is-lskf-captured isn't implemented on build, assuming captured"); 463 return true; 464 } 465 466 return "true".equalsIgnoreCase(matcher.group(1)); 467 } 468 deviceRebootAndApply()469 private void deviceRebootAndApply() throws Exception { 470 deviceRebootAndApply(PKG); 471 } 472 deviceRebootAndApply(String clientName)473 private void deviceRebootAndApply(String clientName) throws Exception { 474 verifyLskfCaptured(clientName); 475 mBootCountTrackingRule.increaseExpectedBootCountDifference(1); 476 477 String res = executeShellCommandWithLogging( 478 "cmd recovery reboot-and-apply " + clientName + " cts-test"); 479 if (res != null && res.contains("Reboot and apply status: failure")) { 480 fail("could not call reboot-and-apply"); 481 } 482 483 getDevice().waitForDeviceNotAvailable(SHUTDOWN_TIME_MS); 484 getDevice().waitForDeviceOnline(120000); 485 486 waitForBootCompleted(getDevice()); 487 } 488 installTestPackages()489 private void installTestPackages() throws Exception { 490 new InstallMultiple().addFile(APK).run(); 491 new InstallMultiple().addFile(OTHER_APK).run(); 492 } 493 removeTestPackages()494 private void removeTestPackages() throws DeviceNotAvailableException { 495 getDevice().uninstallPackage(PKG); 496 getDevice().uninstallPackage(OTHER_PKG); 497 } 498 listUsers()499 private ArrayList<Integer> listUsers() throws DeviceNotAvailableException { 500 return getDevice().listUsers(); 501 } 502 503 /** 504 * Calls switch-user, but without trying to dismiss the keyguard. 505 */ switchUser(int userId)506 private void switchUser(int userId) throws Exception { 507 getDevice().switchUser(userId); 508 HostSideTestUtils.waitUntil("Could not switch users", USER_SWITCH_TIMEOUT_SECONDS, 509 () -> getDevice().getCurrentUser() == userId); 510 RunUtil.getDefault().sleep(USER_SWITCH_WAIT); 511 } 512 stopUserAsync(int userId)513 private void stopUserAsync(int userId) throws Exception { 514 executeShellCommandWithLogging("am stop-user -f " + userId); 515 } 516 removeUser(int userId)517 private void removeUser(int userId) throws Exception { 518 if (listUsers().contains(userId) && userId != USER_SYSTEM 519 && userId != getDevice().getMainUserId()) { 520 // Don't log output, as tests sometimes set no debug user restriction, which 521 // causes this to fail, we should still continue and remove the user. 522 String stopUserCommand = "am stop-user -w -f " + userId; 523 CLog.d("stopping and removing user " + userId); 524 getDevice().executeShellCommand(stopUserCommand); 525 // Ephemeral users may have already been removed after being stopped. 526 if (listUsers().contains(userId)) { 527 assertThat("Couldn't remove user", getDevice().removeUser(userId), is(true)); 528 } 529 } 530 } 531 createManagedProfile(int parentUserId)532 private int createManagedProfile(int parentUserId) throws DeviceNotAvailableException { 533 String commandOutput = getCreateManagedProfileCommandOutput(parentUserId); 534 return getUserIdFromCreateUserCommandOutput(commandOutput); 535 } 536 getUserIdFromCreateUserCommandOutput(String commandOutput)537 private int getUserIdFromCreateUserCommandOutput(String commandOutput) { 538 // Extract the id of the new user. 539 String[] tokens = commandOutput.split("\\s+"); 540 assertThat(commandOutput + " expected to have format \"Success: {USER_ID}\"", 541 tokens.length, greaterThan(0)); 542 assertThat("Command output should start with \"Success\"" + commandOutput, tokens[0], 543 is("Success:")); 544 return Integer.parseInt(tokens[tokens.length - 1]); 545 } 546 getCreateManagedProfileCommandOutput(int parentUserId)547 private String getCreateManagedProfileCommandOutput(int parentUserId) 548 throws DeviceNotAvailableException { 549 String command = "pm create-user --profileOf " + parentUserId + " --managed " 550 + "TestProfile_" + System.currentTimeMillis(); 551 CLog.d("Starting command " + command); 552 String commandOutput = getDevice().executeShellCommand(command); 553 CLog.d("Output for command " + command + ": " + commandOutput); 554 return commandOutput; 555 } 556 runDeviceTestsAsUser(String testMethodName, int userId)557 private void runDeviceTestsAsUser(String testMethodName, int userId) 558 throws DeviceNotAvailableException { 559 Utils.runDeviceTests(getDevice(), PKG, CLASS, testMethodName, userId); 560 } 561 isSupportedSDevice()562 private boolean isSupportedSDevice() throws Exception { 563 // The following tests targets API level >= S. 564 boolean isAtleastS = ApiLevelUtil.isAfter(getDevice(), 30 /* BUILD.VERSION_CODES.R */) 565 || ApiLevelUtil.codenameEquals(getDevice(), "S"); 566 567 return isAtleastS && getDevice().hasFeature(FEATURE_SECURE_LOCK_SCREEN); 568 } 569 supportFileBasedEncryption()570 private boolean supportFileBasedEncryption() throws Exception { 571 return "file".equals(getDevice().getProperty("ro.crypto.type")); 572 } 573 574 private class InstallMultiple extends BaseInstallMultiple<InstallMultiple> { InstallMultiple()575 public InstallMultiple() { 576 super(getDevice(), getBuild(), getAbi()); 577 } 578 } 579 setScreenStayOnValue(boolean value)580 private void setScreenStayOnValue(boolean value) throws DeviceNotAvailableException { 581 CommandResult result = getDevice().executeShellV2Command("svc power stayon " + value); 582 if (result.getStatus() != CommandStatus.SUCCESS) { 583 CLog.w("Could not set screen stay-on value. " + generateErrorStringFromCommandResult( 584 result)); 585 } 586 } 587 unplugBattery()588 private void unplugBattery() throws DeviceNotAvailableException { 589 CommandResult result = getDevice().executeShellV2Command("cmd battery unplug"); 590 if (result.getStatus() != CommandStatus.SUCCESS) { 591 CLog.w("Could not set device to battery-unplugged state" 592 + generateErrorStringFromCommandResult(result)); 593 } 594 } 595 resetBatteryOverride()596 private void resetBatteryOverride() throws DeviceNotAvailableException { 597 CommandResult result = getDevice().executeShellV2Command("cmd battery reset"); 598 if (result.getStatus() != CommandStatus.SUCCESS) { 599 fail("Could not reset device battery state override: " 600 + generateErrorStringFromCommandResult(result)); 601 } 602 } 603 normalizeUserStates()604 private void normalizeUserStates() throws Exception { 605 int[] userIds = Utils.getAllUsers(getDevice()); 606 switchUser(userIds[0]); 607 608 for (int userId : userIds) { 609 CommandResult lockScreenDisabledResult = 610 getDevice().executeShellV2Command( 611 "locksettings get-disabled --old " + DEFAULT_PIN + " --user " + userId); 612 if (lockScreenDisabledResult.getStatus() != CommandStatus.SUCCESS) { 613 CLog.w("Couldn't check whether there's already a PIN on the device. " 614 + generateErrorStringFromCommandResult(lockScreenDisabledResult)); 615 } 616 if ("false".equals(lockScreenDisabledResult.getStdout().trim())) { 617 CommandResult unsetPinResult = 618 getDevice().executeShellV2Command( 619 "locksettings clear --old " + DEFAULT_PIN + " --user " + userId); 620 if (unsetPinResult.getStatus() != CommandStatus.SUCCESS) { 621 CLog.w("Couldn't unset existing PIN on device. Test might not work properly. " 622 + generateErrorStringFromCommandResult(unsetPinResult)); 623 } 624 } 625 } 626 } 627 generateErrorStringFromCommandResult(CommandResult result)628 private static String generateErrorStringFromCommandResult(CommandResult result) { 629 return "Status code: " + result.getStatus() + ", Exit code: " + result.getExitCode() 630 + ", Error: " + result.getStderr(); 631 } 632 executeShellCommandWithLogging(String command)633 private String executeShellCommandWithLogging(String command) 634 throws DeviceNotAvailableException { 635 CLog.d("Starting command: " + command); 636 String result = getDevice().executeShellCommand(command); 637 CLog.d("Output for command \"" + command + "\": " + result); 638 return result; 639 } 640 prepareUsers(int users)641 private int[] prepareUsers(int users) throws DeviceNotAvailableException { 642 return Utils.prepareMultipleUsers(getDevice(), users); 643 } 644 } 645