• 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                             KeyguardUpdateMonitor.getCurrentUser()),
210                     timeoutInSeconds);
211             showDialog(null, message);
212         }
213     }
214 
showAlmostAtWipeDialog(int attempts, int remaining, int userType)215     private void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
216         String message = null;
217         switch (userType) {
218             case USER_TYPE_PRIMARY:
219                 message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe,
220                         attempts, remaining);
221                 break;
222             case USER_TYPE_SECONDARY_USER:
223                 message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_user,
224                         attempts, remaining);
225                 break;
226             case USER_TYPE_WORK_PROFILE:
227                 message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_profile,
228                         attempts, remaining);
229                 break;
230         }
231         showDialog(null, message);
232     }
233 
showWipeDialog(int attempts, int userType)234     private void showWipeDialog(int attempts, int userType) {
235         String message = null;
236         switch (userType) {
237             case USER_TYPE_PRIMARY:
238                 message = mContext.getString(R.string.kg_failed_attempts_now_wiping,
239                         attempts);
240                 break;
241             case USER_TYPE_SECONDARY_USER:
242                 message = mContext.getString(R.string.kg_failed_attempts_now_erasing_user,
243                         attempts);
244                 break;
245             case USER_TYPE_WORK_PROFILE:
246                 message = mContext.getString(R.string.kg_failed_attempts_now_erasing_profile,
247                         attempts);
248                 break;
249         }
250         showDialog(null, message);
251     }
252 
reportFailedUnlockAttempt(int userId, int timeoutMs)253     private void reportFailedUnlockAttempt(int userId, int timeoutMs) {
254         final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
255         final int failedAttempts = monitor.getFailedUnlockAttempts(userId) + 1; // +1 for this time
256 
257         if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
258 
259         final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
260         final int failedAttemptsBeforeWipe =
261                 dpm.getMaximumFailedPasswordsForWipe(null, userId);
262 
263         final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
264                 (failedAttemptsBeforeWipe - failedAttempts)
265                 : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
266         if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
267             // The user has installed a DevicePolicyManager that requests a user/profile to be wiped
268             // N attempts. Once we get below the grace period, we post this dialog every time as a
269             // clear warning until the deletion fires.
270             // Check which profile has the strictest policy for failed password attempts
271             final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId);
272             int userType = USER_TYPE_PRIMARY;
273             if (expiringUser == userId) {
274                 // TODO: http://b/23522538
275                 if (expiringUser != UserHandle.USER_SYSTEM) {
276                     userType = USER_TYPE_SECONDARY_USER;
277                 }
278             } else if (expiringUser != UserHandle.USER_NULL) {
279                 userType = USER_TYPE_WORK_PROFILE;
280             } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY
281             if (remainingBeforeWipe > 0) {
282                 showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType);
283             } else {
284                 // Too many attempts. The device will be wiped shortly.
285                 Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!");
286                 showWipeDialog(failedAttempts, userType);
287             }
288         }
289         monitor.reportFailedStrongAuthUnlockAttempt(userId);
290         mLockPatternUtils.reportFailedPasswordAttempt(userId);
291         if (timeoutMs > 0) {
292             showTimeoutDialog(timeoutMs);
293         }
294     }
295 
296     /**
297      * Shows the primary security screen for the user. This will be either the multi-selector
298      * or the user's security method.
299      * @param turningOff true if the device is being turned off
300      */
showPrimarySecurityScreen(boolean turningOff)301     void showPrimarySecurityScreen(boolean turningOff) {
302         SecurityMode securityMode = mSecurityModel.getSecurityMode();
303         if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
304         showSecurityScreen(securityMode);
305     }
306 
307     /**
308      * Shows the next security screen if there is one.
309      * @param authenticated true if the user entered the correct authentication
310      * @return true if keyguard is done
311      */
showNextSecurityScreenOrFinish(boolean authenticated)312     boolean showNextSecurityScreenOrFinish(boolean authenticated) {
313         if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
314         boolean finish = false;
315         boolean strongAuth = false;
316         if (mUpdateMonitor.getUserCanSkipBouncer(
317                 KeyguardUpdateMonitor.getCurrentUser())) {
318             finish = true;
319         } else if (SecurityMode.None == mCurrentSecuritySelection) {
320             SecurityMode securityMode = mSecurityModel.getSecurityMode();
321             if (SecurityMode.None == securityMode) {
322                 finish = true; // no security required
323             } else {
324                 showSecurityScreen(securityMode); // switch to the alternate security view
325             }
326         } else if (authenticated) {
327             switch (mCurrentSecuritySelection) {
328                 case Pattern:
329                 case Password:
330                 case PIN:
331                     strongAuth = true;
332                     finish = true;
333                     break;
334 
335                 case SimPin:
336                 case SimPuk:
337                     // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
338                     SecurityMode securityMode = mSecurityModel.getSecurityMode();
339                     if (securityMode != SecurityMode.None
340                             || !mLockPatternUtils.isLockScreenDisabled(
341                             KeyguardUpdateMonitor.getCurrentUser())) {
342                         showSecurityScreen(securityMode);
343                     } else {
344                         finish = true;
345                     }
346                     break;
347 
348                 default:
349                     Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
350                     showPrimarySecurityScreen(false);
351                     break;
352             }
353         }
354         if (finish) {
355             mSecurityCallback.finish(strongAuth);
356         }
357         return finish;
358     }
359 
360     /**
361      * Switches to the given security view unless it's already being shown, in which case
362      * this is a no-op.
363      *
364      * @param securityMode
365      */
showSecurityScreen(SecurityMode securityMode)366     private void showSecurityScreen(SecurityMode securityMode) {
367         if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
368 
369         if (securityMode == mCurrentSecuritySelection) return;
370 
371         KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
372         KeyguardSecurityView newView = getSecurityView(securityMode);
373 
374         // Emulate Activity life cycle
375         if (oldView != null) {
376             oldView.onPause();
377             oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
378         }
379         if (securityMode != SecurityMode.None) {
380             newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
381             newView.setKeyguardCallback(mCallback);
382         }
383 
384         // Find and show this child.
385         final int childCount = mSecurityViewFlipper.getChildCount();
386 
387         final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
388         for (int i = 0; i < childCount; i++) {
389             if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) {
390                 mSecurityViewFlipper.setDisplayedChild(i);
391                 break;
392             }
393         }
394 
395         mCurrentSecuritySelection = securityMode;
396         mSecurityCallback.onSecurityModeChanged(securityMode,
397                 securityMode != SecurityMode.None && newView.needsInput());
398     }
399 
getFlipper()400     private KeyguardSecurityViewFlipper getFlipper() {
401         for (int i = 0; i < getChildCount(); i++) {
402             View child = getChildAt(i);
403             if (child instanceof KeyguardSecurityViewFlipper) {
404                 return (KeyguardSecurityViewFlipper) child;
405             }
406         }
407         return null;
408     }
409 
410     private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
411         public void userActivity() {
412             if (mSecurityCallback != null) {
413                 mSecurityCallback.userActivity();
414             }
415         }
416 
417         public void dismiss(boolean authenticated) {
418             mSecurityCallback.dismiss(authenticated);
419         }
420 
421         public boolean isVerifyUnlockOnly() {
422             return mIsVerifyUnlockOnly;
423         }
424 
425         public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
426             KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
427             if (success) {
428                 monitor.clearFailedUnlockAttempts();
429                 mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
430             } else {
431                 KeyguardSecurityContainer.this.reportFailedUnlockAttempt(userId, timeoutMs);
432             }
433         }
434 
435         public void reset() {
436             mSecurityCallback.reset();
437         }
438     };
439 
440     // The following is used to ignore callbacks from SecurityViews that are no longer current
441     // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
442     // state for the current security method.
443     private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
444         @Override
445         public void userActivity() { }
446         @Override
447         public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { }
448         @Override
449         public boolean isVerifyUnlockOnly() { return false; }
450         @Override
451         public void dismiss(boolean securityVerified) { }
452         @Override
453         public void reset() {}
454     };
455 
getSecurityViewIdForMode(SecurityMode securityMode)456     private int getSecurityViewIdForMode(SecurityMode securityMode) {
457         switch (securityMode) {
458             case Pattern: return R.id.keyguard_pattern_view;
459             case PIN: return R.id.keyguard_pin_view;
460             case Password: return R.id.keyguard_password_view;
461             case SimPin: return R.id.keyguard_sim_pin_view;
462             case SimPuk: return R.id.keyguard_sim_puk_view;
463         }
464         return 0;
465     }
466 
getLayoutIdFor(SecurityMode securityMode)467     protected int getLayoutIdFor(SecurityMode securityMode) {
468         switch (securityMode) {
469             case Pattern: return R.layout.keyguard_pattern_view;
470             case PIN: return R.layout.keyguard_pin_view;
471             case Password: return R.layout.keyguard_password_view;
472             case SimPin: return R.layout.keyguard_sim_pin_view;
473             case SimPuk: return R.layout.keyguard_sim_puk_view;
474             default:
475                 return 0;
476         }
477     }
478 
getSecurityMode()479     public SecurityMode getSecurityMode() {
480         return mSecurityModel.getSecurityMode();
481     }
482 
getCurrentSecurityMode()483     public SecurityMode getCurrentSecurityMode() {
484         return mCurrentSecuritySelection;
485     }
486 
verifyUnlock()487     public void verifyUnlock() {
488         mIsVerifyUnlockOnly = true;
489         showSecurityScreen(getSecurityMode());
490     }
491 
getCurrentSecuritySelection()492     public SecurityMode getCurrentSecuritySelection() {
493         return mCurrentSecuritySelection;
494     }
495 
dismiss(boolean authenticated)496     public void dismiss(boolean authenticated) {
497         mCallback.dismiss(authenticated);
498     }
499 
needsInput()500     public boolean needsInput() {
501         return mSecurityViewFlipper.needsInput();
502     }
503 
504     @Override
setKeyguardCallback(KeyguardSecurityCallback callback)505     public void setKeyguardCallback(KeyguardSecurityCallback callback) {
506         mSecurityViewFlipper.setKeyguardCallback(callback);
507     }
508 
509     @Override
reset()510     public void reset() {
511         mSecurityViewFlipper.reset();
512     }
513 
514     @Override
getCallback()515     public KeyguardSecurityCallback getCallback() {
516         return mSecurityViewFlipper.getCallback();
517     }
518 
519     @Override
showPromptReason(int reason)520     public void showPromptReason(int reason) {
521         if (mCurrentSecuritySelection != SecurityMode.None) {
522             if (reason != PROMPT_REASON_NONE) {
523                 Log.i(TAG, "Strong auth required, reason: " + reason);
524             }
525             getSecurityView(mCurrentSecuritySelection).showPromptReason(reason);
526         }
527     }
528 
529 
showMessage(String message, int color)530     public void showMessage(String message, int color) {
531         if (mCurrentSecuritySelection != SecurityMode.None) {
532             getSecurityView(mCurrentSecuritySelection).showMessage(message, color);
533         }
534     }
535 
536     @Override
showUsabilityHint()537     public void showUsabilityHint() {
538         mSecurityViewFlipper.showUsabilityHint();
539     }
540 
541 }
542 
543