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