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