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