1 /* 2 * Copyright (C) 2014 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 package com.android.keyguard; 17 18 import android.app.Activity; 19 import android.app.AlertDialog; 20 import android.app.admin.DevicePolicyManager; 21 import android.content.Context; 22 import android.content.res.ColorStateList; 23 import android.graphics.Rect; 24 import android.metrics.LogMaker; 25 import android.os.UserHandle; 26 import android.util.AttributeSet; 27 import android.util.Log; 28 import android.util.Slog; 29 import android.util.StatsLog; 30 import android.util.TypedValue; 31 import android.view.LayoutInflater; 32 import android.view.MotionEvent; 33 import android.view.VelocityTracker; 34 import android.view.View; 35 import android.view.ViewConfiguration; 36 import android.view.WindowManager; 37 import android.widget.FrameLayout; 38 39 import androidx.annotation.VisibleForTesting; 40 import androidx.dynamicanimation.animation.DynamicAnimation; 41 import androidx.dynamicanimation.animation.SpringAnimation; 42 43 import com.android.internal.logging.MetricsLogger; 44 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 45 import com.android.internal.widget.LockPatternUtils; 46 import com.android.keyguard.KeyguardSecurityModel.SecurityMode; 47 import com.android.settingslib.utils.ThreadUtils; 48 import com.android.systemui.Dependency; 49 import com.android.systemui.R; 50 import com.android.systemui.SystemUIFactory; 51 import com.android.systemui.statusbar.phone.UnlockMethodCache; 52 import com.android.systemui.util.InjectionInflationController; 53 54 public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView { 55 private static final boolean DEBUG = KeyguardConstants.DEBUG; 56 private static final String TAG = "KeyguardSecurityView"; 57 58 private static final int USER_TYPE_PRIMARY = 1; 59 private static final int USER_TYPE_WORK_PROFILE = 2; 60 private static final int USER_TYPE_SECONDARY_USER = 3; 61 62 // Bouncer is dismissed due to no security. 63 private static final int BOUNCER_DISMISS_NONE_SECURITY = 0; 64 // Bouncer is dismissed due to pin, password or pattern entered. 65 private static final int BOUNCER_DISMISS_PASSWORD = 1; 66 // Bouncer is dismissed due to biometric (face, fingerprint or iris) authenticated. 67 private static final int BOUNCER_DISMISS_BIOMETRIC = 2; 68 // Bouncer is dismissed due to extended access granted. 69 private static final int BOUNCER_DISMISS_EXTENDED_ACCESS = 3; 70 // Bouncer is dismissed due to sim card unlock code entered. 71 private static final int BOUNCER_DISMISS_SIM = 4; 72 73 // Make the view move slower than the finger, as if the spring were applying force. 74 private static final float TOUCH_Y_MULTIPLIER = 0.25f; 75 // How much you need to drag the bouncer to trigger an auth retry (in dps.) 76 private static final float MIN_DRAG_SIZE = 10; 77 // How much to scale the default slop by, to avoid accidental drags. 78 private static final float SLOP_SCALE = 2f; 79 80 private KeyguardSecurityModel mSecurityModel; 81 private LockPatternUtils mLockPatternUtils; 82 83 private KeyguardSecurityViewFlipper mSecurityViewFlipper; 84 private boolean mIsVerifyUnlockOnly; 85 private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid; 86 private KeyguardSecurityView mCurrentSecurityView; 87 private SecurityCallback mSecurityCallback; 88 private AlertDialog mAlertDialog; 89 private InjectionInflationController mInjectionInflationController; 90 private boolean mSwipeUpToRetry; 91 92 private final ViewConfiguration mViewConfiguration; 93 private final SpringAnimation mSpringAnimation; 94 private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); 95 private final KeyguardUpdateMonitor mUpdateMonitor; 96 private final UnlockMethodCache mUnlockMethodCache; 97 98 private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); 99 private float mLastTouchY = -1; 100 private int mActivePointerId = -1; 101 private boolean mIsDragging; 102 private float mStartTouchY = -1; 103 104 // Used to notify the container when something interesting happens. 105 public interface SecurityCallback { dismiss(boolean authenticated, int targetUserId)106 public boolean dismiss(boolean authenticated, int targetUserId); userActivity()107 public void userActivity(); onSecurityModeChanged(SecurityMode securityMode, boolean needsInput)108 public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput); 109 110 /** 111 * @param strongAuth wheher the user has authenticated with strong authentication like 112 * pattern, password or PIN but not by trust agents or fingerprint 113 * @param targetUserId a user that needs to be the foreground user at the finish completion. 114 */ finish(boolean strongAuth, int targetUserId)115 public void finish(boolean strongAuth, int targetUserId); reset()116 public void reset(); onCancelClicked()117 public void onCancelClicked(); 118 } 119 KeyguardSecurityContainer(Context context, AttributeSet attrs)120 public KeyguardSecurityContainer(Context context, AttributeSet attrs) { 121 this(context, attrs, 0); 122 } 123 KeyguardSecurityContainer(Context context)124 public KeyguardSecurityContainer(Context context) { 125 this(context, null, 0); 126 } 127 KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle)128 public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) { 129 super(context, attrs, defStyle); 130 mSecurityModel = new KeyguardSecurityModel(context); 131 mLockPatternUtils = new LockPatternUtils(context); 132 mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext); 133 mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y); 134 mInjectionInflationController = new InjectionInflationController( 135 SystemUIFactory.getInstance().getRootComponent()); 136 mUnlockMethodCache = UnlockMethodCache.getInstance(context); 137 mViewConfiguration = ViewConfiguration.get(context); 138 } 139 setSecurityCallback(SecurityCallback callback)140 public void setSecurityCallback(SecurityCallback callback) { 141 mSecurityCallback = callback; 142 } 143 144 @Override onResume(int reason)145 public void onResume(int reason) { 146 if (mCurrentSecuritySelection != SecurityMode.None) { 147 getSecurityView(mCurrentSecuritySelection).onResume(reason); 148 } 149 updateBiometricRetry(); 150 } 151 152 @Override onPause()153 public void onPause() { 154 if (mAlertDialog != null) { 155 mAlertDialog.dismiss(); 156 mAlertDialog = null; 157 } 158 if (mCurrentSecuritySelection != SecurityMode.None) { 159 getSecurityView(mCurrentSecuritySelection).onPause(); 160 } 161 } 162 163 @Override shouldDelayChildPressedState()164 public boolean shouldDelayChildPressedState() { 165 return true; 166 } 167 168 @Override onInterceptTouchEvent(MotionEvent event)169 public boolean onInterceptTouchEvent(MotionEvent event) { 170 switch (event.getActionMasked()) { 171 case MotionEvent.ACTION_DOWN: 172 int pointerIndex = event.getActionIndex(); 173 mStartTouchY = event.getY(pointerIndex); 174 mActivePointerId = event.getPointerId(pointerIndex); 175 mVelocityTracker.clear(); 176 break; 177 case MotionEvent.ACTION_MOVE: 178 if (mIsDragging) { 179 return true; 180 } 181 if (!mSwipeUpToRetry) { 182 return false; 183 } 184 // Avoid dragging the pattern view 185 if (mCurrentSecurityView.disallowInterceptTouch(event)) { 186 return false; 187 } 188 int index = event.findPointerIndex(mActivePointerId); 189 float touchSlop = mViewConfiguration.getScaledTouchSlop() * SLOP_SCALE; 190 if (mCurrentSecurityView != null && index != -1 191 && mStartTouchY - event.getY(index) > touchSlop) { 192 mIsDragging = true; 193 return true; 194 } 195 break; 196 case MotionEvent.ACTION_CANCEL: 197 case MotionEvent.ACTION_UP: 198 mIsDragging = false; 199 break; 200 } 201 return false; 202 } 203 204 @Override onTouchEvent(MotionEvent event)205 public boolean onTouchEvent(MotionEvent event) { 206 final int action = event.getActionMasked(); 207 switch (action) { 208 case MotionEvent.ACTION_MOVE: 209 mVelocityTracker.addMovement(event); 210 int pointerIndex = event.findPointerIndex(mActivePointerId); 211 float y = event.getY(pointerIndex); 212 if (mLastTouchY != -1) { 213 float dy = y - mLastTouchY; 214 setTranslationY(getTranslationY() + dy * TOUCH_Y_MULTIPLIER); 215 } 216 mLastTouchY = y; 217 break; 218 case MotionEvent.ACTION_UP: 219 case MotionEvent.ACTION_CANCEL: 220 mActivePointerId = -1; 221 mLastTouchY = -1; 222 mIsDragging = false; 223 startSpringAnimation(mVelocityTracker.getYVelocity()); 224 break; 225 case MotionEvent.ACTION_POINTER_UP: 226 int index = event.getActionIndex(); 227 int pointerId = event.getPointerId(index); 228 if (pointerId == mActivePointerId) { 229 // This was our active pointer going up. Choose a new 230 // active pointer and adjust accordingly. 231 final int newPointerIndex = index == 0 ? 1 : 0; 232 mLastTouchY = event.getY(newPointerIndex); 233 mActivePointerId = event.getPointerId(newPointerIndex); 234 } 235 break; 236 } 237 if (action == MotionEvent.ACTION_UP) { 238 if (-getTranslationY() > TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 239 MIN_DRAG_SIZE, getResources().getDisplayMetrics()) 240 && !mUpdateMonitor.isFaceDetectionRunning()) { 241 mUpdateMonitor.requestFaceAuth(); 242 mCallback.userActivity(); 243 showMessage(null, null); 244 } 245 } 246 return true; 247 } 248 startSpringAnimation(float startVelocity)249 private void startSpringAnimation(float startVelocity) { 250 mSpringAnimation 251 .setStartVelocity(startVelocity) 252 .animateToFinalPosition(0); 253 } 254 startAppearAnimation()255 public void startAppearAnimation() { 256 if (mCurrentSecuritySelection != SecurityMode.None) { 257 getSecurityView(mCurrentSecuritySelection).startAppearAnimation(); 258 } 259 } 260 startDisappearAnimation(Runnable onFinishRunnable)261 public boolean startDisappearAnimation(Runnable onFinishRunnable) { 262 if (mCurrentSecuritySelection != SecurityMode.None) { 263 return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation( 264 onFinishRunnable); 265 } 266 return false; 267 } 268 269 /** 270 * Enables/disables swipe up to retry on the bouncer. 271 */ updateBiometricRetry()272 private void updateBiometricRetry() { 273 SecurityMode securityMode = getSecurityMode(); 274 mSwipeUpToRetry = mUnlockMethodCache.isFaceAuthEnabled() 275 && securityMode != SecurityMode.SimPin 276 && securityMode != SecurityMode.SimPuk 277 && securityMode != SecurityMode.None; 278 } 279 getTitle()280 public CharSequence getTitle() { 281 return mSecurityViewFlipper.getTitle(); 282 } 283 getSecurityView(SecurityMode securityMode)284 private KeyguardSecurityView getSecurityView(SecurityMode securityMode) { 285 final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); 286 KeyguardSecurityView view = null; 287 final int children = mSecurityViewFlipper.getChildCount(); 288 for (int child = 0; child < children; child++) { 289 if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) { 290 view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child)); 291 break; 292 } 293 } 294 int layoutId = getLayoutIdFor(securityMode); 295 if (view == null && layoutId != 0) { 296 final LayoutInflater inflater = LayoutInflater.from(mContext); 297 if (DEBUG) Log.v(TAG, "inflating id = " + layoutId); 298 View v = mInjectionInflationController.injectable(inflater) 299 .inflate(layoutId, mSecurityViewFlipper, false); 300 mSecurityViewFlipper.addView(v); 301 updateSecurityView(v); 302 view = (KeyguardSecurityView)v; 303 view.reset(); 304 } 305 306 return view; 307 } 308 updateSecurityView(View view)309 private void updateSecurityView(View view) { 310 if (view instanceof KeyguardSecurityView) { 311 KeyguardSecurityView ksv = (KeyguardSecurityView) view; 312 ksv.setKeyguardCallback(mCallback); 313 ksv.setLockPatternUtils(mLockPatternUtils); 314 } else { 315 Log.w(TAG, "View " + view + " is not a KeyguardSecurityView"); 316 } 317 } 318 onFinishInflate()319 protected void onFinishInflate() { 320 mSecurityViewFlipper = findViewById(R.id.view_flipper); 321 mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils); 322 } 323 setLockPatternUtils(LockPatternUtils utils)324 public void setLockPatternUtils(LockPatternUtils utils) { 325 mLockPatternUtils = utils; 326 mSecurityModel.setLockPatternUtils(utils); 327 mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils); 328 } 329 330 @Override fitSystemWindows(Rect insets)331 protected boolean fitSystemWindows(Rect insets) { 332 // Consume bottom insets because we're setting the padding locally (for IME and navbar.) 333 setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), insets.bottom); 334 insets.bottom = 0; 335 return false; 336 } 337 showDialog(String title, String message)338 private void showDialog(String title, String message) { 339 if (mAlertDialog != null) { 340 mAlertDialog.dismiss(); 341 } 342 343 mAlertDialog = new AlertDialog.Builder(mContext) 344 .setTitle(title) 345 .setMessage(message) 346 .setCancelable(false) 347 .setNeutralButton(R.string.ok, null) 348 .create(); 349 if (!(mContext instanceof Activity)) { 350 mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 351 } 352 mAlertDialog.show(); 353 } 354 showTimeoutDialog(int userId, int timeoutMs)355 private void showTimeoutDialog(int userId, int timeoutMs) { 356 int timeoutInSeconds = (int) timeoutMs / 1000; 357 int messageId = 0; 358 359 switch (mSecurityModel.getSecurityMode(userId)) { 360 case Pattern: 361 messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message; 362 break; 363 case PIN: 364 messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message; 365 break; 366 case Password: 367 messageId = R.string.kg_too_many_failed_password_attempts_dialog_message; 368 break; 369 // These don't have timeout dialogs. 370 case Invalid: 371 case None: 372 case SimPin: 373 case SimPuk: 374 break; 375 } 376 377 if (messageId != 0) { 378 final String message = mContext.getString(messageId, 379 mLockPatternUtils.getCurrentFailedPasswordAttempts(userId), 380 timeoutInSeconds); 381 showDialog(null, message); 382 } 383 } 384 showAlmostAtWipeDialog(int attempts, int remaining, int userType)385 private void showAlmostAtWipeDialog(int attempts, int remaining, int userType) { 386 String message = null; 387 switch (userType) { 388 case USER_TYPE_PRIMARY: 389 message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe, 390 attempts, remaining); 391 break; 392 case USER_TYPE_SECONDARY_USER: 393 message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_user, 394 attempts, remaining); 395 break; 396 case USER_TYPE_WORK_PROFILE: 397 message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_profile, 398 attempts, remaining); 399 break; 400 } 401 showDialog(null, message); 402 } 403 showWipeDialog(int attempts, int userType)404 private void showWipeDialog(int attempts, int userType) { 405 String message = null; 406 switch (userType) { 407 case USER_TYPE_PRIMARY: 408 message = mContext.getString(R.string.kg_failed_attempts_now_wiping, 409 attempts); 410 break; 411 case USER_TYPE_SECONDARY_USER: 412 message = mContext.getString(R.string.kg_failed_attempts_now_erasing_user, 413 attempts); 414 break; 415 case USER_TYPE_WORK_PROFILE: 416 message = mContext.getString(R.string.kg_failed_attempts_now_erasing_profile, 417 attempts); 418 break; 419 } 420 showDialog(null, message); 421 } 422 reportFailedUnlockAttempt(int userId, int timeoutMs)423 private void reportFailedUnlockAttempt(int userId, int timeoutMs) { 424 // +1 for this time 425 final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1; 426 427 if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts); 428 429 final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager(); 430 final int failedAttemptsBeforeWipe = 431 dpm.getMaximumFailedPasswordsForWipe(null, userId); 432 433 final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ? 434 (failedAttemptsBeforeWipe - failedAttempts) 435 : Integer.MAX_VALUE; // because DPM returns 0 if no restriction 436 if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) { 437 // The user has installed a DevicePolicyManager that requests a user/profile to be wiped 438 // N attempts. Once we get below the grace period, we post this dialog every time as a 439 // clear warning until the deletion fires. 440 // Check which profile has the strictest policy for failed password attempts 441 final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId); 442 int userType = USER_TYPE_PRIMARY; 443 if (expiringUser == userId) { 444 // TODO: http://b/23522538 445 if (expiringUser != UserHandle.USER_SYSTEM) { 446 userType = USER_TYPE_SECONDARY_USER; 447 } 448 } else if (expiringUser != UserHandle.USER_NULL) { 449 userType = USER_TYPE_WORK_PROFILE; 450 } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY 451 if (remainingBeforeWipe > 0) { 452 showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType); 453 } else { 454 // Too many attempts. The device will be wiped shortly. 455 Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!"); 456 showWipeDialog(failedAttempts, userType); 457 } 458 } 459 mLockPatternUtils.reportFailedPasswordAttempt(userId); 460 if (timeoutMs > 0) { 461 mLockPatternUtils.reportPasswordLockout(timeoutMs, userId); 462 showTimeoutDialog(userId, timeoutMs); 463 } 464 } 465 466 /** 467 * Shows the primary security screen for the user. This will be either the multi-selector 468 * or the user's security method. 469 * @param turningOff true if the device is being turned off 470 */ showPrimarySecurityScreen(boolean turningOff)471 void showPrimarySecurityScreen(boolean turningOff) { 472 SecurityMode securityMode = mSecurityModel.getSecurityMode( 473 KeyguardUpdateMonitor.getCurrentUser()); 474 if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")"); 475 showSecurityScreen(securityMode); 476 } 477 478 /** 479 * Shows the next security screen if there is one. 480 * @param authenticated true if the user entered the correct authentication 481 * @param targetUserId a user that needs to be the foreground user at the finish (if called) 482 * completion. 483 * @return true if keyguard is done 484 */ showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId)485 boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId) { 486 if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")"); 487 boolean finish = false; 488 boolean strongAuth = false; 489 int eventSubtype = -1; 490 if (mUpdateMonitor.getUserHasTrust(targetUserId)) { 491 finish = true; 492 eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS; 493 } else if (mUpdateMonitor.getUserUnlockedWithBiometric(targetUserId)) { 494 finish = true; 495 eventSubtype = BOUNCER_DISMISS_BIOMETRIC; 496 } else if (SecurityMode.None == mCurrentSecuritySelection) { 497 SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); 498 if (SecurityMode.None == securityMode) { 499 finish = true; // no security required 500 eventSubtype = BOUNCER_DISMISS_NONE_SECURITY; 501 } else { 502 showSecurityScreen(securityMode); // switch to the alternate security view 503 } 504 } else if (authenticated) { 505 switch (mCurrentSecuritySelection) { 506 case Pattern: 507 case Password: 508 case PIN: 509 strongAuth = true; 510 finish = true; 511 eventSubtype = BOUNCER_DISMISS_PASSWORD; 512 break; 513 514 case SimPin: 515 case SimPuk: 516 // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home 517 SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); 518 if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled( 519 KeyguardUpdateMonitor.getCurrentUser())) { 520 finish = true; 521 eventSubtype = BOUNCER_DISMISS_SIM; 522 } else { 523 showSecurityScreen(securityMode); 524 } 525 break; 526 527 default: 528 Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe"); 529 showPrimarySecurityScreen(false); 530 break; 531 } 532 } 533 if (eventSubtype != -1) { 534 mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER) 535 .setType(MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype)); 536 } 537 if (finish) { 538 mSecurityCallback.finish(strongAuth, targetUserId); 539 } 540 return finish; 541 } 542 543 /** 544 * Switches to the given security view unless it's already being shown, in which case 545 * this is a no-op. 546 * 547 * @param securityMode 548 */ showSecurityScreen(SecurityMode securityMode)549 private void showSecurityScreen(SecurityMode securityMode) { 550 if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")"); 551 552 if (securityMode == mCurrentSecuritySelection) return; 553 554 KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection); 555 KeyguardSecurityView newView = getSecurityView(securityMode); 556 557 // Emulate Activity life cycle 558 if (oldView != null) { 559 oldView.onPause(); 560 oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view 561 } 562 if (securityMode != SecurityMode.None) { 563 newView.onResume(KeyguardSecurityView.VIEW_REVEALED); 564 newView.setKeyguardCallback(mCallback); 565 } 566 567 // Find and show this child. 568 final int childCount = mSecurityViewFlipper.getChildCount(); 569 570 final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); 571 for (int i = 0; i < childCount; i++) { 572 if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) { 573 mSecurityViewFlipper.setDisplayedChild(i); 574 break; 575 } 576 } 577 578 mCurrentSecuritySelection = securityMode; 579 mCurrentSecurityView = newView; 580 mSecurityCallback.onSecurityModeChanged(securityMode, 581 securityMode != SecurityMode.None && newView.needsInput()); 582 } 583 getFlipper()584 private KeyguardSecurityViewFlipper getFlipper() { 585 for (int i = 0; i < getChildCount(); i++) { 586 View child = getChildAt(i); 587 if (child instanceof KeyguardSecurityViewFlipper) { 588 return (KeyguardSecurityViewFlipper) child; 589 } 590 } 591 return null; 592 } 593 594 private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() { 595 public void userActivity() { 596 if (mSecurityCallback != null) { 597 mSecurityCallback.userActivity(); 598 } 599 } 600 601 @Override 602 public void onUserInput() { 603 mUpdateMonitor.cancelFaceAuth(); 604 } 605 606 public void dismiss(boolean authenticated, int targetId) { 607 mSecurityCallback.dismiss(authenticated, targetId); 608 } 609 610 public boolean isVerifyUnlockOnly() { 611 return mIsVerifyUnlockOnly; 612 } 613 614 public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { 615 if (success) { 616 StatsLog.write(StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED, 617 StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS); 618 mLockPatternUtils.reportSuccessfulPasswordAttempt(userId); 619 // Force a garbage collection in an attempt to erase any lockscreen password left in 620 // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard 621 // dismiss animation janky. 622 ThreadUtils.postOnBackgroundThread(() -> { 623 try { 624 Thread.sleep(5000); 625 } catch (InterruptedException ignored) { } 626 Runtime.getRuntime().gc(); 627 }); 628 } else { 629 StatsLog.write(StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED, 630 StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE); 631 KeyguardSecurityContainer.this.reportFailedUnlockAttempt(userId, timeoutMs); 632 } 633 mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER) 634 .setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE)); 635 } 636 637 public void reset() { 638 mSecurityCallback.reset(); 639 } 640 641 public void onCancelClicked() { 642 mSecurityCallback.onCancelClicked(); 643 } 644 }; 645 646 // The following is used to ignore callbacks from SecurityViews that are no longer current 647 // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the 648 // state for the current security method. 649 private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() { 650 @Override 651 public void userActivity() { } 652 @Override 653 public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { } 654 @Override 655 public boolean isVerifyUnlockOnly() { return false; } 656 @Override 657 public void dismiss(boolean securityVerified, int targetUserId) { } 658 @Override 659 public void onUserInput() { } 660 @Override 661 public void reset() {} 662 }; 663 getSecurityViewIdForMode(SecurityMode securityMode)664 private int getSecurityViewIdForMode(SecurityMode securityMode) { 665 switch (securityMode) { 666 case Pattern: return R.id.keyguard_pattern_view; 667 case PIN: return R.id.keyguard_pin_view; 668 case Password: return R.id.keyguard_password_view; 669 case SimPin: return R.id.keyguard_sim_pin_view; 670 case SimPuk: return R.id.keyguard_sim_puk_view; 671 } 672 return 0; 673 } 674 675 @VisibleForTesting getLayoutIdFor(SecurityMode securityMode)676 public int getLayoutIdFor(SecurityMode securityMode) { 677 switch (securityMode) { 678 case Pattern: return R.layout.keyguard_pattern_view; 679 case PIN: return R.layout.keyguard_pin_view; 680 case Password: return R.layout.keyguard_password_view; 681 case SimPin: return R.layout.keyguard_sim_pin_view; 682 case SimPuk: return R.layout.keyguard_sim_puk_view; 683 default: 684 return 0; 685 } 686 } 687 getSecurityMode()688 public SecurityMode getSecurityMode() { 689 return mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()); 690 } 691 getCurrentSecurityMode()692 public SecurityMode getCurrentSecurityMode() { 693 return mCurrentSecuritySelection; 694 } 695 getCurrentSecurityView()696 public KeyguardSecurityView getCurrentSecurityView() { 697 return mCurrentSecurityView; 698 } 699 verifyUnlock()700 public void verifyUnlock() { 701 mIsVerifyUnlockOnly = true; 702 showSecurityScreen(getSecurityMode()); 703 } 704 getCurrentSecuritySelection()705 public SecurityMode getCurrentSecuritySelection() { 706 return mCurrentSecuritySelection; 707 } 708 dismiss(boolean authenticated, int targetUserId)709 public void dismiss(boolean authenticated, int targetUserId) { 710 mCallback.dismiss(authenticated, targetUserId); 711 } 712 needsInput()713 public boolean needsInput() { 714 return mSecurityViewFlipper.needsInput(); 715 } 716 717 @Override setKeyguardCallback(KeyguardSecurityCallback callback)718 public void setKeyguardCallback(KeyguardSecurityCallback callback) { 719 mSecurityViewFlipper.setKeyguardCallback(callback); 720 } 721 722 @Override reset()723 public void reset() { 724 mSecurityViewFlipper.reset(); 725 } 726 727 @Override getCallback()728 public KeyguardSecurityCallback getCallback() { 729 return mSecurityViewFlipper.getCallback(); 730 } 731 732 @Override showPromptReason(int reason)733 public void showPromptReason(int reason) { 734 if (mCurrentSecuritySelection != SecurityMode.None) { 735 if (reason != PROMPT_REASON_NONE) { 736 Log.i(TAG, "Strong auth required, reason: " + reason); 737 } 738 getSecurityView(mCurrentSecuritySelection).showPromptReason(reason); 739 } 740 } 741 showMessage(CharSequence message, ColorStateList colorState)742 public void showMessage(CharSequence message, ColorStateList colorState) { 743 if (mCurrentSecuritySelection != SecurityMode.None) { 744 getSecurityView(mCurrentSecuritySelection).showMessage(message, colorState); 745 } 746 } 747 748 @Override showUsabilityHint()749 public void showUsabilityHint() { 750 mSecurityViewFlipper.showUsabilityHint(); 751 } 752 753 } 754 755