xref: /aosp_15_r20/cts/tests/framework/base/windowmanager/util/src/android/server/wm/LockScreenSession.java (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1 /*
2  * Copyright (C) 2023 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.server.wm;
18 
19 import static android.content.pm.PackageManager.FEATURE_AUTOMOTIVE;
20 import static android.content.pm.PackageManager.FEATURE_EMBEDDED;
21 import static android.content.pm.PackageManager.FEATURE_LEANBACK;
22 import static android.content.pm.PackageManager.FEATURE_SECURE_LOCK_SCREEN;
23 import static android.content.pm.PackageManager.FEATURE_WATCH;
24 import static android.server.wm.ShellCommandHelper.executeShellCommandAndGetStdout;
25 import static android.server.wm.StateLogger.log;
26 import static android.server.wm.StateLogger.logE;
27 import static android.server.wm.UiDeviceUtils.pressBackButton;
28 import static android.server.wm.UiDeviceUtils.pressEnterButton;
29 import static android.server.wm.UiDeviceUtils.pressHomeButton;
30 import static android.server.wm.UiDeviceUtils.pressSleepButton;
31 import static android.server.wm.UiDeviceUtils.pressUnlockButton;
32 import static android.server.wm.UiDeviceUtils.pressWakeupButton;
33 import static android.server.wm.UiDeviceUtils.waitForDeviceIdle;
34 import static android.server.wm.WindowManagerState.STATE_RESUMED;
35 import static android.view.Display.DEFAULT_DISPLAY;
36 
37 import android.accessibilityservice.AccessibilityService;
38 import android.app.Instrumentation;
39 import android.app.KeyguardManager;
40 import android.content.ComponentName;
41 import android.content.Context;
42 import android.content.res.Resources;
43 import android.hardware.display.AmbientDisplayConfiguration;
44 import android.hardware.display.DisplayManager;
45 import android.view.Display;
46 
47 import androidx.annotation.NonNull;
48 
49 import com.android.compatibility.common.util.FeatureUtil;
50 import com.android.compatibility.common.util.SystemUtil;
51 
52 public class LockScreenSession implements AutoCloseable {
53     enum LockState {
54         LOCK_DISABLED,
55         LOCK_ENABLED
56     }
57 
58     private static final boolean DEBUG = true;
59     private static final String LOCK_CREDENTIAL = "1234";
60 
61     private final Instrumentation mInstrumentation;
62     private final Context mContext;
63 
64     private final LockState mInitialState;
65     private final AmbientDisplayConfiguration mAmbientDisplayConfiguration;
66 
67     private final WindowManagerStateHelper mWmState;
68     private final TouchHelper mTouchHelper;
69 
70     private final DisplayManager mDm;
71     private final KeyguardManager mKm;
72 
73     private boolean mLockCredentialSet;
74 
LockScreenSession(Instrumentation instrumentation, WindowManagerStateHelper wmState)75     public LockScreenSession(Instrumentation instrumentation, WindowManagerStateHelper wmState) {
76         mInstrumentation = instrumentation;
77         mWmState = wmState;
78         mContext = instrumentation.getContext();
79         mTouchHelper = new TouchHelper(instrumentation, wmState);
80         mDm = mContext.getSystemService(DisplayManager.class);
81         mKm = mContext.getSystemService(KeyguardManager.class);
82 
83         // Store the initial state so that it can be restored when the session
84         // goes out of scope.
85         mInitialState = isLockDisabled() ? LockState.LOCK_DISABLED : LockState.LOCK_ENABLED;
86 
87         // Enable lock screen (swipe) by default.
88         setLockDisabled(false);
89         mAmbientDisplayConfiguration = new AmbientDisplayConfiguration(mContext);
90 
91         // On devices that don't support any insecure locks but supports a secure lock, let's
92         // enable a secure lock.
93         if (!supportsInsecureLock() && supportsSecureLock()) {
94             setLockCredential();
95         }
96     }
97 
getSupportsInsecureLockScreen()98     private boolean getSupportsInsecureLockScreen() {
99         boolean insecure;
100         try {
101             insecure = mContext.getResources().getBoolean(
102                     Resources.getSystem().getIdentifier(
103                             "config_supportsInsecureLockScreen", "bool", "android"));
104         } catch (Resources.NotFoundException e) {
105             insecure = true;
106         }
107         return insecure;
108     }
109 
110     /** Whether or not the device supports pin/pattern/password lock. */
supportsSecureLock()111     private boolean supportsSecureLock() {
112         return FeatureUtil.hasSystemFeature(FEATURE_SECURE_LOCK_SCREEN);
113     }
114 
115     /** Whether or not the device supports "swipe" lock. */
supportsInsecureLock()116     private boolean supportsInsecureLock() {
117         return !FeatureUtil.hasAnySystemFeature(
118                 FEATURE_LEANBACK, FEATURE_WATCH, FEATURE_EMBEDDED, FEATURE_AUTOMOTIVE)
119                 && getSupportsInsecureLockScreen();
120     }
121 
runCommandAndPrintOutput(String command)122     protected static String runCommandAndPrintOutput(String command) {
123         final String output = executeShellCommandAndGetStdout(command);
124         log(output);
125         return output;
126     }
127 
128     /**
129      * Sets a credential to use with a secure lock method.
130      */
setLockCredential()131     public LockScreenSession setLockCredential() {
132         if (mLockCredentialSet) {
133             // "set-pin" command isn't idempotent. We need to provide the old credential in
134             // order to change it to a new one. However we never use a different credential in
135             // CTS so we don't need to do anything if the credential is already set.
136             return this;
137         }
138         mLockCredentialSet = true;
139         runCommandAndPrintOutput(
140                 "locksettings set-pin " + LOCK_CREDENTIAL);
141         return this;
142     }
143 
144     /**
145      * Unlocks a device by entering a lock credential.
146      */
enterAndConfirmLockCredential()147     public LockScreenSession enterAndConfirmLockCredential() {
148         // Ensure focus will switch to default display. Meanwhile we cannot tap on center area,
149         // which may tap on input credential area.
150         mTouchHelper.touchAndCancelOnDisplayCenterSync(DEFAULT_DISPLAY);
151         mWmState.waitForNonActivityWindowFocused();
152 
153         waitForDeviceIdle(3000);
154         SystemUtil.runWithShellPermissionIdentity(
155                 () -> mInstrumentation.sendStringSync(LOCK_CREDENTIAL));
156         pressEnterButton();
157         return this;
158     }
159 
removeLockCredential()160     private static void removeLockCredential() {
161         runCommandAndPrintOutput("locksettings clear --old " + LOCK_CREDENTIAL);
162     }
163 
164     /**
165      * Disables the lock screen. Clears the secure credential first if one is set.
166      */
disableLockScreen()167     public LockScreenSession disableLockScreen() {
168         // Lock credentials need to be cleared before disabling the lock.
169         if (mLockCredentialSet) {
170             removeLockCredential();
171             mLockCredentialSet = false;
172         }
173         setLockDisabled(true);
174         return this;
175     }
176 
isDisplayOn()177     private boolean isDisplayOn() {
178         final Display display = mDm.getDisplay(DEFAULT_DISPLAY);
179         return display != null && display.getState() == Display.STATE_ON;
180     }
181 
182     /**
183      * Puts the device to sleep with intention of locking if a lock is enabled.
184      */
sleepDevice()185     public LockScreenSession sleepDevice() {
186         pressSleepButton();
187         // Not all device variants lock when we go to sleep, so we need to explicitly lock the
188         // device. Note that pressSleepButton() above is redundant because the action also
189         // puts the device to sleep, but kept around for clarity.
190         if (FeatureUtil.isWatch()) {
191             mInstrumentation.getUiAutomation().performGlobalAction(
192                     AccessibilityService.GLOBAL_ACTION_LOCK_SCREEN);
193         }
194         if (mAmbientDisplayConfiguration.alwaysOnEnabled(
195                 android.os.Process.myUserHandle().getIdentifier())) {
196             mWmState.waitForAodShowing();
197         } else {
198             Condition.waitFor("display to turn off", () -> !isDisplayOn());
199         }
200         if (!isLockDisabled()) {
201             mWmState.waitFor(
202                     state -> state.getKeyguardControllerState().keyguardShowing,
203                     "Keyguard showing");
204         }
205         return this;
206     }
207 
208     /**
209      * Wakes the device up.
210      */
wakeUpDevice()211     public LockScreenSession wakeUpDevice() {
212         pressWakeupButton();
213         return this;
214     }
215 
216     /**
217      * Unlocks the device by using the unlock button.
218      */
unlockDevice()219     public LockScreenSession unlockDevice() {
220         // Make sure the unlock button event is send to the default display.
221         mTouchHelper.touchAndCancelOnDisplayCenterSync(DEFAULT_DISPLAY);
222 
223         pressUnlockButton();
224         return this;
225     }
226 
227     /**
228      * Locks the device and wakes it up so that the keyguard is shown.
229      * @param showWhenLockedActivities Activities to check for after showing the keyguard.
230      */
gotoKeyguard(ComponentName... showWhenLockedActivities)231     public LockScreenSession gotoKeyguard(ComponentName... showWhenLockedActivities) {
232         if (DEBUG && isLockDisabled()) {
233             logE("LockScreenSession.gotoKeyguard() is called without lock enabled.");
234         }
235         sleepDevice();
236         wakeUpDevice();
237         if (showWhenLockedActivities.length == 0) {
238             mWmState.waitForKeyguardShowingAndNotOccluded();
239         } else {
240             mWmState.waitForValidState(showWhenLockedActivities);
241         }
242         return this;
243     }
244 
isKeyguardLocked()245     private boolean isKeyguardLocked() {
246         return mKm != null && mKm.isKeyguardLocked();
247     }
248 
249     @Override
close()250     public void close() {
251         // If keyguard is occluded, credential cannot be removed as expected.
252         // LockScreenSession#close is always called before stopping all test activities,
253         // which could cause the keyguard to stay occluded after wakeup.
254         // If Keyguard is occluded, pressing the back key can hide the ShowWhenLocked activity.
255         wakeUpDevice();
256         mWmState.computeState();
257         if (WindowManagerStateHelper.isKeyguardOccluded(mWmState)) {
258             pressBackButton();
259         }
260 
261         final boolean wasCredentialSet = mLockCredentialSet;
262         boolean wasDeviceLocked = false;
263         if (mLockCredentialSet) {
264             wasDeviceLocked = mKm != null && mKm.isDeviceLocked();
265             removeLockCredential();
266             mLockCredentialSet = false;
267         }
268 
269         // Restore the initial state.
270         switch (mInitialState) {
271             case LOCK_DISABLED -> setLockDisabled(true);
272             case LOCK_ENABLED -> setLockDisabled(false);
273         }
274 
275         if (FeatureUtil.isWatch()) {
276             // Keyguard will be dismissed when the credential is removed.
277             mWmState.waitForKeyguardGone();
278         }
279 
280         if (!isKeyguardLocked()) {
281             // we can return early if keyguard is not locked
282             log("Returning early since keyguard is not locked");
283             return;
284         }
285 
286         // Dismiss active keyguard after credential is cleared, so keyguard doesn't ask for
287         // the stale credential.
288 
289         // If the credential wasn't set, the steps for restoring can be simpler.
290         if (!wasCredentialSet) {
291             mWmState.computeState();
292             if (WindowManagerStateHelper.isKeyguardShowingAndNotOccluded(mWmState)) {
293                 // Keyguard is showing and not occluded so only need to unlock.
294                 unlockDevice();
295                 return;
296             }
297 
298             final ComponentName home = mWmState.getHomeActivityName();
299             if (home != null && mWmState.hasActivityState(home, STATE_RESUMED)) {
300                 // Home is resumed so nothing to do (e.g. after finishing show-when-locked app).
301                 return;
302             }
303         }
304 
305         // If device is unlocked, there might have ShowWhenLocked activity runs on,
306         // use home key to clear all activity at foreground.
307         pressHomeButton();
308         if (wasDeviceLocked) {
309             // The removal of credential needs an extra cycle to take effect.
310             sleepDevice();
311             wakeUpDevice();
312         }
313         if (isKeyguardLocked()) {
314             unlockDevice();
315         }
316     }
317 
318     /**
319      * Returns whether the lock screen is disabled.
320      *
321      * @return true if the lock screen is disabled, false otherwise.
322      */
isLockDisabled()323     private boolean isLockDisabled() {
324         final String isLockDisabled = runCommandAndPrintOutput(
325                 "locksettings get-disabled " + oldIfNeeded()).trim();
326         return !"null".equals(isLockDisabled) && Boolean.parseBoolean(isLockDisabled);
327     }
328 
329     /**
330      * Disable the lock screen.
331      *
332      * @param lockDisabled true if should disable, false otherwise.
333      */
setLockDisabled(boolean lockDisabled)334     private void setLockDisabled(boolean lockDisabled) {
335         runCommandAndPrintOutput("locksettings set-disabled " + lockDisabled);
336     }
337 
338     @NonNull
oldIfNeeded()339     private String oldIfNeeded() {
340         if (mLockCredentialSet) {
341             return " --old " + ActivityManagerTestBase.LOCK_CREDENTIAL + " ";
342         }
343         return "";
344     }
345 }
346