2 * Copyright (C) 2014 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 package com.android.keyguard;
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;
31 import com.android.internal.widget.LockPatternUtils;
32 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
34 public class KeyguardSecurityContainer extends FrameLayout implements KeyguardSecurityView {
35 private static final boolean DEBUG = KeyguardConstants.DEBUG;
36 private static final String TAG = "KeyguardSecurityView";
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;
42 private KeyguardSecurityModel mSecurityModel;
43 private LockPatternUtils mLockPatternUtils;
45 private KeyguardSecurityViewFlipper mSecurityViewFlipper;
46 private boolean mIsVerifyUnlockOnly;
47 private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
48 private SecurityCallback mSecurityCallback;
50 private final KeyguardUpdateMonitor mUpdateMonitor;
52 // Used to notify the container when something interesting happens.
53 public interface SecurityCallback {
54 public boolean dismiss(boolean authenticated, int targetUserId);
55 public void userActivity();
56 public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput);
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 * @param targetUserId a user that needs to be the foreground user at the finish completion.
63 public void finish(boolean strongAuth, int targetUserId);
67 public KeyguardSecurityContainer(Context context, AttributeSet attrs) {
68 this(context, attrs, 0);
71 public KeyguardSecurityContainer(Context context) {
72 this(context, null, 0);
75 public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) {
76 super(context, attrs, defStyle);
77 mSecurityModel = new KeyguardSecurityModel(context);
78 mLockPatternUtils = new LockPatternUtils(context);
79 mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
82 public void setSecurityCallback(SecurityCallback callback) {
83 mSecurityCallback = callback;
87 public void onResume(int reason) {
88 if (mCurrentSecuritySelection != SecurityMode.None) {
89 getSecurityView(mCurrentSecuritySelection).onResume(reason);
94 public void onPause() {
95 if (mCurrentSecuritySelection != SecurityMode.None) {
96 getSecurityView(mCurrentSecuritySelection).onPause();
100 public void startAppearAnimation() {
101 if (mCurrentSecuritySelection != SecurityMode.None) {
102 getSecurityView(mCurrentSecuritySelection).startAppearAnimation();
106 public boolean startDisappearAnimation(Runnable onFinishRunnable) {
107 if (mCurrentSecuritySelection != SecurityMode.None) {
108 return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation(
114 public void announceCurrentSecurityMethod() {
115 View v = (View) getSecurityView(mCurrentSecuritySelection);
117 v.announceForAccessibility(v.getContentDescription());
121 public CharSequence getCurrentSecurityModeContentDescription() {
122 View v = (View) getSecurityView(mCurrentSecuritySelection);
124 return v.getContentDescription();
129 private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
130 final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
131 KeyguardSecurityView view = null;
132 final int children = mSecurityViewFlipper.getChildCount();
133 for (int child = 0; child < children; child++) {
134 if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) {
135 view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child));
139 int layoutId = getLayoutIdFor(securityMode);
140 if (view == null && layoutId != 0) {
141 final LayoutInflater inflater = LayoutInflater.from(mContext);
142 if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
143 View v = inflater.inflate(layoutId, mSecurityViewFlipper, false);
144 mSecurityViewFlipper.addView(v);
145 updateSecurityView(v);
146 view = (KeyguardSecurityView)v;
152 private void updateSecurityView(View view) {
153 if (view instanceof KeyguardSecurityView) {
154 KeyguardSecurityView ksv = (KeyguardSecurityView) view;
155 ksv.setKeyguardCallback(mCallback);
156 ksv.setLockPatternUtils(mLockPatternUtils);
158 Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
162 protected void onFinishInflate() {
163 mSecurityViewFlipper = findViewById(R.id.view_flipper);
164 mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
167 public void setLockPatternUtils(LockPatternUtils utils) {
168 mLockPatternUtils = utils;
169 mSecurityModel.setLockPatternUtils(utils);
170 mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils);
173 private void showDialog(String title, String message) {
174 final AlertDialog dialog = new AlertDialog.Builder(mContext)
177 .setCancelable(false)
178 .setNeutralButton(R.string.ok, null)
180 if (!(mContext instanceof Activity)) {
181 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
186 private void showTimeoutDialog(int userId, int timeoutMs) {
187 int timeoutInSeconds = (int) timeoutMs / 1000;
190 switch (mSecurityModel.getSecurityMode(userId)) {
192 messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
195 messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message;
198 messageId = R.string.kg_too_many_failed_password_attempts_dialog_message;
200 // These don't have timeout dialogs.
208 if (messageId != 0) {
209 final String message = mContext.getString(messageId,
210 KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(userId),
212 showDialog(null, message);
216 private void showAlmostAtWipeDialog(int attempts, int remaining, int userType) {
217 String message = null;
219 case USER_TYPE_PRIMARY:
220 message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe,
221 attempts, remaining);
223 case USER_TYPE_SECONDARY_USER:
224 message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_user,
225 attempts, remaining);
227 case USER_TYPE_WORK_PROFILE:
228 message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_profile,
229 attempts, remaining);
232 showDialog(null, message);
235 private void showWipeDialog(int attempts, int userType) {
236 String message = null;
238 case USER_TYPE_PRIMARY:
239 message = mContext.getString(R.string.kg_failed_attempts_now_wiping,
242 case USER_TYPE_SECONDARY_USER:
243 message = mContext.getString(R.string.kg_failed_attempts_now_erasing_user,
246 case USER_TYPE_WORK_PROFILE:
247 message = mContext.getString(R.string.kg_failed_attempts_now_erasing_profile,
251 showDialog(null, message);
254 private void reportFailedUnlockAttempt(int userId, int timeoutMs) {
255 final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
256 final int failedAttempts = monitor.getFailedUnlockAttempts(userId) + 1; // +1 for this time
258 if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
260 final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager();
261 final int failedAttemptsBeforeWipe =
262 dpm.getMaximumFailedPasswordsForWipe(null, userId);
264 final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
265 (failedAttemptsBeforeWipe - failedAttempts)
266 : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
267 if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
268 // The user has installed a DevicePolicyManager that requests a user/profile to be wiped
269 // N attempts. Once we get below the grace period, we post this dialog every time as a
270 // clear warning until the deletion fires.
271 // Check which profile has the strictest policy for failed password attempts
272 final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(userId);
273 int userType = USER_TYPE_PRIMARY;
274 if (expiringUser == userId) {
275 // TODO: http://b/23522538
276 if (expiringUser != UserHandle.USER_SYSTEM) {
277 userType = USER_TYPE_SECONDARY_USER;
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);
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);
290 monitor.reportFailedStrongAuthUnlockAttempt(userId);
291 mLockPatternUtils.reportFailedPasswordAttempt(userId);
293 mLockPatternUtils.reportPasswordLockout(timeoutMs, userId);
294 showTimeoutDialog(userId, timeoutMs);
299 * Shows the primary security screen for the user. This will be either the multi-selector
300 * or the user's security method.
301 * @param turningOff true if the device is being turned off
303 void showPrimarySecurityScreen(boolean turningOff) {
304 SecurityMode securityMode = mSecurityModel.getSecurityMode(
305 KeyguardUpdateMonitor.getCurrentUser());
306 if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
307 showSecurityScreen(securityMode);
311 * Shows the next security screen if there is one.
312 * @param authenticated true if the user entered the correct authentication
313 * @param targetUserId a user that needs to be the foreground user at the finish (if called)
315 * @return true if keyguard is done
317 boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId) {
318 if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
319 boolean finish = false;
320 boolean strongAuth = false;
321 if (mUpdateMonitor.getUserCanSkipBouncer(targetUserId)) {
323 } else if (SecurityMode.None == mCurrentSecuritySelection) {
324 SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
325 if (SecurityMode.None == securityMode) {
326 finish = true; // no security required
328 showSecurityScreen(securityMode); // switch to the alternate security view
330 } else if (authenticated) {
331 switch (mCurrentSecuritySelection) {
341 // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
342 SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
343 if (securityMode != SecurityMode.None
344 || !mLockPatternUtils.isLockScreenDisabled(
345 KeyguardUpdateMonitor.getCurrentUser())) {
346 showSecurityScreen(securityMode);
353 Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
354 showPrimarySecurityScreen(false);
359 mSecurityCallback.finish(strongAuth, targetUserId);
365 * Switches to the given security view unless it's already being shown, in which case
368 * @param securityMode
370 private void showSecurityScreen(SecurityMode securityMode) {
371 if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
373 if (securityMode == mCurrentSecuritySelection) return;
375 KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
376 KeyguardSecurityView newView = getSecurityView(securityMode);
378 // Emulate Activity life cycle
379 if (oldView != null) {
381 oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
383 if (securityMode != SecurityMode.None) {
384 newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
385 newView.setKeyguardCallback(mCallback);
388 // Find and show this child.
389 final int childCount = mSecurityViewFlipper.getChildCount();
391 final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
392 for (int i = 0; i < childCount; i++) {
393 if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) {
394 mSecurityViewFlipper.setDisplayedChild(i);
399 mCurrentSecuritySelection = securityMode;
400 mSecurityCallback.onSecurityModeChanged(securityMode,
401 securityMode != SecurityMode.None && newView.needsInput());
404 private KeyguardSecurityViewFlipper getFlipper() {
405 for (int i = 0; i < getChildCount(); i++) {
406 View child = getChildAt(i);
407 if (child instanceof KeyguardSecurityViewFlipper) {
408 return (KeyguardSecurityViewFlipper) child;
414 private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
415 public void userActivity() {
416 if (mSecurityCallback != null) {
417 mSecurityCallback.userActivity();
421 public void dismiss(boolean authenticated, int targetId) {
422 mSecurityCallback.dismiss(authenticated, targetId);
425 public boolean isVerifyUnlockOnly() {
426 return mIsVerifyUnlockOnly;
429 public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) {
430 KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
432 monitor.clearFailedUnlockAttempts();
433 mLockPatternUtils.reportSuccessfulPasswordAttempt(userId);
435 KeyguardSecurityContainer.this.reportFailedUnlockAttempt(userId, timeoutMs);
439 public void reset() {
440 mSecurityCallback.reset();
444 // The following is used to ignore callbacks from SecurityViews that are no longer current
445 // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the
446 // state for the current security method.
447 private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
449 public void userActivity() { }
451 public void reportUnlockAttempt(int userId, boolean success, int timeoutMs) { }
453 public boolean isVerifyUnlockOnly() { return false; }
455 public void dismiss(boolean securityVerified, int targetUserId) { }
457 public void reset() {}
460 private int getSecurityViewIdForMode(SecurityMode securityMode) {
461 switch (securityMode) {
462 case Pattern: return R.id.keyguard_pattern_view;
463 case PIN: return R.id.keyguard_pin_view;
464 case Password: return R.id.keyguard_password_view;
465 case SimPin: return R.id.keyguard_sim_pin_view;
466 case SimPuk: return R.id.keyguard_sim_puk_view;
471 protected int getLayoutIdFor(SecurityMode securityMode) {
472 switch (securityMode) {
473 case Pattern: return R.layout.keyguard_pattern_view;
474 case PIN: return R.layout.keyguard_pin_view;
475 case Password: return R.layout.keyguard_password_view;
476 case SimPin: return R.layout.keyguard_sim_pin_view;
477 case SimPuk: return R.layout.keyguard_sim_puk_view;
483 public SecurityMode getSecurityMode() {
484 return mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser());
487 public SecurityMode getCurrentSecurityMode() {
488 return mCurrentSecuritySelection;
491 public void verifyUnlock() {
492 mIsVerifyUnlockOnly = true;
493 showSecurityScreen(getSecurityMode());
496 public SecurityMode getCurrentSecuritySelection() {
497 return mCurrentSecuritySelection;
500 public void dismiss(boolean authenticated, int targetUserId) {
501 mCallback.dismiss(authenticated, targetUserId);
504 public boolean needsInput() {
505 return mSecurityViewFlipper.needsInput();
509 public void setKeyguardCallback(KeyguardSecurityCallback callback) {
510 mSecurityViewFlipper.setKeyguardCallback(callback);
514 public void reset() {
515 mSecurityViewFlipper.reset();
519 public KeyguardSecurityCallback getCallback() {
520 return mSecurityViewFlipper.getCallback();
524 public void showPromptReason(int reason) {
525 if (mCurrentSecuritySelection != SecurityMode.None) {
526 if (reason != PROMPT_REASON_NONE) {
527 Log.i(TAG, "Strong auth required, reason: " + reason);
529 getSecurityView(mCurrentSecuritySelection).showPromptReason(reason);
534 public void showMessage(String message, int color) {
535 if (mCurrentSecuritySelection != SecurityMode.None) {
536 getSecurityView(mCurrentSecuritySelection).showMessage(message, color);
541 public void showUsabilityHint() {
542 mSecurityViewFlipper.showUsabilityHint();