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