1 /*
2  * Copyright (C) 2022 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.settings.biometrics2.utils;
18 
19 
20 import static java.lang.String.format;
21 
22 import android.app.KeyguardManager;
23 import android.content.Context;
24 import android.os.SystemClock;
25 import android.util.Log;
26 
27 import androidx.test.InstrumentationRegistry;
28 import androidx.test.uiautomator.UiDevice;
29 
30 import org.junit.Assert;
31 
32 import java.io.IOException;
33 import java.util.function.Supplier;
34 
35 public class LockScreenUtil {
36 
37     private static final String TAG = LockScreenUtil.class.getSimpleName();
38 
39     private static final int SLEEP_MS = 100;
40     private static final int WAIT_TIME_MS = 10000;
41 
42     private static final String SET_PATTERN_COMMAND = "locksettings set-pattern";
43     private static final String SET_PASSWORD_COMMAND = "locksettings set-password";
44     private static final String SET_PIN_COMMAND = "locksettings set-pin";
45 
46     private static final String RESET_LOCKSCREEN_SHELL_COMMAND = "locksettings clear --old";
47 
48     /**
49      * Different way to set the Lockscreen for Android device. Currently we only support PIN,
50      * PATTERN and PASSWORD
51      *
52      * @param lockscreenType it enum with list of supported lockscreen type
53      * @param lockscreenCode code[PIN or PATTERN or PASSWORD] which needs to be set.
54      * @param expectedResult expected result after setting the lockscreen because for lock type
55      *                       Swipe and None Keygaurd#isKeyguardSecure remain unlocked i.e. false
56      */
setLockscreen(LockscreenType lockscreenType, String lockscreenCode, boolean expectedResult)57     public static void setLockscreen(LockscreenType lockscreenType, String lockscreenCode,
58             boolean expectedResult) {
59         Log.d(TAG, format("Setting Lockscreen [%s(%s)]", lockscreenType, lockscreenCode));
60         switch (lockscreenType) {
61             case PIN:
62                 executeShellCommand(format("%s %s", SET_PIN_COMMAND, lockscreenCode));
63                 break;
64             case PASSWORD:
65                 executeShellCommand(format("%s %s", SET_PASSWORD_COMMAND, lockscreenCode));
66                 break;
67             case PATTERN:
68                 executeShellCommand(format("%s %s", SET_PATTERN_COMMAND, lockscreenCode));
69                 break;
70             default:
71                 throw new AssertionError("Non-supported Lockscreen Type: " + lockscreenType);
72         }
73         assertKeyguardSecure(expectedResult);
74     }
75 
76     /**
77      * Resets the give lockscreen.
78      *
79      * @param lockscreenCode old code which is currently set.
80      */
resetLockscreen(String lockscreenCode)81     public static void resetLockscreen(String lockscreenCode) {
82         Log.d(TAG, String.format("Re-Setting Lockscreen %s", lockscreenCode));
83         executeShellCommand(
84                 format("%s %s", RESET_LOCKSCREEN_SHELL_COMMAND, lockscreenCode));
85         assertKeyguardSecure(/* expectedSecure= */ false);
86     }
87 
88 
89     /**
90      * This method help you execute you shell command.
91      * Example: adb shell pm list packages -f
92      * Here you just need to provide executeShellCommand("pm list packages -f")
93      *
94      * @param command command need to executed.
95      */
executeShellCommand(String command)96     private static void executeShellCommand(String command) {
97         Log.d(TAG, format("Executing Shell Command: %s", command));
98         try {
99             getUiDevice().executeShellCommand(command);
100         } catch (IOException e) {
101             Log.d(TAG, format("IOException Occurred: %s", e));
102         }
103     }
104 
105     /**
106      * Enum for different types of Lockscreen, PIN, PATTERN and PASSWORD.
107      */
108     public enum LockscreenType {
109         PIN,
110         PASSWORD,
111         PATTERN
112     }
113 
getUiDevice()114     private static UiDevice getUiDevice() {
115         return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
116     }
117 
assertKeyguardSecure(boolean expectedSecure)118     private static void assertKeyguardSecure(boolean expectedSecure) {
119         waitForCondition(
120                 () -> String.format("Assert that keyguard %s secure, but failed.",
121                         expectedSecure ? "is" : "isn't"),
122                 () -> getKeyguardManager().isKeyguardSecure() == expectedSecure);
123     }
124 
125     /**
126      * Waits for a condition and fails if it doesn't become true within 10 sec.
127      *
128      * @param message   Supplier of the error message.
129      * @param condition Condition.
130      */
waitForCondition( Supplier<String> message, Condition condition)131     private static void waitForCondition(
132             Supplier<String> message, Condition condition) {
133         waitForCondition(message, condition, WAIT_TIME_MS);
134     }
135 
136     /**
137      * Waits for a condition and fails if it doesn't become true within specified time period.
138      *
139      * @param message   Supplier of the error message.
140      * @param condition Condition.
141      * @param timeoutMs Timeout.
142      */
waitForCondition( Supplier<String> message, Condition condition, long timeoutMs)143     private static void waitForCondition(
144             Supplier<String> message, Condition condition, long timeoutMs) {
145         final long startTime = SystemClock.uptimeMillis();
146         while (SystemClock.uptimeMillis() < startTime + timeoutMs) {
147             try {
148                 if (condition.isTrue()) {
149                     return;
150                 }
151             } catch (Throwable t) {
152                 throw new RuntimeException(t);
153             }
154             SystemClock.sleep(SLEEP_MS);
155         }
156 
157         // Check once more before failing.
158         try {
159             if (condition.isTrue()) {
160                 return;
161             }
162         } catch (Throwable t) {
163             throw new RuntimeException(t);
164         }
165 
166         Assert.fail(message.get());
167     }
168 
169     /**
170      * To get an instance of class that can be used to lock and unlock the keygaurd.
171      *
172      * @return an instance of class that can be used to lock and unlock the screen.
173      */
getKeyguardManager()174     private static KeyguardManager getKeyguardManager() {
175         return (KeyguardManager) InstrumentationRegistry.getContext().getSystemService(
176                 Context.KEYGUARD_SERVICE);
177     }
178 
179     /** Supplier of a boolean that can throw an exception. */
180     private interface Condition {
isTrue()181         boolean isTrue() throws Throwable;
182     }
183 }
184