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 { 57 public boolean dismiss(boolean authenticated, int targetUserId); 58 public void userActivity(); 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 */ 66 public void finish(boolean strongAuth, int targetUserId); 67 public void reset(); 68 } 69 70 public KeyguardSecurityContainer(Context context, AttributeSet attrs) { 71 this(context, attrs, 0); 72 } 73 74 public KeyguardSecurityContainer(Context context) { 75 this(context, null, 0); 76 } 77 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 85 public void setSecurityCallback(SecurityCallback callback) { 86 mSecurityCallback = callback; 87 } 88 89 @Override 90 public void onResume(int reason) { 91 if (mCurrentSecuritySelection != SecurityMode.None) { 92 getSecurityView(mCurrentSecuritySelection).onResume(reason); 93 } 94 } 95 96 @Override 97 public void onPause() { 98 if (mCurrentSecuritySelection != SecurityMode.None) { 99 getSecurityView(mCurrentSecuritySelection).onPause(); 100 } 101 } 102 103 public void startAppearAnimation() { 104 if (mCurrentSecuritySelection != SecurityMode.None) { 105 getSecurityView(mCurrentSecuritySelection).startAppearAnimation(); 106 } 107 } 108 109 public boolean startDisappearAnimation(Runnable onFinishRunnable) { 110 if (mCurrentSecuritySelection != SecurityMode.None) { 111 return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation( 112 onFinishRunnable); 113 } 114 return false; 115 } 116 117 public void announceCurrentSecurityMethod() { 118 View v = (View) getSecurityView(mCurrentSecuritySelection); 119 if (v != null) { 120 v.announceForAccessibility(v.getContentDescription()); 121 } 122 } 123 124 public CharSequence getCurrentSecurityModeContentDescription() { 125 View v = (View) getSecurityView(mCurrentSecuritySelection); 126 if (v != null) { 127 return v.getContentDescription(); 128 } 129 return ""; 130 } 131 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 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 165 protected void onFinishInflate() { 166 mSecurityViewFlipper = findViewById(R.id.view_flipper); 167 mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils); 168 } 169 170 public void setLockPatternUtils(LockPatternUtils utils) { 171 mLockPatternUtils = utils; 172 mSecurityModel.setLockPatternUtils(utils); 173 mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils); 174 } 175 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 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 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 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 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 */ 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 */ 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 */ 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 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 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 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 487 public SecurityMode getSecurityMode() { 488 return mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()); 489 } 490 491 public SecurityMode getCurrentSecurityMode() { 492 return mCurrentSecuritySelection; 493 } 494 495 public void verifyUnlock() { 496 mIsVerifyUnlockOnly = true; 497 showSecurityScreen(getSecurityMode()); 498 } 499 500 public SecurityMode getCurrentSecuritySelection() { 501 return mCurrentSecuritySelection; 502 } 503 504 public void dismiss(boolean authenticated, int targetUserId) { 505 mCallback.dismiss(authenticated, targetUserId); 506 } 507 508 public boolean needsInput() { 509 return mSecurityViewFlipper.needsInput(); 510 } 511 512 @Override 513 public void setKeyguardCallback(KeyguardSecurityCallback callback) { 514 mSecurityViewFlipper.setKeyguardCallback(callback); 515 } 516 517 @Override 518 public void reset() { 519 mSecurityViewFlipper.reset(); 520 } 521 522 @Override 523 public KeyguardSecurityCallback getCallback() { 524 return mSecurityViewFlipper.getCallback(); 525 } 526 527 @Override 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 538 public void showMessage(String message, int color) { 539 if (mCurrentSecuritySelection != SecurityMode.None) { 540 getSecurityView(mCurrentSecuritySelection).showMessage(message, color); 541 } 542 } 543 544 @Override 545 public void showUsabilityHint() { 546 mSecurityViewFlipper.showUsabilityHint(); 547 } 548 549 } 550 551