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