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); finish()57 public void finish(); reset()58 public void reset(); 59 } 60 KeyguardSecurityContainer(Context context, AttributeSet attrs)61 public KeyguardSecurityContainer(Context context, AttributeSet attrs) { 62 this(context, attrs, 0); 63 } 64 KeyguardSecurityContainer(Context context)65 public KeyguardSecurityContainer(Context context) { 66 this(context, null, 0); 67 } 68 KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle)69 public KeyguardSecurityContainer(Context context, AttributeSet attrs, int defStyle) { 70 super(context, attrs, defStyle); 71 mSecurityModel = new KeyguardSecurityModel(context); 72 mLockPatternUtils = new LockPatternUtils(context); 73 mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext); 74 } 75 setSecurityCallback(SecurityCallback callback)76 public void setSecurityCallback(SecurityCallback callback) { 77 mSecurityCallback = callback; 78 } 79 80 @Override onResume(int reason)81 public void onResume(int reason) { 82 if (mCurrentSecuritySelection != SecurityMode.None) { 83 getSecurityView(mCurrentSecuritySelection).onResume(reason); 84 } 85 } 86 87 @Override onPause()88 public void onPause() { 89 if (mCurrentSecuritySelection != SecurityMode.None) { 90 getSecurityView(mCurrentSecuritySelection).onPause(); 91 } 92 } 93 startAppearAnimation()94 public void startAppearAnimation() { 95 if (mCurrentSecuritySelection != SecurityMode.None) { 96 getSecurityView(mCurrentSecuritySelection).startAppearAnimation(); 97 } 98 } 99 startDisappearAnimation(Runnable onFinishRunnable)100 public boolean startDisappearAnimation(Runnable onFinishRunnable) { 101 if (mCurrentSecuritySelection != SecurityMode.None) { 102 return getSecurityView(mCurrentSecuritySelection).startDisappearAnimation( 103 onFinishRunnable); 104 } 105 return false; 106 } 107 announceCurrentSecurityMethod()108 public void announceCurrentSecurityMethod() { 109 View v = (View) getSecurityView(mCurrentSecuritySelection); 110 if (v != null) { 111 v.announceForAccessibility(v.getContentDescription()); 112 } 113 } 114 getCurrentSecurityModeContentDescription()115 public CharSequence getCurrentSecurityModeContentDescription() { 116 View v = (View) getSecurityView(mCurrentSecuritySelection); 117 if (v != null) { 118 return v.getContentDescription(); 119 } 120 return ""; 121 } 122 getSecurityView(SecurityMode securityMode)123 private KeyguardSecurityView getSecurityView(SecurityMode securityMode) { 124 final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); 125 KeyguardSecurityView view = null; 126 final int children = mSecurityViewFlipper.getChildCount(); 127 for (int child = 0; child < children; child++) { 128 if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) { 129 view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child)); 130 break; 131 } 132 } 133 int layoutId = getLayoutIdFor(securityMode); 134 if (view == null && layoutId != 0) { 135 final LayoutInflater inflater = LayoutInflater.from(mContext); 136 if (DEBUG) Log.v(TAG, "inflating id = " + layoutId); 137 View v = inflater.inflate(layoutId, mSecurityViewFlipper, false); 138 mSecurityViewFlipper.addView(v); 139 updateSecurityView(v); 140 view = (KeyguardSecurityView)v; 141 } 142 143 return view; 144 } 145 updateSecurityView(View view)146 private void updateSecurityView(View view) { 147 if (view instanceof KeyguardSecurityView) { 148 KeyguardSecurityView ksv = (KeyguardSecurityView) view; 149 ksv.setKeyguardCallback(mCallback); 150 ksv.setLockPatternUtils(mLockPatternUtils); 151 } else { 152 Log.w(TAG, "View " + view + " is not a KeyguardSecurityView"); 153 } 154 } 155 onFinishInflate()156 protected void onFinishInflate() { 157 mSecurityViewFlipper = (KeyguardSecurityViewFlipper) findViewById(R.id.view_flipper); 158 mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils); 159 } 160 setLockPatternUtils(LockPatternUtils utils)161 public void setLockPatternUtils(LockPatternUtils utils) { 162 mLockPatternUtils = utils; 163 mSecurityModel.setLockPatternUtils(utils); 164 mSecurityViewFlipper.setLockPatternUtils(mLockPatternUtils); 165 } 166 showDialog(String title, String message)167 private void showDialog(String title, String message) { 168 final AlertDialog dialog = new AlertDialog.Builder(mContext) 169 .setTitle(title) 170 .setMessage(message) 171 .setNeutralButton(R.string.ok, null) 172 .create(); 173 if (!(mContext instanceof Activity)) { 174 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 175 } 176 dialog.show(); 177 } 178 showTimeoutDialog(int timeoutMs)179 private void showTimeoutDialog(int timeoutMs) { 180 int timeoutInSeconds = (int) timeoutMs / 1000; 181 int messageId = 0; 182 183 switch (mSecurityModel.getSecurityMode()) { 184 case Pattern: 185 messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message; 186 break; 187 case PIN: 188 messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message; 189 break; 190 case Password: 191 messageId = R.string.kg_too_many_failed_password_attempts_dialog_message; 192 break; 193 // These don't have timeout dialogs. 194 case Invalid: 195 case None: 196 case SimPin: 197 case SimPuk: 198 break; 199 } 200 201 if (messageId != 0) { 202 final String message = mContext.getString(messageId, 203 KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(), 204 timeoutInSeconds); 205 showDialog(null, message); 206 } 207 } 208 showAlmostAtWipeDialog(int attempts, int remaining, int userType)209 private void showAlmostAtWipeDialog(int attempts, int remaining, int userType) { 210 String message = null; 211 switch (userType) { 212 case USER_TYPE_PRIMARY: 213 message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe, 214 attempts, remaining); 215 break; 216 case USER_TYPE_SECONDARY_USER: 217 message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_user, 218 attempts, remaining); 219 break; 220 case USER_TYPE_WORK_PROFILE: 221 message = mContext.getString(R.string.kg_failed_attempts_almost_at_erase_profile, 222 attempts, remaining); 223 break; 224 } 225 showDialog(null, message); 226 } 227 showWipeDialog(int attempts, int userType)228 private void showWipeDialog(int attempts, int userType) { 229 String message = null; 230 switch (userType) { 231 case USER_TYPE_PRIMARY: 232 message = mContext.getString(R.string.kg_failed_attempts_now_wiping, 233 attempts); 234 break; 235 case USER_TYPE_SECONDARY_USER: 236 message = mContext.getString(R.string.kg_failed_attempts_now_erasing_user, 237 attempts); 238 break; 239 case USER_TYPE_WORK_PROFILE: 240 message = mContext.getString(R.string.kg_failed_attempts_now_erasing_profile, 241 attempts); 242 break; 243 } 244 showDialog(null, message); 245 } 246 reportFailedUnlockAttempt(int timeoutMs)247 private void reportFailedUnlockAttempt(int timeoutMs) { 248 final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); 249 final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time 250 251 if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts); 252 253 SecurityMode mode = mSecurityModel.getSecurityMode(); 254 final int currentUser = KeyguardUpdateMonitor.getCurrentUser(); 255 final DevicePolicyManager dpm = mLockPatternUtils.getDevicePolicyManager(); 256 final int failedAttemptsBeforeWipe = 257 dpm.getMaximumFailedPasswordsForWipe(null, currentUser); 258 259 final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ? 260 (failedAttemptsBeforeWipe - failedAttempts) 261 : Integer.MAX_VALUE; // because DPM returns 0 if no restriction 262 boolean showTimeout = false; 263 if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) { 264 // The user has installed a DevicePolicyManager that requests a user/profile to be wiped 265 // N attempts. Once we get below the grace period, we post this dialog every time as a 266 // clear warning until the deletion fires. 267 // Check which profile has the strictest policy for failed password attempts 268 final int expiringUser = dpm.getProfileWithMinimumFailedPasswordsForWipe(currentUser); 269 int userType = USER_TYPE_PRIMARY; 270 if (expiringUser == currentUser) { 271 if (expiringUser != UserHandle.USER_OWNER) { 272 userType = USER_TYPE_SECONDARY_USER; 273 } 274 } else if (expiringUser != UserHandle.USER_NULL) { 275 userType = USER_TYPE_WORK_PROFILE; 276 } // If USER_NULL, which shouldn't happen, leave it as USER_TYPE_PRIMARY 277 if (remainingBeforeWipe > 0) { 278 showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe, userType); 279 } else { 280 // Too many attempts. The device will be wiped shortly. 281 Slog.i(TAG, "Too many unlock attempts; user " + expiringUser + " will be wiped!"); 282 showWipeDialog(failedAttempts, userType); 283 } 284 } 285 monitor.reportFailedUnlockAttempt(); 286 mLockPatternUtils.reportFailedPasswordAttempt(KeyguardUpdateMonitor.getCurrentUser()); 287 if (timeoutMs > 0) { 288 showTimeoutDialog(timeoutMs); 289 } 290 } 291 292 /** 293 * Shows the primary security screen for the user. This will be either the multi-selector 294 * or the user's security method. 295 * @param turningOff true if the device is being turned off 296 */ showPrimarySecurityScreen(boolean turningOff)297 void showPrimarySecurityScreen(boolean turningOff) { 298 SecurityMode securityMode = mSecurityModel.getSecurityMode(); 299 if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")"); 300 showSecurityScreen(securityMode); 301 } 302 303 /** 304 * Shows the next security screen if there is one. 305 * @param authenticated true if the user entered the correct authentication 306 * @return true if keyguard is done 307 */ showNextSecurityScreenOrFinish(boolean authenticated)308 boolean showNextSecurityScreenOrFinish(boolean authenticated) { 309 if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")"); 310 boolean finish = false; 311 if (mUpdateMonitor.getUserCanSkipBouncer( 312 KeyguardUpdateMonitor.getCurrentUser())) { 313 finish = true; 314 } else if (SecurityMode.None == mCurrentSecuritySelection) { 315 SecurityMode securityMode = mSecurityModel.getSecurityMode(); 316 if (SecurityMode.None == securityMode) { 317 finish = true; // no security required 318 } else { 319 showSecurityScreen(securityMode); // switch to the alternate security view 320 } 321 } else if (authenticated) { 322 switch (mCurrentSecuritySelection) { 323 case Pattern: 324 case Password: 325 case PIN: 326 finish = true; 327 break; 328 329 case SimPin: 330 case SimPuk: 331 // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home 332 SecurityMode securityMode = mSecurityModel.getSecurityMode(); 333 if (securityMode != SecurityMode.None 334 || !mLockPatternUtils.isLockScreenDisabled( 335 KeyguardUpdateMonitor.getCurrentUser())) { 336 showSecurityScreen(securityMode); 337 } else { 338 finish = true; 339 } 340 break; 341 342 default: 343 Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe"); 344 showPrimarySecurityScreen(false); 345 break; 346 } 347 } 348 if (finish) { 349 mSecurityCallback.finish(); 350 } 351 return finish; 352 } 353 354 /** 355 * Switches to the given security view unless it's already being shown, in which case 356 * this is a no-op. 357 * 358 * @param securityMode 359 */ showSecurityScreen(SecurityMode securityMode)360 private void showSecurityScreen(SecurityMode securityMode) { 361 if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")"); 362 363 if (securityMode == mCurrentSecuritySelection) return; 364 365 KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection); 366 KeyguardSecurityView newView = getSecurityView(securityMode); 367 368 // Emulate Activity life cycle 369 if (oldView != null) { 370 oldView.onPause(); 371 oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view 372 } 373 if (securityMode != SecurityMode.None) { 374 newView.onResume(KeyguardSecurityView.VIEW_REVEALED); 375 newView.setKeyguardCallback(mCallback); 376 } 377 378 // Find and show this child. 379 final int childCount = mSecurityViewFlipper.getChildCount(); 380 381 final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); 382 for (int i = 0; i < childCount; i++) { 383 if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) { 384 mSecurityViewFlipper.setDisplayedChild(i); 385 break; 386 } 387 } 388 389 mCurrentSecuritySelection = securityMode; 390 mSecurityCallback.onSecurityModeChanged(securityMode, 391 securityMode != SecurityMode.None && newView.needsInput()); 392 } 393 getFlipper()394 private KeyguardSecurityViewFlipper getFlipper() { 395 for (int i = 0; i < getChildCount(); i++) { 396 View child = getChildAt(i); 397 if (child instanceof KeyguardSecurityViewFlipper) { 398 return (KeyguardSecurityViewFlipper) child; 399 } 400 } 401 return null; 402 } 403 404 private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() { 405 public void userActivity() { 406 if (mSecurityCallback != null) { 407 mSecurityCallback.userActivity(); 408 } 409 } 410 411 public void dismiss(boolean authenticated) { 412 mSecurityCallback.dismiss(authenticated); 413 } 414 415 public boolean isVerifyUnlockOnly() { 416 return mIsVerifyUnlockOnly; 417 } 418 419 public void reportUnlockAttempt(boolean success, int timeoutMs) { 420 KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); 421 if (success) { 422 monitor.clearFailedUnlockAttempts(); 423 mLockPatternUtils.reportSuccessfulPasswordAttempt( 424 KeyguardUpdateMonitor.getCurrentUser()); 425 } else { 426 KeyguardSecurityContainer.this.reportFailedUnlockAttempt(timeoutMs); 427 } 428 } 429 430 public void reset() { 431 mSecurityCallback.reset(); 432 } 433 }; 434 435 // The following is used to ignore callbacks from SecurityViews that are no longer current 436 // (e.g. face unlock). This avoids unwanted asynchronous events from messing with the 437 // state for the current security method. 438 private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() { 439 @Override 440 public void userActivity() { } 441 @Override 442 public void reportUnlockAttempt(boolean success, int timeoutMs) { } 443 @Override 444 public boolean isVerifyUnlockOnly() { return false; } 445 @Override 446 public void dismiss(boolean securityVerified) { } 447 @Override 448 public void reset() {} 449 }; 450 getSecurityViewIdForMode(SecurityMode securityMode)451 private int getSecurityViewIdForMode(SecurityMode securityMode) { 452 switch (securityMode) { 453 case Pattern: return R.id.keyguard_pattern_view; 454 case PIN: return R.id.keyguard_pin_view; 455 case Password: return R.id.keyguard_password_view; 456 case SimPin: return R.id.keyguard_sim_pin_view; 457 case SimPuk: return R.id.keyguard_sim_puk_view; 458 } 459 return 0; 460 } 461 getLayoutIdFor(SecurityMode securityMode)462 private int getLayoutIdFor(SecurityMode securityMode) { 463 switch (securityMode) { 464 case Pattern: return R.layout.keyguard_pattern_view; 465 case PIN: return R.layout.keyguard_pin_view; 466 case Password: return R.layout.keyguard_password_view; 467 case SimPin: return R.layout.keyguard_sim_pin_view; 468 case SimPuk: return R.layout.keyguard_sim_puk_view; 469 default: 470 return 0; 471 } 472 } 473 getSecurityMode()474 public SecurityMode getSecurityMode() { 475 return mSecurityModel.getSecurityMode(); 476 } 477 getCurrentSecurityMode()478 public SecurityMode getCurrentSecurityMode() { 479 return mCurrentSecuritySelection; 480 } 481 verifyUnlock()482 public void verifyUnlock() { 483 mIsVerifyUnlockOnly = true; 484 showSecurityScreen(getSecurityMode()); 485 } 486 getCurrentSecuritySelection()487 public SecurityMode getCurrentSecuritySelection() { 488 return mCurrentSecuritySelection; 489 } 490 dismiss(boolean authenticated)491 public void dismiss(boolean authenticated) { 492 mCallback.dismiss(authenticated); 493 } 494 needsInput()495 public boolean needsInput() { 496 return mSecurityViewFlipper.needsInput(); 497 } 498 499 @Override setKeyguardCallback(KeyguardSecurityCallback callback)500 public void setKeyguardCallback(KeyguardSecurityCallback callback) { 501 mSecurityViewFlipper.setKeyguardCallback(callback); 502 } 503 504 @Override reset()505 public void reset() { 506 mSecurityViewFlipper.reset(); 507 } 508 509 @Override getCallback()510 public KeyguardSecurityCallback getCallback() { 511 return mSecurityViewFlipper.getCallback(); 512 } 513 514 @Override showPromptReason(int reason)515 public void showPromptReason(int reason) { 516 if (mCurrentSecuritySelection != SecurityMode.None) { 517 getSecurityView(mCurrentSecuritySelection).showPromptReason(reason); 518 } 519 } 520 521 @Override showUsabilityHint()522 public void showUsabilityHint() { 523 mSecurityViewFlipper.showUsabilityHint(); 524 } 525 526 } 527 528