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