• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.system.helpers;
18 
19 import static junit.framework.Assert.assertTrue;
20 
21 import android.app.KeyguardManager;
22 import android.content.Context;
23 import android.graphics.Point;
24 import android.provider.Settings;
25 import android.support.test.uiautomator.By;
26 import android.support.test.uiautomator.BySelector;
27 import android.support.test.uiautomator.UiDevice;
28 import android.support.test.uiautomator.UiObject2;
29 import android.support.test.uiautomator.Until;
30 
31 import androidx.test.InstrumentationRegistry;
32 
33 import junit.framework.Assert;
34 
35 import java.io.IOException;
36 import java.util.regex.Pattern;
37 
38 /**
39  * Implement common helper methods for Lockscreen.
40  */
41 public class LockscreenHelper {
42     private static final String LOG_TAG = LockscreenHelper.class.getSimpleName();
43     public static final int SHORT_TIMEOUT = 200;
44     public static final int LONG_TIMEOUT = 2000;
45     public static final String EDIT_TEXT_CLASS_NAME = "android.widget.EditText";
46     public static final String CAMERA2_PACKAGE = "com.android.camera2";
47     public static final String CAMERA_PACKAGE = "com.google.android.GoogleCamera";
48     public static final String MODE_PIN = "PIN";
49     public static final String MODE_PASSWORD = "Password";
50     public static final String MODE_PATTERN = "Pattern";
51     private static final int SWIPE_MARGIN = 5;
52     private static final int SWIPE_MARGIN_BOTTOM = 100;
53     private static final int DEFAULT_FLING_STEPS = 5;
54     private static final int DEFAULT_SCROLL_STEPS = 15;
55     private static final long MAX_SCREEN_LOCK_WAIT_TIME_MS = 5_000;
56     private static final BySelector SCREEN_LOCK =
57             By.res("com.android.systemui", "keyguard_bottom_area");
58     private static final String PIN_ENTRY = "com.android.systemui:id/pinEntry";
59     private static final String SET_PIN_COMMAND = "locksettings set-pin %s";
60     private static final String SET_PASSWORD_COMMAND = "locksettings set-password %s";
61     private static final String SET_PATTERN_COMMAND = "locksettings set-pattern %s";
62     private static final String CLEAR_COMMAND = "locksettings clear --old %s";
63     private static final String HOTSEAT = "hotseat";
64     private static final BySelector DONE_BUTTON =
65             By.res("com.android.settings", "redaction_done_button");
66 
67     private static LockscreenHelper sInstance = null;
68     private Context mContext = null;
69     private UiDevice mDevice = null;
70     private final ActivityHelper mActivityHelper;
71     private final CommandsHelper mCommandsHelper;
72     private final DeviceHelper mDeviceHelper;
73     private boolean mIsRyuDevice = false;
74 
LockscreenHelper()75     public LockscreenHelper() {
76         mContext = InstrumentationRegistry.getTargetContext();
77         mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
78         mActivityHelper = ActivityHelper.getInstance();
79         mCommandsHelper = CommandsHelper.getInstance(InstrumentationRegistry.getInstrumentation());
80         mDeviceHelper = DeviceHelper.getInstance();
81         mIsRyuDevice = mDeviceHelper.isRyuDevice();
82     }
83 
getInstance()84     public static LockscreenHelper getInstance() {
85         if (sInstance == null) {
86             sInstance = new LockscreenHelper();
87         }
88         return sInstance;
89     }
90 
getLauncherPackage()91     public String getLauncherPackage() {
92         return mDevice.getLauncherPackageName();
93     }
94 
95     /**
96      * Launch Camera on LockScreen
97      * @return true/false
98      */
launchCameraOnLockScreen()99     public boolean launchCameraOnLockScreen() {
100         // Hit the back button to dismiss any keyguard
101         mDevice.pressBack();
102         String CameraPackage = mIsRyuDevice ? CAMERA2_PACKAGE : CAMERA_PACKAGE;
103         int w = mDevice.getDisplayWidth();
104         int h = mDevice.getDisplayHeight();
105         // Load camera on LockScreen and take a photo
106         mDevice.drag((w - 25), (h - 25), (int) (w * 0.5), (int) (w * 0.5), 40);
107         mDevice.waitForIdle();
108         return mDevice.wait(Until.hasObject(
109                 By.res(CameraPackage, "activity_root_view")),
110                 LONG_TIMEOUT * 2);
111     }
112 
113      /**
114      * Sets the screen lock pin or password
115      * @param pwd text of Password or Pin for lockscreen
116      * @param mode indicate if its password or PIN
117      * @throws InterruptedException
118      */
setScreenLock(String pwd, String mode, boolean mIsNexusDevice)119     public void setScreenLock(String pwd, String mode, boolean mIsNexusDevice)
120             throws InterruptedException {
121         if (mode.equalsIgnoreCase("None")) {
122             mDevice.wait(Until.findObject(By.text("None")), LONG_TIMEOUT * 2).click();
123             return;
124         }
125         enterScreenLockOnce(pwd, mode, mIsNexusDevice);
126         Thread.sleep(LONG_TIMEOUT);
127         // Re-enter password on confirmation screen
128         UiObject2 pinField = mDevice.wait(Until.findObject(By.clazz(EDIT_TEXT_CLASS_NAME)),
129                 LONG_TIMEOUT);
130         pinField.setText(pwd);
131         Thread.sleep(LONG_TIMEOUT);
132         mDevice.pressEnter();
133         // Click DONE on lock screen notification setting screen
134         mDevice.wait(Until.findObject(DONE_BUTTON), LONG_TIMEOUT).click();
135     }
136 
137     /**
138      * Enters the screen lock once on the setting screen
139      * @param pwd text of Password or Pin for lockscreen
140      * @param mode indicate if its password or PIN
141      * @throws InterruptedException
142      */
enterScreenLockOnce(String pwd, String mode, boolean mIsNexusDevice)143     public void enterScreenLockOnce(String pwd, String mode, boolean mIsNexusDevice) {
144         mDevice.wait(Until.findObject(By.text(mode)), LONG_TIMEOUT * 2).click();
145         // set up Secure start-up page
146         if (!mIsNexusDevice) {
147             mDevice.wait(Until.findObject(By.text("No thanks")), LONG_TIMEOUT).click();
148         }
149         UiObject2 pinField = mDevice.wait(Until.findObject(By.clazz(EDIT_TEXT_CLASS_NAME)),
150                 LONG_TIMEOUT);
151         pinField.setText(pwd);
152         // enter
153         mDevice.pressEnter();
154     }
155 
156     /*
157      * Enters non matching passcodes on both setting screens.
158      * Note: this will fail if you enter matching passcodes.
159      */
enterNonMatchingPasscodes(String firstPasscode, String secondPasscode, String mode, boolean mIsNexusDevice)160     public void enterNonMatchingPasscodes(String firstPasscode, String secondPasscode,
161             String mode, boolean mIsNexusDevice) throws Exception {
162         enterScreenLockOnce(firstPasscode, mode, mIsNexusDevice);
163         Thread.sleep(LONG_TIMEOUT);
164         UiObject2 pinField = mDevice.wait(Until.findObject(By.clazz(EDIT_TEXT_CLASS_NAME)),
165                 LONG_TIMEOUT);
166         pinField.setText(secondPasscode);
167         mDevice.pressEnter();
168         Thread.sleep(LONG_TIMEOUT);
169         // Verify that error is thrown.
170         UiObject2 dontMatchMessage = mDevice.wait(Until.findObject
171                 (By.textContains("don’t match")), LONG_TIMEOUT);
172         Assert.assertNotNull("Error message for passcode confirmation not visible",
173                 dontMatchMessage);
174     }
175 
176     /**
177      * check if Emergency Call page exists
178      * @throws InterruptedException
179      */
checkEmergencyCallOnLockScreen()180     public void checkEmergencyCallOnLockScreen() throws InterruptedException {
181         mDevice.pressMenu();
182         mDevice.wait(Until.findObject(By.text("EMERGENCY")), LONG_TIMEOUT).click();
183         Thread.sleep(LONG_TIMEOUT);
184         UiObject2 dialButton = mDevice.wait(Until.findObject(By.desc("dial")),
185                 LONG_TIMEOUT);
186         Assert.assertNotNull("Can't reach emergency call page", dialButton);
187         mDevice.pressBack();
188         Thread.sleep(LONG_TIMEOUT);
189     }
190 
191     /**
192      * remove Screen Lock, reset to Swipe.
193      * @throws InterruptedException
194      */
removeScreenLock(String pwd)195     public void removeScreenLock(String pwd)
196             throws InterruptedException {
197         navigateToScreenLock();
198         UiObject2 pinField = mDevice.wait(Until.findObject(By.clazz(EDIT_TEXT_CLASS_NAME)),
199                 LONG_TIMEOUT);
200         pinField.setText(pwd);
201         mDevice.pressEnter();
202         mDevice.wait(Until.findObject(By.text("Swipe")), LONG_TIMEOUT).click();
203         mDevice.waitForIdle();
204         mDevice.wait(Until.findObject(By.text(
205                 Pattern.compile("YES, REMOVE", Pattern.CASE_INSENSITIVE))), LONG_TIMEOUT).click();
206     }
207 
208     /**
209      * Enter a screen password or PIN.
210      * Pattern not supported, please use
211      * unlockDeviceWithPattern(String) below.
212      * Method assumes the device is on lockscreen.
213      * with keyguard exposed. It will wake
214      * up the device, swipe up to reveal the keyguard,
215      * and enter the password or pin and hit enter.
216      * @throws InterruptedException, IOException
217      */
unlockScreen(String pwd)218     public void unlockScreen(String pwd)
219             throws InterruptedException, IOException {
220         // Press menu key (82 is the code for the menu key)
221         String command = String.format(" %s %s %s", "input", "keyevent", "82");
222         mDevice.executeShellCommand(command);
223         Thread.sleep(SHORT_TIMEOUT);
224         Thread.sleep(SHORT_TIMEOUT);
225         // enter password to unlock screen
226         command = String.format(" %s %s %s", "input", "text", pwd);
227         mDevice.executeShellCommand(command);
228         mDevice.waitForIdle();
229         Thread.sleep(SHORT_TIMEOUT);
230         mDevice.pressEnter();
231     }
232 
233     /**
234      * navigate to screen lock setting page
235      * @throws InterruptedException
236      */
navigateToScreenLock()237     public void navigateToScreenLock()
238             throws InterruptedException {
239         mActivityHelper.launchIntent(Settings.ACTION_SECURITY_SETTINGS);
240         mDevice.wait(Until.findObject(By.text("Screen lock")), LONG_TIMEOUT).click();
241     }
242 
243     /**
244      * check if Lock Screen is enabled
245      */
isLockScreenEnabled()246     public boolean isLockScreenEnabled() {
247         KeyguardManager km = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
248         return km.isKeyguardSecure();
249     }
250 
251     /**
252      * Sets a screen lock via shell.
253      */
setScreenLockViaShell(String passcode, String mode)254     public void setScreenLockViaShell(String passcode, String mode) throws Exception {
255         switch (mode) {
256             case MODE_PIN:
257                 mCommandsHelper.executeShellCommand(String.format(SET_PIN_COMMAND, passcode));
258                 break;
259             case MODE_PASSWORD:
260                 mCommandsHelper.executeShellCommand(String.format(SET_PASSWORD_COMMAND, passcode));
261                 break;
262             case MODE_PATTERN:
263                 mCommandsHelper.executeShellCommand(String.format(SET_PATTERN_COMMAND, passcode));
264                 break;
265             default:
266                 throw new IllegalArgumentException("Unsupported mode: " + mode);
267         }
268     }
269 
270     /**
271      * Removes the screen lock via shell.
272      */
removeScreenLockViaShell(String pwd)273     public void removeScreenLockViaShell(String pwd) throws Exception {
274         mCommandsHelper.executeShellCommand(String.format(CLEAR_COMMAND, pwd));
275     }
276 
277     /**
278      * swipe up to unlock the screen
279      */
unlockScreenSwipeUp()280     public void unlockScreenSwipeUp() throws Exception {
281         mDevice.wakeUp();
282         mDevice.waitForIdle();
283         mDevice.swipe(mDevice.getDisplayWidth() / 2,
284                 mDevice.getDisplayHeight() - SWIPE_MARGIN,
285                 mDevice.getDisplayWidth() / 2,
286                 SWIPE_MARGIN,
287                 DEFAULT_SCROLL_STEPS);
288         mDevice.waitForIdle();
289     }
290 
291     /*
292      * Takes in the correct code (pin or password), the attempted
293      * code (pin or password), the mode for the code (whether pin or password)
294      * and whether or not they are expected to match.
295      * Asserts that the device has been successfully unlocked (or not).
296      */
setAndEnterLockscreenCode(String actualCode, String attemptedCode, String mode, boolean shouldMatch)297     public void setAndEnterLockscreenCode(String actualCode, String attemptedCode,
298             String mode, boolean shouldMatch) throws Exception {
299         setScreenLockViaShell(actualCode, mode);
300         Thread.sleep(LONG_TIMEOUT);
301         enterLockscreenCode(actualCode, attemptedCode, mode, shouldMatch);
302     }
303 
enterLockscreenCode(String actualCode, String attemptedCode, String mode, boolean shouldMatch)304     public void enterLockscreenCode(String actualCode, String attemptedCode,
305             String mode, boolean shouldMatch) throws Exception {
306         mDevice.pressHome();
307         mDeviceHelper.sleepAndWakeUpDevice();
308         unlockScreen(attemptedCode);
309         checkForHotseatOnHome(shouldMatch);
310         removeScreenLockViaShell(actualCode);
311         Thread.sleep(LONG_TIMEOUT);
312         mDevice.pressHome();
313     }
314 
315     /*
316      * Takes in the correct pattern, the attempted pattern,
317      * and whether or not they are expected to match.
318      * Asserts that the device has been successfully unlocked (or not).
319      */
setAndEnterLockscreenPattern(String actualPattern, String attemptedPattern, boolean shouldMatch)320     public void setAndEnterLockscreenPattern(String actualPattern,
321         String attemptedPattern, boolean shouldMatch) throws Exception {
322         setScreenLockViaShell
323                 (actualPattern, LockscreenHelper.MODE_PATTERN);
324         unlockDeviceWithPattern(attemptedPattern);
325         checkForHotseatOnHome(shouldMatch);
326         removeScreenLockViaShell(actualPattern);
327         Thread.sleep(LONG_TIMEOUT);
328         mDevice.pressHome();
329     }
330 
checkForHotseatOnHome(boolean deviceUnlocked)331     public void checkForHotseatOnHome(boolean deviceUnlocked)  throws Exception {
332         mDevice.pressHome();
333         Thread.sleep(LONG_TIMEOUT);
334         UiObject2 hotseat = mDevice.findObject(By.res(getLauncherPackage(), HOTSEAT));
335         if (deviceUnlocked) {
336         Assert.assertNotNull("Device not unlocked correctly", hotseat);
337         }
338         else {
339             Assert.assertNull("Device should not be unlocked", hotseat);
340         }
341     }
342 
343     /*
344      * The pattern below is always invalid as you need at least
345      * four dots for a valid lock. That action of changing
346      * directions while dragging is unsupported by
347      * uiautomator.
348      */
enterInvalidPattern()349     public void enterInvalidPattern() throws Exception {
350         // Get coordinates for left top dot
351         UiObject2 lockPattern = mDevice.wait(Until.findObject
352                 (By.res("com.android.systemui:id/lockPatternView")),
353                 LONG_TIMEOUT);
354         // Get coordinates for left side dots
355         int xCoordinate =(int) (lockPattern.getVisibleBounds().left +
356                  lockPattern.getVisibleBounds().left*0.16);
357         int y1Coordinate = (int) (lockPattern.getVisibleBounds().top +
358                 lockPattern.getVisibleBounds().top*0.16);
359         int y2Coordinate = (int) (lockPattern.getVisibleBounds().bottom -
360                 lockPattern.getVisibleBounds().bottom*0.16);
361         // Drag coordinates from one point to another
362         mDevice.swipe(xCoordinate, y1Coordinate, xCoordinate, y2Coordinate, 2);
363     }
364 
365     /* Valid pattern unlock attempt
366      * Takes in a contiguous string as input
367      * 1 2 3
368      * 4 5 6
369      * 7 8 9
370      * with each number representing a dot. Eg: "1236"
371      */
unlockDeviceWithPattern(String unlockPattern)372     public void unlockDeviceWithPattern(String unlockPattern) throws Exception {
373         mDeviceHelper.sleepAndWakeUpDevice();
374         unlockScreenSwipeUp();
375         Point[] coordinateArray = new Point[unlockPattern.length()];
376         for (int i=0; i < unlockPattern.length(); i++) {
377             coordinateArray[i] = calculateCoordinatesForPatternDot(unlockPattern.charAt(i),
378                                  "com.android.systemui:id/lockPatternView");
379         }
380         // Note: 50 controls the speed of the pattern drawing.
381         mDevice.swipe(coordinateArray, 50);
382         Thread.sleep(SHORT_TIMEOUT);
383     }
384 
385     /* Pattern lock setting attempt
386      * Takes in a contiguous string as input
387      * 1 2 3
388      * 4 5 6
389      * 7 8 9
390      * with each number representing a dot. Eg: "1236"
391      */
enterPatternLockOnceForSettingLock(String unlockPattern)392     public void enterPatternLockOnceForSettingLock(String unlockPattern)
393             throws InterruptedException {
394         Point[] coordinateArray = new Point[unlockPattern.length()];
395         for (int i=0; i < unlockPattern.length(); i++) {
396             coordinateArray[i] = calculateCoordinatesForPatternDot(unlockPattern.charAt(i),
397                                  "com.android.settings:id/lockPattern");
398         }
399         // Note: 50 controls the speed of the pattern drawing.
400         mDevice.swipe(coordinateArray, 50);
401         Thread.sleep(SHORT_TIMEOUT);
402     }
403 
404     /* Pattern lock setting - this enters and reconfirms pattern to set
405      * using the UI.
406      * Takes in a contiguous string as input
407      * 1 2 3
408      * 4 5 6
409      * 7 8 9
410      * with each number representing a dot. Eg: "1236"
411      */
setPatternLockSettingLock(String unlockPattern)412     public void setPatternLockSettingLock(String unlockPattern)  throws Exception {
413         // Enter the same pattern twice, once on the initial set
414         // screen and once on the confirmation screen.
415         for (int i=0; i<2; i++) {
416             enterPatternLockOnceForSettingLock(unlockPattern);
417             mDevice.pressEnter();
418         }
419         mDevice.wait(Until.findObject(By.text("DONE")), LONG_TIMEOUT).click();
420     }
421 
waitLockscreenVisible()422     public void waitLockscreenVisible() {
423         assertTrue(mDevice.wait(Until.hasObject(SCREEN_LOCK), MAX_SCREEN_LOCK_WAIT_TIME_MS));
424     }
425 
426     /* Returns screen coordinates for each pattern dot
427      * for the current device
428      * Represented as follows by chars
429      * 1 2 3
430      * 4 5 6
431      * 7 8 9
432      * this is consistent with the set-pattern command
433      * to avoid confusion.
434      */
calculateCoordinatesForPatternDot(char dotNumber, String lockPatternResId)435     private Point calculateCoordinatesForPatternDot(char dotNumber, String lockPatternResId) {
436         UiObject2 lockPattern = mDevice.wait(Until.findObject
437                 (By.res(lockPatternResId)), LONG_TIMEOUT);
438         // Calculate x coordinate
439         int xCoordinate = 0;
440         int deltaX = (int) ((lockPattern.getVisibleBounds().right -
441                 lockPattern.getVisibleBounds().left)*0.16);
442         if (dotNumber == '1' || dotNumber == '4' || dotNumber == '7') {
443             xCoordinate = lockPattern.getVisibleBounds().left + deltaX;
444         }
445         else if (dotNumber == '2' || dotNumber == '5' || dotNumber == '8') {
446             xCoordinate = lockPattern.getVisibleCenter().x;
447         }
448         else if (dotNumber == '3' || dotNumber == '6' || dotNumber == '9') {
449             xCoordinate = lockPattern.getVisibleBounds().right - deltaX;
450         }
451         // Calculate y coordinate
452         int yCoordinate = 0;
453         int deltaY = (int) ((lockPattern.getVisibleBounds().bottom -
454                 lockPattern.getVisibleBounds().top)*0.16);
455         if (dotNumber == '1' || dotNumber == '2' || dotNumber == '3') {
456             yCoordinate = lockPattern.getVisibleBounds().top + deltaY;
457         }
458         else if (dotNumber == '4' || dotNumber == '5' || dotNumber == '6') {
459             yCoordinate = lockPattern.getVisibleCenter().y;
460         }
461         else if (dotNumber == '7' || dotNumber == '8' || dotNumber == '9') {
462             yCoordinate = lockPattern.getVisibleBounds().bottom - deltaY;
463         }
464         return new Point(xCoordinate, yCoordinate);
465      }
466 }
467