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