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 static android.view.ViewRootImpl.NEW_INSETS_MODE_FULL; 19 import static android.view.ViewRootImpl.sNewInsetsMode; 20 import static android.view.WindowInsets.Type.ime; 21 import static android.view.WindowInsets.Type.systemBars; 22 import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; 23 24 import static com.android.systemui.DejankUtils.whitelistIpcs; 25 26 import static java.lang.Integer.max; 27 28 import android.animation.Animator; 29 import android.animation.AnimatorListenerAdapter; 30 import android.animation.ValueAnimator; 31 import android.app.Activity; 32 import android.app.AlertDialog; 33 import android.app.admin.DevicePolicyManager; 34 import android.content.Context; 35 import android.content.Intent; 36 import android.content.res.ColorStateList; 37 import android.graphics.Insets; 38 import android.graphics.Rect; 39 import android.metrics.LogMaker; 40 import android.os.Handler; 41 import android.os.Looper; 42 import android.os.UserHandle; 43 import android.util.AttributeSet; 44 import android.util.Log; 45 import android.util.MathUtils; 46 import android.util.Slog; 47 import android.util.TypedValue; 48 import android.view.LayoutInflater; 49 import android.view.MotionEvent; 50 import android.view.VelocityTracker; 51 import android.view.View; 52 import android.view.ViewConfiguration; 53 import android.view.WindowInsets; 54 import android.view.WindowInsetsAnimation; 55 import android.view.WindowInsetsAnimationControlListener; 56 import android.view.WindowInsetsAnimationController; 57 import android.view.WindowManager; 58 import android.widget.FrameLayout; 59 60 import androidx.annotation.NonNull; 61 import androidx.annotation.Nullable; 62 import androidx.annotation.VisibleForTesting; 63 import androidx.dynamicanimation.animation.DynamicAnimation; 64 import androidx.dynamicanimation.animation.SpringAnimation; 65 66 import com.android.internal.logging.MetricsLogger; 67 import com.android.internal.logging.UiEvent; 68 import com.android.internal.logging.UiEventLogger; 69 import com.android.internal.logging.UiEventLoggerImpl; 70 import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 71 import com.android.internal.widget.LockPatternUtils; 72 import com.android.keyguard.KeyguardSecurityModel.SecurityMode; 73 import com.android.settingslib.utils.ThreadUtils; 74 import com.android.systemui.Dependency; 75 import com.android.systemui.Interpolators; 76 import com.android.systemui.R; 77 import com.android.systemui.SystemUIFactory; 78 import com.android.systemui.shared.system.SysUiStatsLog; 79 import com.android.systemui.statusbar.policy.KeyguardStateController; 80 import com.android.systemui.util.InjectionInflationController; 81 82 import java.util.List; 83 84 public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView { 85 private static final boolean DEBUG = KeyguardConstants.DEBUG; 86 private static final String TAG = "KeyguardSecurityView"; 87 88 private static final int USER_TYPE_PRIMARY = 1; 89 private static final int USER_TYPE_WORK_PROFILE = 2; 90 private static final int USER_TYPE_SECONDARY_USER = 3; 91 92 // Bouncer is dismissed due to no security. 93 private static final int BOUNCER_DISMISS_NONE_SECURITY = 0; 94 // Bouncer is dismissed due to pin, password or pattern entered. 95 private static final int BOUNCER_DISMISS_PASSWORD = 1; 96 // Bouncer is dismissed due to biometric (face, fingerprint or iris) authenticated. 97 private static final int BOUNCER_DISMISS_BIOMETRIC = 2; 98 // Bouncer is dismissed due to extended access granted. 99 private static final int BOUNCER_DISMISS_EXTENDED_ACCESS = 3; 100 // Bouncer is dismissed due to sim card unlock code entered. 101 private static final int BOUNCER_DISMISS_SIM = 4; 102 103 // Make the view move slower than the finger, as if the spring were applying force. 104 private static final float TOUCH_Y_MULTIPLIER = 0.25f; 105 // How much you need to drag the bouncer to trigger an auth retry (in dps.) 106 private static final float MIN_DRAG_SIZE = 10; 107 // How much to scale the default slop by, to avoid accidental drags. 108 private static final float SLOP_SCALE = 4f; 109 110 private static final UiEventLogger sUiEventLogger = new UiEventLoggerImpl(); 111 112 private static final long IME_DISAPPEAR_DURATION_MS = 125; 113 114 private KeyguardSecurityModel mSecurityModel; 115 private LockPatternUtils mLockPatternUtils; 116 117 @VisibleForTesting 118 KeyguardSecurityViewFlipper mSecurityViewFlipper; 119 private boolean mIsVerifyUnlockOnly; 120 private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid; 121 private KeyguardSecurityView mCurrentSecurityView; 122 private SecurityCallback mSecurityCallback; 123 private AlertDialog mAlertDialog; 124 private InjectionInflationController mInjectionInflationController; 125 private boolean mSwipeUpToRetry; 126 private AdminSecondaryLockScreenController mSecondaryLockScreenController; 127 128 private final ViewConfiguration mViewConfiguration; 129 private final SpringAnimation mSpringAnimation; 130 private final VelocityTracker mVelocityTracker = VelocityTracker.obtain(); 131 private final KeyguardUpdateMonitor mUpdateMonitor; 132 private final KeyguardStateController mKeyguardStateController; 133 134 private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class); 135 private float mLastTouchY = -1; 136 private int mActivePointerId = -1; 137 private boolean mIsDragging; 138 private float mStartTouchY = -1; 139 private boolean mDisappearAnimRunning; 140 141 private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback = 142 new WindowInsetsAnimation.Callback(DISPATCH_MODE_STOP) { 143 144 private final Rect mInitialBounds = new Rect(); 145 private final Rect mFinalBounds = new Rect(); 146 147 @Override 148 public void onPrepare(WindowInsetsAnimation animation) { 149 mSecurityViewFlipper.getBoundsOnScreen(mInitialBounds); 150 } 151 152 @Override 153 public WindowInsetsAnimation.Bounds onStart(WindowInsetsAnimation animation, 154 WindowInsetsAnimation.Bounds bounds) { 155 mSecurityViewFlipper.getBoundsOnScreen(mFinalBounds); 156 return bounds; 157 } 158 159 @Override 160 public WindowInsets onProgress(WindowInsets windowInsets, 161 List<WindowInsetsAnimation> list) { 162 int translationY = 0; 163 if (mDisappearAnimRunning) { 164 mSecurityViewFlipper.setTranslationY( 165 mInitialBounds.bottom - mFinalBounds.bottom); 166 } else { 167 for (WindowInsetsAnimation animation : list) { 168 if ((animation.getTypeMask() & WindowInsets.Type.ime()) == 0) { 169 continue; 170 } 171 final int paddingBottom = (int) MathUtils.lerp( 172 mInitialBounds.bottom - mFinalBounds.bottom, 0, 173 animation.getInterpolatedFraction()); 174 translationY += paddingBottom; 175 } 176 mSecurityViewFlipper.setTranslationY(translationY); 177 } 178 return windowInsets; 179 } 180 181 @Override 182 public void onEnd(WindowInsetsAnimation animation) { 183 if (!mDisappearAnimRunning) { 184 mSecurityViewFlipper.setTranslationY(0); 185 } 186 } 187 }; 188 189 // Used to notify the container when something interesting happens. 190 public interface SecurityCallback { dismiss(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen)191 public boolean dismiss(boolean authenticated, int targetUserId, 192 boolean bypassSecondaryLockScreen); userActivity()193 public void userActivity(); onSecurityModeChanged(SecurityMode securityMode, boolean needsInput)194 public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput); 195 196 /** 197 * @param strongAuth wheher the user has authenticated with strong authentication like 198 * pattern, password or PIN but not by trust agents or fingerprint 199 * @param targetUserId a user that needs to be the foreground user at the finish completion. 200 */ finish(boolean strongAuth, int targetUserId)201 public void finish(boolean strongAuth, int targetUserId); reset()202 public void reset(); onCancelClicked()203 public void onCancelClicked(); 204 } 205 206 @VisibleForTesting 207 public enum BouncerUiEvent implements UiEventLogger.UiEventEnum { 208 @UiEvent(doc = "Default UiEvent used for variable initialization.") 209 UNKNOWN(0), 210 211 @UiEvent(doc = "Bouncer is dismissed using extended security access.") 212 BOUNCER_DISMISS_EXTENDED_ACCESS(413), 213 214 @UiEvent(doc = "Bouncer is dismissed using biometric.") 215 BOUNCER_DISMISS_BIOMETRIC(414), 216 217 @UiEvent(doc = "Bouncer is dismissed without security access.") 218 BOUNCER_DISMISS_NONE_SECURITY(415), 219 220 @UiEvent(doc = "Bouncer is dismissed using password security.") 221 BOUNCER_DISMISS_PASSWORD(416), 222 223 @UiEvent(doc = "Bouncer is dismissed using sim security access.") 224 BOUNCER_DISMISS_SIM(417), 225 226 @UiEvent(doc = "Bouncer is successfully unlocked using password.") 227 BOUNCER_PASSWORD_SUCCESS(418), 228 229 @UiEvent(doc = "An attempt to unlock bouncer using password has failed.") 230 BOUNCER_PASSWORD_FAILURE(419); 231 232 private final int mId; 233 BouncerUiEvent(int id)234 BouncerUiEvent(int id) { 235 mId = id; 236 } 237 238 @Override getId()239 public int getId() { 240 return mId; 241 } 242 } 243 KeyguardSecurityContainer(Context context, AttributeSet attrs)244 public KeyguardSecurityContainer(Context context, AttributeSet attrs) { 245 this(context, attrs, 0); 246 } 247 KeyguardSecurityContainer(Context context)248 public KeyguardSecurityContainer(Context context) { 249 this(context, null, 0); 250 } 251 KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle)252 public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) { 253 super(context, attrs, defStyle); 254 mSecurityModel = Dependency.get(KeyguardSecurityModel.class); 255 mLockPatternUtils = new LockPatternUtils(context); 256 mUpdateMonitor = Dependency.get(KeyguardUpdateMonitor.class); 257 mSpringAnimation = new SpringAnimation(this, DynamicAnimation.Y); 258 mInjectionInflationController = new InjectionInflationController( 259 SystemUIFactory.getInstance().getRootComponent()); 260 mViewConfiguration = ViewConfiguration.get(context); 261 mKeyguardStateController = Dependency.get(KeyguardStateController.class); 262 mSecondaryLockScreenController = new AdminSecondaryLockScreenController(context, this, 263 mUpdateMonitor, mCallback, new Handler(Looper.myLooper())); 264 } 265 setSecurityCallback(SecurityCallback callback)266 public void setSecurityCallback(SecurityCallback callback) { 267 mSecurityCallback = callback; 268 } 269 270 @Override onResume(int reason)271 public void onResume(int reason) { 272 if (mCurrentSecuritySelection != SecurityMode.None) { 273 getSecurityView(mCurrentSecuritySelection).onResume(reason); 274 } 275 mSecurityViewFlipper.setWindowInsetsAnimationCallback(mWindowInsetsAnimationCallback); 276 updateBiometricRetry(); 277 } 278 279 @Override onPause()280 public void onPause() { 281 if (mAlertDialog != null) { 282 mAlertDialog.dismiss(); 283 mAlertDialog = null; 284 } 285 mSecondaryLockScreenController.hide(); 286 if (mCurrentSecuritySelection != SecurityMode.None) { 287 getSecurityView(mCurrentSecuritySelection).onPause(); 288 } 289 mSecurityViewFlipper.setWindowInsetsAnimationCallback(null); 290 } 291 292 @Override onStartingToHide()293 public void onStartingToHide() { 294 if (mCurrentSecuritySelection != SecurityMode.None) { 295 getSecurityView(mCurrentSecuritySelection).onStartingToHide(); 296 } 297 } 298 299 @Override shouldDelayChildPressedState()300 public boolean shouldDelayChildPressedState() { 301 return true; 302 } 303 304 @Override onInterceptTouchEvent(MotionEvent event)305 public boolean onInterceptTouchEvent(MotionEvent event) { 306 switch (event.getActionMasked()) { 307 case MotionEvent.ACTION_DOWN: 308 int pointerIndex = event.getActionIndex(); 309 mStartTouchY = event.getY(pointerIndex); 310 mActivePointerId = event.getPointerId(pointerIndex); 311 mVelocityTracker.clear(); 312 break; 313 case MotionEvent.ACTION_MOVE: 314 if (mIsDragging) { 315 return true; 316 } 317 if (!mSwipeUpToRetry) { 318 return false; 319 } 320 // Avoid dragging the pattern view 321 if (mCurrentSecurityView.disallowInterceptTouch(event)) { 322 return false; 323 } 324 int index = event.findPointerIndex(mActivePointerId); 325 float touchSlop = mViewConfiguration.getScaledTouchSlop() * SLOP_SCALE; 326 if (mCurrentSecurityView != null && index != -1 327 && mStartTouchY - event.getY(index) > touchSlop) { 328 mIsDragging = true; 329 return true; 330 } 331 break; 332 case MotionEvent.ACTION_CANCEL: 333 case MotionEvent.ACTION_UP: 334 mIsDragging = false; 335 break; 336 } 337 return false; 338 } 339 340 @Override onTouchEvent(MotionEvent event)341 public boolean onTouchEvent(MotionEvent event) { 342 final int action = event.getActionMasked(); 343 switch (action) { 344 case MotionEvent.ACTION_MOVE: 345 mVelocityTracker.addMovement(event); 346 int pointerIndex = event.findPointerIndex(mActivePointerId); 347 float y = event.getY(pointerIndex); 348 if (mLastTouchY != -1) { 349 float dy = y - mLastTouchY; 350 setTranslationY(getTranslationY() + dy * TOUCH_Y_MULTIPLIER); 351 } 352 mLastTouchY = y; 353 break; 354 case MotionEvent.ACTION_UP: 355 case MotionEvent.ACTION_CANCEL: 356 mActivePointerId = -1; 357 mLastTouchY = -1; 358 mIsDragging = false; 359 startSpringAnimation(mVelocityTracker.getYVelocity()); 360 break; 361 case MotionEvent.ACTION_POINTER_UP: 362 int index = event.getActionIndex(); 363 int pointerId = event.getPointerId(index); 364 if (pointerId == mActivePointerId) { 365 // This was our active pointer going up. Choose a new 366 // active pointer and adjust accordingly. 367 final int newPointerIndex = index == 0 ? 1 : 0; 368 mLastTouchY = event.getY(newPointerIndex); 369 mActivePointerId = event.getPointerId(newPointerIndex); 370 } 371 break; 372 } 373 if (action == MotionEvent.ACTION_UP) { 374 if (-getTranslationY() > TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 375 MIN_DRAG_SIZE, getResources().getDisplayMetrics()) 376 && !mUpdateMonitor.isFaceDetectionRunning()) { 377 mUpdateMonitor.requestFaceAuth(); 378 mCallback.userActivity(); 379 showMessage(null, null); 380 } 381 } 382 return true; 383 } 384 startSpringAnimation(float startVelocity)385 private void startSpringAnimation(float startVelocity) { 386 mSpringAnimation 387 .setStartVelocity(startVelocity) 388 .animateToFinalPosition(0); 389 } 390 startAppearAnimation()391 public void startAppearAnimation() { 392 if (mCurrentSecuritySelection != SecurityMode.None) { 393 getSecurityView(mCurrentSecuritySelection).startAppearAnimation(); 394 } 395 } 396 startDisappearAnimation(Runnable onFinishRunnable)397 public boolean startDisappearAnimation(Runnable onFinishRunnable) { 398 mDisappearAnimRunning = true; 399 if (mCurrentSecuritySelection == SecurityMode.Password) { 400 mSecurityViewFlipper.getWindowInsetsController().controlWindowInsetsAnimation(ime(), 401 IME_DISAPPEAR_DURATION_MS, 402 Interpolators.LINEAR, null, new WindowInsetsAnimationControlListener() { 403 404 405 @Override 406 public void onReady(@NonNull WindowInsetsAnimationController controller, 407 int types) { 408 ValueAnimator anim = ValueAnimator.ofFloat(1f, 0f); 409 anim.addUpdateListener(animation -> { 410 if (controller.isCancelled()) { 411 return; 412 } 413 Insets shownInsets = controller.getShownStateInsets(); 414 Insets insets = Insets.add(shownInsets, Insets.of(0, 0, 0, 415 (int) (-shownInsets.bottom / 4 416 * anim.getAnimatedFraction()))); 417 controller.setInsetsAndAlpha(insets, 418 (float) animation.getAnimatedValue(), 419 anim.getAnimatedFraction()); 420 }); 421 anim.addListener(new AnimatorListenerAdapter() { 422 @Override 423 public void onAnimationEnd(Animator animation) { 424 controller.finish(false); 425 } 426 }); 427 anim.setDuration(IME_DISAPPEAR_DURATION_MS); 428 anim.setInterpolator(Interpolators.FAST_OUT_LINEAR_IN); 429 anim.start(); 430 } 431 432 @Override 433 public void onFinished( 434 @NonNull WindowInsetsAnimationController controller) { 435 mDisappearAnimRunning = false; 436 } 437 438 @Override 439 public void onCancelled( 440 @Nullable WindowInsetsAnimationController controller) { 441 } 442 }); 443 } 444 if (mCurrentSecuritySelection != SecurityMode.None) { 445 return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation( 446 onFinishRunnable); 447 } 448 return false; 449 } 450 451 /** 452 * Enables/disables swipe up to retry on the bouncer. 453 */ updateBiometricRetry()454 private void updateBiometricRetry() { 455 SecurityMode securityMode = getSecurityMode(); 456 mSwipeUpToRetry = mKeyguardStateController.isFaceAuthEnabled() 457 && securityMode != SecurityMode.SimPin 458 && securityMode != SecurityMode.SimPuk 459 && securityMode != SecurityMode.None; 460 } 461 getTitle()462 public CharSequence getTitle() { 463 return mSecurityViewFlipper.getTitle(); 464 } 465 466 @VisibleForTesting getSecurityView(SecurityMode securityMode)467 protected KeyguardSecurityView getSecurityView(SecurityMode securityMode) { 468 final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); 469 KeyguardSecurityView view = null; 470 final int children = mSecurityViewFlipper.getChildCount(); 471 for (int child = 0; child < children; child++) { 472 if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) { 473 view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child)); 474 break; 475 } 476 } 477 int layoutId = getLayoutIdFor(securityMode); 478 if (view == null && layoutId != 0) { 479 final LayoutInflater inflater = LayoutInflater.from(mContext); 480 if (DEBUG) Log.v(TAG, "inflating id = " + layoutId); 481 View v = mInjectionInflationController.injectable(inflater) 482 .inflate(layoutId, mSecurityViewFlipper, false); 483 mSecurityViewFlipper.addView(v); 484 updateSecurityView(v); 485 view = (KeyguardSecurityView)v; 486 view.reset(); 487 } 488 489 return view; 490 } 491 updateSecurityView(View view)492 private void updateSecurityView(View view) { 493 if (view instanceof KeyguardSecurityView) { 494 KeyguardSecurityView ksv = (KeyguardSecurityView) view; 495 ksv.setKeyguardCallback(mCallback); 496 ksv.setLockPatternUtils(mLockPatternUtils); 497 } else { 498 Log.w(TAG, "View " + view + " is not a KeyguardSecurityView"); 499 } 500 } 501 502 @Override onFinishInflate()503 public void onFinishInflate() { 504 super.onFinishInflate(); 505 mSecurityViewFlipper = findViewById(R.id.view_flipper); 506 mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils); 507 } 508 setLockPatternUtils(LockPatternUtils utils)509 public void setLockPatternUtils(LockPatternUtils utils) { 510 mLockPatternUtils = utils; 511 mSecurityModel.setLockPatternUtils(utils); 512 mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils); 513 } 514 515 @Override onApplyWindowInsets(WindowInsets insets)516 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 517 518 // Consume bottom insets because we're setting the padding locally (for IME and navbar.) 519 int inset; 520 if (sNewInsetsMode == NEW_INSETS_MODE_FULL) { 521 int bottomInset = insets.getInsetsIgnoringVisibility(systemBars()).bottom; 522 int imeInset = insets.getInsets(ime()).bottom; 523 inset = max(bottomInset, imeInset); 524 } else { 525 inset = insets.getSystemWindowInsetBottom(); 526 } 527 setPadding(getPaddingLeft(), getPaddingTop(), getPaddingRight(), inset); 528 return insets.inset(0, 0, 0, inset); 529 } 530 531 showDialog(String title, String message)532 private void showDialog(String title, String message) { 533 if (mAlertDialog != null) { 534 mAlertDialog.dismiss(); 535 } 536 537 mAlertDialog = new AlertDialog.Builder(mContext) 538 .setTitle(title) 539 .setMessage(message) 540 .setCancelable(false) 541 .setNeutralButton(R.string.ok, null) 542 .create(); 543 if (!(mContext instanceof Activity)) { 544 mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 545 } 546 mAlertDialog.show(); 547 } 548 showTimeoutDialog(int userId, int timeoutMs)549 private void showTimeoutDialog(int userId, int timeoutMs) { 550 int timeoutInSeconds = (int) timeoutMs / 1000; 551 int messageId = 0; 552 553 switch (mSecurityModel.getSecurityMode(userId)) { 554 case Pattern: 555 messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message; 556 break; 557 case PIN: 558 messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message; 559 break; 560 case Password: 561 messageId = R.string.kg_too_many_failed_password_attempts_dialog_message; 562 break; 563 // These don't have timeout dialogs. 564 case Invalid: 565 case None: 566 case SimPin: 567 case SimPuk: 568 break; 569 } 570 571 if (messageId != 0) { 572 final String message = mContext.getString(messageId, 573 mLockPatternUtils.getCurrentFailedPasswordAttempts(userId), 574 timeoutInSeconds); 575 showDialog(null, message); 576 } 577 } 578 showAlmostAtWipeDialog(int attempts, int remaining, int userType)579 private void showAlmostAtWipeDialog(int attempts, int remaining, int userType) { 580 String message = null; 581 switch (userType) { 582 case USER_TYPE_PRIMARY: 583 message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe, 584 attempts, remaining); 585 break; 586 case USER_TYPE_SECONDARY_USER: 587 message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_user, 588 attempts, remaining); 589 break; 590 case USER_TYPE_WORK_PROFILE: 591 message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_profile, 592 attempts, remaining); 593 break; 594 } 595 showDialog(null, message); 596 } 597 showWipeDialog(int attempts, int userType)598 private void showWipeDialog(int attempts, int userType) { 599 String message = null; 600 switch (userType) { 601 case USER_TYPE_PRIMARY: 602 message = mContext.getString(R.string.kg_failed_attempts_now_wiping, 603 attempts); 604 break; 605 case USER_TYPE_SECONDARY_USER: 606 message = mContext.getString(R.string.kg_failed_attempts_now_erasing_user, 607 attempts); 608 break; 609 case USER_TYPE_WORK_PROFILE: 610 message = mContext.getString(R.string.kg_failed_attempts_now_erasing_profile, 611 attempts); 612 break; 613 } 614 showDialog(null, message); 615 } 616 reportFailedUnlockAttempt(int userId, int timeoutMs)617 private void reportFailedUnlockAttempt(int userId, int timeoutMs) { 618 // +1 for this time 619 final int failedAttempts = mLockPatternUtils.getCurrentFailedPasswordAttempts(userId) + 1; 620 621 if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts); 622 623 final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager(); 624 final int failedAttemptsBeforeWipe = 625 dpm.getMaximumFailedPasswordsForWipe(null, userId); 626 627 final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ? 628 (failedAttemptsBeforeWipe - failedAttempts) 629 : Integer.MAX_VALUE; // because DPM returns 0 if no restriction 630 if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) { 631 // The user has installed a DevicePolicyManager that requests a user/profile to be wiped 632 // N attempts. Once we get below the grace period, we post this dialog every time as a 633 // clear warning until the deletion fires. 634 // Check which profile has the strictest policy for failed password attempts 635 final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId); 636 int userType = USER_TYPE_PRIMARY; 637 if (expiringUser == userId) { 638 // TODO: http://b/23522538 639 if (expiringUser != UserHandle.USER_SYSTEM) { 640 userType = USER_TYPE_SECONDARY_USER; 641 } 642 } else if (expiringUser != UserHandle.USER_NULL) { 643 userType = USER_TYPE_WORK_PROFILE; 644 } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY 645 if (remainingBeforeWipe > 0) { 646 showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType); 647 } else { 648 // Too many attempts. The device will be wiped shortly. 649 Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!"); 650 showWipeDialog(failedAttempts, userType); 651 } 652 } 653 mLockPatternUtils.reportFailedPasswordAttempt(userId); 654 if (timeoutMs > 0) { 655 mLockPatternUtils.reportPasswordLockout(timeoutMs, userId); 656 showTimeoutDialog(userId, timeoutMs); 657 } 658 } 659 660 /** 661 * Shows the primary security screen for the user. This will be either the multi-selector 662 * or the user's security method. 663 * @param turningOff true if the device is being turned off 664 */ showPrimarySecurityScreen(boolean turningOff)665 void showPrimarySecurityScreen(boolean turningOff) { 666 SecurityMode securityMode = whitelistIpcs(() -> mSecurityModel.getSecurityMode( 667 KeyguardUpdateMonitor.getCurrentUser())); 668 if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")"); 669 showSecurityScreen(securityMode); 670 } 671 672 /** 673 * Shows the next security screen if there is one. 674 * @param authenticated true if the user entered the correct authentication 675 * @param targetUserId a user that needs to be the foreground user at the finish (if called) 676 * completion. 677 * @param bypassSecondaryLockScreen true if the user is allowed to bypass the secondary 678 * secondary lock screen requirement, if any. 679 * @return true if keyguard is done 680 */ showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId, boolean bypassSecondaryLockScreen)681 boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId, 682 boolean bypassSecondaryLockScreen) { 683 if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")"); 684 boolean finish = false; 685 boolean strongAuth = false; 686 int eventSubtype = -1; 687 BouncerUiEvent uiEvent = BouncerUiEvent.UNKNOWN; 688 if (mUpdateMonitor.getUserHasTrust(targetUserId)) { 689 finish = true; 690 eventSubtype = BOUNCER_DISMISS_EXTENDED_ACCESS; 691 uiEvent = BouncerUiEvent.BOUNCER_DISMISS_EXTENDED_ACCESS; 692 } else if (mUpdateMonitor.getUserUnlockedWithBiometric(targetUserId)) { 693 finish = true; 694 eventSubtype = BOUNCER_DISMISS_BIOMETRIC; 695 uiEvent = BouncerUiEvent.BOUNCER_DISMISS_BIOMETRIC; 696 } else if (SecurityMode.None == mCurrentSecuritySelection) { 697 SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); 698 if (SecurityMode.None == securityMode) { 699 finish = true; // no security required 700 eventSubtype = BOUNCER_DISMISS_NONE_SECURITY; 701 uiEvent = BouncerUiEvent.BOUNCER_DISMISS_NONE_SECURITY; 702 } else { 703 showSecurityScreen(securityMode); // switch to the alternate security view 704 } 705 } else if (authenticated) { 706 switch (mCurrentSecuritySelection) { 707 case Pattern: 708 case Password: 709 case PIN: 710 strongAuth = true; 711 finish = true; 712 eventSubtype = BOUNCER_DISMISS_PASSWORD; 713 uiEvent = BouncerUiEvent.BOUNCER_DISMISS_PASSWORD; 714 break; 715 716 case SimPin: 717 case SimPuk: 718 // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home 719 SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId); 720 if (securityMode == SecurityMode.None && mLockPatternUtils.isLockScreenDisabled( 721 KeyguardUpdateMonitor.getCurrentUser())) { 722 finish = true; 723 eventSubtype = BOUNCER_DISMISS_SIM; 724 uiEvent = BouncerUiEvent.BOUNCER_DISMISS_SIM; 725 } else { 726 showSecurityScreen(securityMode); 727 } 728 break; 729 730 default: 731 Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe"); 732 showPrimarySecurityScreen(false); 733 break; 734 } 735 } 736 // Check for device admin specified additional security measures. 737 if (finish && !bypassSecondaryLockScreen) { 738 Intent secondaryLockscreenIntent = 739 mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId); 740 if (secondaryLockscreenIntent != null) { 741 mSecondaryLockScreenController.show(secondaryLockscreenIntent); 742 return false; 743 } 744 } 745 if (eventSubtype != -1) { 746 mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER) 747 .setType(MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype)); 748 } 749 if (uiEvent != BouncerUiEvent.UNKNOWN) { 750 sUiEventLogger.log(uiEvent); 751 } 752 if (finish) { 753 mSecurityCallback.finish(strongAuth, targetUserId); 754 } 755 return finish; 756 } 757 758 /** 759 * Switches to the given security view unless it's already being shown, in which case 760 * this is a no-op. 761 * 762 * @param securityMode 763 */ showSecurityScreen(SecurityMode securityMode)764 private void showSecurityScreen(SecurityMode securityMode) { 765 if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")"); 766 767 if (securityMode == mCurrentSecuritySelection) return; 768 769 KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection); 770 KeyguardSecurityView newView = getSecurityView(securityMode); 771 772 // Emulate Activity life cycle 773 if (oldView != null) { 774 oldView.onPause(); 775 oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view 776 } 777 if (securityMode != SecurityMode.None) { 778 newView.onResume(KeyguardSecurityView.VIEW_REVEALED); 779 newView.setKeyguardCallback(mCallback); 780 } 781 782 // Find and show this child. 783 final int childCount = mSecurityViewFlipper.getChildCount(); 784 785 final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); 786 for (int i = 0; i < childCount; i++) { 787 if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) { 788 mSecurityViewFlipper.setDisplayedChild(i); 789 break; 790 } 791 } 792 793 mCurrentSecuritySelection = securityMode; 794 mCurrentSecurityView = newView; 795 mSecurityCallback.onSecurityModeChanged(securityMode, 796 securityMode != SecurityMode.None && newView.needsInput()); 797 } 798 getFlipper()799 private KeyguardSecurityViewFlipper getFlipper() { 800 for (int i = 0; i < getChildCount(); i++) { 801 View child = getChildAt(i); 802 if (child instanceof KeyguardSecurityViewFlipper) { 803 return (KeyguardSecurityViewFlipper) child; 804 } 805 } 806 return null; 807 } 808 809 private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() { 810 public void userActivity() { 811 if (mSecurityCallback != null) { 812 mSecurityCallback.userActivity(); 813 } 814 } 815 816 @Override 817 public void onUserInput() { 818 mUpdateMonitor.cancelFaceAuth(); 819 } 820 821 @Override 822 public void dismiss(boolean authenticated, int targetId) { 823 dismiss(authenticated, targetId, /* bypassSecondaryLockScreen */ false); 824 } 825 826 @Override 827 public void dismiss(boolean authenticated, int targetId, 828 boolean bypassSecondaryLockScreen) { 829 mSecurityCallback.dismiss(authenticated, targetId, bypassSecondaryLockScreen); 830 } 831 832 public boolean isVerifyUnlockOnly() { 833 return mIsVerifyUnlockOnly; 834 } 835 836 public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { 837 if (success) { 838 SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED, 839 SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS); 840 mLockPatternUtils.reportSuccessfulPasswordAttempt(userId); 841 // Force a garbage collection in an attempt to erase any lockscreen password left in 842 // memory. Do it asynchronously with a 5-sec delay to avoid making the keyguard 843 // dismiss animation janky. 844 ThreadUtils.postOnBackgroundThread(() -> { 845 try { 846 Thread.sleep(5000); 847 } catch (InterruptedException ignored) { } 848 Runtime.getRuntime().gc(); 849 }); 850 } else { 851 SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED, 852 SysUiStatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE); 853 KeyguardSecurityContainer.this.reportFailedUnlockAttempt(userId, timeoutMs); 854 } 855 mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER) 856 .setType(success ? MetricsEvent.TYPE_SUCCESS : MetricsEvent.TYPE_FAILURE)); 857 sUiEventLogger.log(success ? BouncerUiEvent.BOUNCER_PASSWORD_SUCCESS 858 : BouncerUiEvent.BOUNCER_PASSWORD_FAILURE); 859 } 860 861 public void reset() { 862 mSecurityCallback.reset(); 863 } 864 865 public void onCancelClicked() { 866 mSecurityCallback.onCancelClicked(); 867 } 868 }; 869 870 // The following is used to ignore callbacks from SecurityViews that are no longer current 871 // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the 872 // state for the current security method. 873 private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() { 874 @Override 875 public void userActivity() { } 876 @Override 877 public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { } 878 @Override 879 public boolean isVerifyUnlockOnly() { return false; } 880 @Override 881 public void dismiss(boolean securityVerified, int targetUserId) { } 882 @Override 883 public void dismiss(boolean authenticated, int targetId, 884 boolean bypassSecondaryLockScreen) { } 885 @Override 886 public void onUserInput() { } 887 @Override 888 public void reset() {} 889 }; 890 getSecurityViewIdForMode(SecurityMode securityMode)891 private int getSecurityViewIdForMode(SecurityMode securityMode) { 892 switch (securityMode) { 893 case Pattern: return R.id.keyguard_pattern_view; 894 case PIN: return R.id.keyguard_pin_view; 895 case Password: return R.id.keyguard_password_view; 896 case SimPin: return R.id.keyguard_sim_pin_view; 897 case SimPuk: return R.id.keyguard_sim_puk_view; 898 } 899 return 0; 900 } 901 902 @VisibleForTesting getLayoutIdFor(SecurityMode securityMode)903 public int getLayoutIdFor(SecurityMode securityMode) { 904 switch (securityMode) { 905 case Pattern: return R.layout.keyguard_pattern_view; 906 case PIN: return R.layout.keyguard_pin_view; 907 case Password: return R.layout.keyguard_password_view; 908 case SimPin: return R.layout.keyguard_sim_pin_view; 909 case SimPuk: return R.layout.keyguard_sim_puk_view; 910 default: 911 return 0; 912 } 913 } 914 getSecurityMode()915 public SecurityMode getSecurityMode() { 916 return mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()); 917 } 918 getCurrentSecurityMode()919 public SecurityMode getCurrentSecurityMode() { 920 return mCurrentSecuritySelection; 921 } 922 getCurrentSecurityView()923 public KeyguardSecurityView getCurrentSecurityView() { 924 return mCurrentSecurityView; 925 } 926 verifyUnlock()927 public void verifyUnlock() { 928 mIsVerifyUnlockOnly = true; 929 showSecurityScreen(getSecurityMode()); 930 } 931 getCurrentSecuritySelection()932 public SecurityMode getCurrentSecuritySelection() { 933 return mCurrentSecuritySelection; 934 } 935 dismiss(boolean authenticated, int targetUserId)936 public void dismiss(boolean authenticated, int targetUserId) { 937 mCallback.dismiss(authenticated, targetUserId); 938 } 939 needsInput()940 public boolean needsInput() { 941 return mSecurityViewFlipper.needsInput(); 942 } 943 944 @Override setKeyguardCallback(KeyguardSecurityCallback callback)945 public void setKeyguardCallback(KeyguardSecurityCallback callback) { 946 mSecurityViewFlipper.setKeyguardCallback(callback); 947 } 948 949 @Override reset()950 public void reset() { 951 mSecurityViewFlipper.reset(); 952 mDisappearAnimRunning = false; 953 } 954 955 @Override getCallback()956 public KeyguardSecurityCallback getCallback() { 957 return mSecurityViewFlipper.getCallback(); 958 } 959 960 @Override showPromptReason(int reason)961 public void showPromptReason(int reason) { 962 if (mCurrentSecuritySelection != SecurityMode.None) { 963 if (reason != PROMPT_REASON_NONE) { 964 Log.i(TAG, "Strong auth required, reason: " + reason); 965 } 966 getSecurityView(mCurrentSecuritySelection).showPromptReason(reason); 967 } 968 } 969 showMessage(CharSequence message, ColorStateList colorState)970 public void showMessage(CharSequence message, ColorStateList colorState) { 971 if (mCurrentSecuritySelection != SecurityMode.None) { 972 getSecurityView(mCurrentSecuritySelection).showMessage(message, colorState); 973 } 974 } 975 976 @Override showUsabilityHint()977 public void showUsabilityHint() { 978 mSecurityViewFlipper.showUsabilityHint(); 979 } 980 } 981 982