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.R.style;
19 import android.app.Activity;
20 import android.app.AlertDialog;
21 import android.app.admin.DevicePolicyManager;
22 import android.content.Context;
23 import android.os.UserHandle;
24 import android.support.annotation.VisibleForTesting;
25 import android.util.AttributeSet;
26 import android.util.Log;
27 import android.util.Slog;
28 import android.util.StatsLog;
29 import android.view.ContextThemeWrapper;
30 import android.view.LayoutInflater;
31 import android.view.View;
32 import android.view.WindowManager;
33 import android.widget.FrameLayout;
34 
35 import com.android.internal.widget.LockPatternUtils;
36 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
37 
38 public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView {
39     private static final boolean DEBUG = KeyguardConstants.DEBUG;
40     private static final String TAG = "KeyguardSecurityView";
41 
42     private static final int USER_TYPE_PRIMARY = 1;
43     private static final int USER_TYPE_WORK_PROFILE = 2;
44     private static final int USER_TYPE_SECONDARY_USER = 3;
45 
46     private KeyguardSecurityModel mSecurityModel;
47     private LockPatternUtils mLockPatternUtils;
48 
49     private KeyguardSecurityViewFlipper mSecurityViewFlipper;
50     private boolean mIsVerifyUnlockOnly;
51     private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
52     private SecurityCallback mSecurityCallback;
53     private AlertDialog mAlertDialog;
54 
55     private final KeyguardUpdateMonitor mUpdateMonitor;
56 
57     // Used to notify the container when something interesting happens.
58     public interface SecurityCallback {
dismiss(boolean authenticated, int targetUserId)59         public boolean dismiss(boolean authenticated, int targetUserId);
userActivity()60         public void userActivity();
onSecurityModeChanged(SecurityMode securityMode, boolean needsInput)61         public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
62 
63         /**
64          * @param strongAuth wheher the user has authenticated with strong authentication like
65          *                   pattern, password or PIN but not by trust agents or fingerprint
66          * @param targetUserId a user that needs to be the foreground user at the finish completion.
67          */
finish(boolean strongAuth, int targetUserId)68         public void finish(boolean strongAuth, int targetUserId);
reset()69         public void reset();
70     }
71 
KeyguardSecurityContainer(Context context, AttributeSet attrs)72     public KeyguardSecurityContainer(Context context, AttributeSet attrs) {
73         this(context, attrs, 0);
74     }
75 
KeyguardSecurityContainer(Context context)76     public KeyguardSecurityContainer(Context context) {
77         this(context, null, 0);
78     }
79 
KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle)80     public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) {
81         super(context, attrs, defStyle);
82         mSecurityModel = new KeyguardSecurityModel(context);
83         mLockPatternUtils = new LockPatternUtils(context);
84         mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
85     }
86 
setSecurityCallback(SecurityCallback callback)87     public void setSecurityCallback(SecurityCallback callback) {
88         mSecurityCallback = callback;
89     }
90 
91     @Override
onResume(int reason)92     public void onResume(int reason) {
93         if (mCurrentSecuritySelection != SecurityMode.None) {
94             getSecurityView(mCurrentSecuritySelection).onResume(reason);
95         }
96     }
97 
98     @Override
onPause()99     public void onPause() {
100         if (mAlertDialog != null) {
101             mAlertDialog.dismiss();
102             mAlertDialog = null;
103         }
104         if (mCurrentSecuritySelection != SecurityMode.None) {
105             getSecurityView(mCurrentSecuritySelection).onPause();
106         }
107     }
108 
startAppearAnimation()109     public void startAppearAnimation() {
110         if (mCurrentSecuritySelection != SecurityMode.None) {
111             getSecurityView(mCurrentSecuritySelection).startAppearAnimation();
112         }
113     }
114 
startDisappearAnimation(Runnable onFinishRunnable)115     public boolean startDisappearAnimation(Runnable onFinishRunnable) {
116         if (mCurrentSecuritySelection != SecurityMode.None) {
117             return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation(
118                     onFinishRunnable);
119         }
120         return false;
121     }
122 
getTitle()123     public CharSequence getTitle() {
124         return mSecurityViewFlipper.getTitle();
125     }
126 
getSecurityView(SecurityMode securityMode)127     private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
128         final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
129         KeyguardSecurityView view = null;
130         final int children = mSecurityViewFlipper.getChildCount();
131         for (int child = 0; child < children; child++) {
132             if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) {
133                 view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child));
134                 break;
135             }
136         }
137         int layoutId = getLayoutIdFor(securityMode);
138         if (view == null && layoutId != 0) {
139             final LayoutInflater inflater = LayoutInflater.from(mContext);
140             if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
141             View v = inflater.inflate(layoutId, mSecurityViewFlipper, false);
142             mSecurityViewFlipper.addView(v);
143             updateSecurityView(v);
144             view = (KeyguardSecurityView)v;
145         }
146 
147         return view;
148     }
149 
updateSecurityView(View view)150     private void updateSecurityView(View view) {
151         if (view instanceof KeyguardSecurityView) {
152             KeyguardSecurityView ksv = (KeyguardSecurityView) view;
153             ksv.setKeyguardCallback(mCallback);
154             ksv.setLockPatternUtils(mLockPatternUtils);
155         } else {
156             Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
157         }
158     }
159 
onFinishInflate()160     protected void onFinishInflate() {
161         mSecurityViewFlipper = findViewById(R.id.view_flipper);
162         mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
163     }
164 
setLockPatternUtils(LockPatternUtils utils)165     public void setLockPatternUtils(LockPatternUtils utils) {
166         mLockPatternUtils = utils;
167         mSecurityModel.setLockPatternUtils(utils);
168         mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
169     }
170 
showDialog(String title, String message)171     private void showDialog(String title, String message) {
172         if (mAlertDialog != null) {
173             mAlertDialog.dismiss();
174         }
175 
176         mAlertDialog = new AlertDialog.Builder(mContext)
177             .setTitle(title)
178             .setMessage(message)
179             .setCancelable(false)
180             .setNeutralButton(R.string.ok, null)
181             .create();
182         if (!(mContext instanceof Activity)) {
183             mAlertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
184         }
185         mAlertDialog.show();
186     }
187 
showTimeoutDialog(int userId, int timeoutMs)188     private void showTimeoutDialog(int userId, int timeoutMs) {
189         int timeoutInSeconds = (int) timeoutMs / 1000;
190         int messageId = 0;
191 
192         switch (mSecurityModel.getSecurityMode(userId)) {
193             case Pattern:
194                 messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
195                 break;
196             case PIN:
197                 messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message;
198                 break;
199             case Password:
200                 messageId = R.string.kg_too_many_failed_password_attempts_dialog_message;
201                 break;
202             // These don't have timeout dialogs.
203             case Invalid:
204             case None:
205             case SimPin:
206             case SimPuk:
207                 break;
208         }
209 
210         if (messageId != 0) {
211             final String message = mContext.getString(messageId,
212                     KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(userId),
213                     timeoutInSeconds);
214             showDialog(null, message);
215         }
216     }
217 
showAlmostAtWipeDialog(int attempts, int remaining, int userType)218     private void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
219         String message = null;
220         switch (userType) {
221             case USER_TYPE_PRIMARY:
222                 message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe,
223                         attempts, remaining);
224                 break;
225             case USER_TYPE_SECONDARY_USER:
226                 message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_user,
227                         attempts, remaining);
228                 break;
229             case USER_TYPE_WORK_PROFILE:
230                 message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_profile,
231                         attempts, remaining);
232                 break;
233         }
234         showDialog(null, message);
235     }
236 
showWipeDialog(int attempts, int userType)237     private void showWipeDialog(int attempts, int userType) {
238         String message = null;
239         switch (userType) {
240             case USER_TYPE_PRIMARY:
241                 message = mContext.getString(R.string.kg_failed_attempts_now_wiping,
242                         attempts);
243                 break;
244             case USER_TYPE_SECONDARY_USER:
245                 message = mContext.getString(R.string.kg_failed_attempts_now_erasing_user,
246                         attempts);
247                 break;
248             case USER_TYPE_WORK_PROFILE:
249                 message = mContext.getString(R.string.kg_failed_attempts_now_erasing_profile,
250                         attempts);
251                 break;
252         }
253         showDialog(null, message);
254     }
255 
reportFailedUnlockAttempt(int userId, int timeoutMs)256     private void reportFailedUnlockAttempt(int userId, int timeoutMs) {
257         final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
258         final int failedAttempts = monitor.getFailedUnlockAttempts(userId) + 1; // +1 for this time
259 
260         if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
261 
262         final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
263         final int failedAttemptsBeforeWipe =
264                 dpm.getMaximumFailedPasswordsForWipe(null, userId);
265 
266         final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
267                 (failedAttemptsBeforeWipe - failedAttempts)
268                 : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
269         if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
270             // The user has installed a DevicePolicyManager that requests a user/profile to be wiped
271             // N attempts. Once we get below the grace period, we post this dialog every time as a
272             // clear warning until the deletion fires.
273             // Check which profile has the strictest policy for failed password attempts
274             final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId);
275             int userType = USER_TYPE_PRIMARY;
276             if (expiringUser == userId) {
277                 // TODO: http://b/23522538
278                 if (expiringUser != UserHandle.USER_SYSTEM) {
279                     userType = USER_TYPE_SECONDARY_USER;
280                 }
281             } else if (expiringUser != UserHandle.USER_NULL) {
282                 userType = USER_TYPE_WORK_PROFILE;
283             } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY
284             if (remainingBeforeWipe > 0) {
285                 showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType);
286             } else {
287                 // Too many attempts. The device will be wiped shortly.
288                 Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!");
289                 showWipeDialog(failedAttempts, userType);
290             }
291         }
292         monitor.reportFailedStrongAuthUnlockAttempt(userId);
293         mLockPatternUtils.reportFailedPasswordAttempt(userId);
294         if (timeoutMs > 0) {
295             mLockPatternUtils.reportPasswordLockout(timeoutMs, userId);
296             showTimeoutDialog(userId, timeoutMs);
297         }
298     }
299 
300     /**
301      * Shows the primary security screen for the user. This will be either the multi-selector
302      * or the user's security method.
303      * @param turningOff true if the device is being turned off
304      */
showPrimarySecurityScreen(boolean turningOff)305     void showPrimarySecurityScreen(boolean turningOff) {
306         SecurityMode securityMode = mSecurityModel.getSecurityMode(
307                 KeyguardUpdateMonitor.getCurrentUser());
308         if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
309         showSecurityScreen(securityMode);
310     }
311 
312     /**
313      * Shows the next security screen if there is one.
314      * @param authenticated true if the user entered the correct authentication
315      * @param targetUserId a user that needs to be the foreground user at the finish (if called)
316      *     completion.
317      * @return true if keyguard is done
318      */
showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId)319     boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId) {
320         if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
321         boolean finish = false;
322         boolean strongAuth = false;
323         if (mUpdateMonitor.getUserCanSkipBouncer(targetUserId)) {
324             finish = true;
325         } else if (SecurityMode.None == mCurrentSecuritySelection) {
326             SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
327             if (SecurityMode.None == securityMode) {
328                 finish = true; // no security required
329             } else {
330                 showSecurityScreen(securityMode); // switch to the alternate security view
331             }
332         } else if (authenticated) {
333             switch (mCurrentSecuritySelection) {
334                 case Pattern:
335                 case Password:
336                 case PIN:
337                     strongAuth = true;
338                     finish = true;
339                     break;
340 
341                 case SimPin:
342                 case SimPuk:
343                     // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
344                     SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
345                     if (securityMode != SecurityMode.None
346                             || !mLockPatternUtils.isLockScreenDisabled(
347                             KeyguardUpdateMonitor.getCurrentUser())) {
348                         showSecurityScreen(securityMode);
349                     } else {
350                         finish = true;
351                     }
352                     break;
353 
354                 default:
355                     Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
356                     showPrimarySecurityScreen(false);
357                     break;
358             }
359         }
360         if (finish) {
361             mSecurityCallback.finish(strongAuth, targetUserId);
362         }
363         return finish;
364     }
365 
366     /**
367      * Switches to the given security view unless it's already being shown, in which case
368      * this is a no-op.
369      *
370      * @param securityMode
371      */
showSecurityScreen(SecurityMode securityMode)372     private void showSecurityScreen(SecurityMode securityMode) {
373         if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
374 
375         if (securityMode == mCurrentSecuritySelection) return;
376 
377         KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
378         KeyguardSecurityView newView = getSecurityView(securityMode);
379 
380         // Emulate Activity life cycle
381         if (oldView != null) {
382             oldView.onPause();
383             oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
384         }
385         if (securityMode != SecurityMode.None) {
386             newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
387             newView.setKeyguardCallback(mCallback);
388         }
389 
390         // Find and show this child.
391         final int childCount = mSecurityViewFlipper.getChildCount();
392 
393         final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
394         for (int i = 0; i < childCount; i++) {
395             if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) {
396                 mSecurityViewFlipper.setDisplayedChild(i);
397                 break;
398             }
399         }
400 
401         mCurrentSecuritySelection = securityMode;
402         mSecurityCallback.onSecurityModeChanged(securityMode,
403                 securityMode != SecurityMode.None && newView.needsInput());
404     }
405 
getFlipper()406     private KeyguardSecurityViewFlipper getFlipper() {
407         for (int i = 0; i < getChildCount(); i++) {
408             View child = getChildAt(i);
409             if (child instanceof KeyguardSecurityViewFlipper) {
410                 return (KeyguardSecurityViewFlipper) child;
411             }
412         }
413         return null;
414     }
415 
416     private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
417         public void userActivity() {
418             if (mSecurityCallback != null) {
419                 mSecurityCallback.userActivity();
420             }
421         }
422 
423         public void dismiss(boolean authenticated, int targetId) {
424             mSecurityCallback.dismiss(authenticated, targetId);
425         }
426 
427         public boolean isVerifyUnlockOnly() {
428             return mIsVerifyUnlockOnly;
429         }
430 
431         public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
432             KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
433             if (success) {
434                 StatsLog.write(StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
435                     StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__SUCCESS);
436                 monitor.clearFailedUnlockAttempts();
437                 mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
438             } else {
439                 StatsLog.write(StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED,
440                     StatsLog.KEYGUARD_BOUNCER_PASSWORD_ENTERED__RESULT__FAILURE);
441                 KeyguardSecurityContainer.this.reportFailedUnlockAttempt(userId, timeoutMs);
442             }
443         }
444 
445         public void reset() {
446             mSecurityCallback.reset();
447         }
448     };
449 
450     // The following is used to ignore callbacks from SecurityViews that are no longer current
451     // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
452     // state for the current security method.
453     private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
454         @Override
455         public void userActivity() { }
456         @Override
457         public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { }
458         @Override
459         public boolean isVerifyUnlockOnly() { return false; }
460         @Override
461         public void dismiss(boolean securityVerified, int targetUserId) { }
462         @Override
463         public void reset() {}
464     };
465 
getSecurityViewIdForMode(SecurityMode securityMode)466     private int getSecurityViewIdForMode(SecurityMode securityMode) {
467         switch (securityMode) {
468             case Pattern: return R.id.keyguard_pattern_view;
469             case PIN: return R.id.keyguard_pin_view;
470             case Password: return R.id.keyguard_password_view;
471             case SimPin: return R.id.keyguard_sim_pin_view;
472             case SimPuk: return R.id.keyguard_sim_puk_view;
473         }
474         return 0;
475     }
476 
477     @VisibleForTesting
getLayoutIdFor(SecurityMode securityMode)478     public int getLayoutIdFor(SecurityMode securityMode) {
479         switch (securityMode) {
480             case Pattern: return R.layout.keyguard_pattern_view;
481             case PIN: return R.layout.keyguard_pin_view;
482             case Password: return R.layout.keyguard_password_view;
483             case SimPin: return R.layout.keyguard_sim_pin_view;
484             case SimPuk: return R.layout.keyguard_sim_puk_view;
485             default:
486                 return 0;
487         }
488     }
489 
getSecurityMode()490     public SecurityMode getSecurityMode() {
491         return mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser());
492     }
493 
getCurrentSecurityMode()494     public SecurityMode getCurrentSecurityMode() {
495         return mCurrentSecuritySelection;
496     }
497 
verifyUnlock()498     public void verifyUnlock() {
499         mIsVerifyUnlockOnly = true;
500         showSecurityScreen(getSecurityMode());
501     }
502 
getCurrentSecuritySelection()503     public SecurityMode getCurrentSecuritySelection() {
504         return mCurrentSecuritySelection;
505     }
506 
dismiss(boolean authenticated, int targetUserId)507     public void dismiss(boolean authenticated, int targetUserId) {
508         mCallback.dismiss(authenticated, targetUserId);
509     }
510 
needsInput()511     public boolean needsInput() {
512         return mSecurityViewFlipper.needsInput();
513     }
514 
515     @Override
setKeyguardCallback(KeyguardSecurityCallback callback)516     public void setKeyguardCallback(KeyguardSecurityCallback callback) {
517         mSecurityViewFlipper.setKeyguardCallback(callback);
518     }
519 
520     @Override
reset()521     public void reset() {
522         mSecurityViewFlipper.reset();
523     }
524 
525     @Override
getCallback()526     public KeyguardSecurityCallback getCallback() {
527         return mSecurityViewFlipper.getCallback();
528     }
529 
530     @Override
showPromptReason(int reason)531     public void showPromptReason(int reason) {
532         if (mCurrentSecuritySelection != SecurityMode.None) {
533             if (reason != PROMPT_REASON_NONE) {
534                 Log.i(TAG, "Strong auth required, reason: " + reason);
535             }
536             getSecurityView(mCurrentSecuritySelection).showPromptReason(reason);
537         }
538     }
539 
showMessage(CharSequence message, int color)540     public void showMessage(CharSequence message, int color) {
541         if (mCurrentSecuritySelection != SecurityMode.None) {
542             getSecurityView(mCurrentSecuritySelection).showMessage(message, color);
543         }
544     }
545 
546     @Override
showUsabilityHint()547     public void showUsabilityHint() {
548         mSecurityViewFlipper.showUsabilityHint();
549     }
550 
551 }
552 
553