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 { 59 public boolean dismiss(boolean authenticated, int targetUserId); 60 public void userActivity(); 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 */ 68 public void finish(boolean strongAuth, int targetUserId); 69 public void reset(); 70 } 71 72 public KeyguardSecurityContainer(Context context, AttributeSet attrs) { 73 this(context, attrs, 0); 74 } 75 76 public KeyguardSecurityContainer(Context context) { 77 this(context, null, 0); 78 } 79 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 87 public void setSecurityCallback(SecurityCallback callback) { 88 mSecurityCallback = callback; 89 } 90 91 @Override 92 public void onResume(int reason) { 93 if (mCurrentSecuritySelection != SecurityMode.None) { 94 getSecurityView(mCurrentSecuritySelection).onResume(reason); 95 } 96 } 97 98 @Override 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 109 public void startAppearAnimation() { 110 if (mCurrentSecuritySelection != SecurityMode.None) { 111 getSecurityView(mCurrentSecuritySelection).startAppearAnimation(); 112 } 113 } 114 115 public boolean startDisappearAnimation(Runnable onFinishRunnable) { 116 if (mCurrentSecuritySelection != SecurityMode.None) { 117 return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation( 118 onFinishRunnable); 119 } 120 return false; 121 } 122 123 public CharSequence getTitle() { 124 return mSecurityViewFlipper.getTitle(); 125 } 126 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 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 160 protected void onFinishInflate() { 161 mSecurityViewFlipper = findViewById(R.id.view_flipper); 162 mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils); 163 } 164 165 public void setLockPatternUtils(LockPatternUtils utils) { 166 mLockPatternUtils = utils; 167 mSecurityModel.setLockPatternUtils(utils); 168 mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils); 169 } 170 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 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 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 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 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 */ 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 */ 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 */ 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 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 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 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 490 public SecurityMode getSecurityMode() { 491 return mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()); 492 } 493 494 public SecurityMode getCurrentSecurityMode() { 495 return mCurrentSecuritySelection; 496 } 497 498 public void verifyUnlock() { 499 mIsVerifyUnlockOnly = true; 500 showSecurityScreen(getSecurityMode()); 501 } 502 503 public SecurityMode getCurrentSecuritySelection() { 504 return mCurrentSecuritySelection; 505 } 506 507 public void dismiss(boolean authenticated, int targetUserId) { 508 mCallback.dismiss(authenticated, targetUserId); 509 } 510 511 public boolean needsInput() { 512 return mSecurityViewFlipper.needsInput(); 513 } 514 515 @Override 516 public void setKeyguardCallback(KeyguardSecurityCallback callback) { 517 mSecurityViewFlipper.setKeyguardCallback(callback); 518 } 519 520 @Override 521 public void reset() { 522 mSecurityViewFlipper.reset(); 523 } 524 525 @Override 526 public KeyguardSecurityCallback getCallback() { 527 return mSecurityViewFlipper.getCallback(); 528 } 529 530 @Override 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 540 public void showMessage(CharSequence message, int color) { 541 if (mCurrentSecuritySelection != SecurityMode.None) { 542 getSecurityView(mCurrentSecuritySelection).showMessage(message, color); 543 } 544 } 545 546 @Override 547 public void showUsabilityHint() { 548 mSecurityViewFlipper.showUsabilityHint(); 549 } 550 551 } 552 553