1 /* 2 * Copyright (C) 2007 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 17 package com.android.internal.policy.impl; 18 19 import com.android.internal.R; 20 import com.android.internal.policy.impl.KeyguardUpdateMonitor.InfoCallback; 21 import com.android.internal.policy.impl.KeyguardUpdateMonitor.InfoCallbackImpl; 22 import com.android.internal.policy.impl.LockPatternKeyguardView.UnlockMode; 23 import com.android.internal.telephony.IccCard; 24 import com.android.internal.widget.LockPatternUtils; 25 import com.android.internal.widget.LockScreenWidgetCallback; 26 import com.android.internal.widget.LockScreenWidgetInterface; 27 import com.android.internal.widget.TransportControlView; 28 29 import android.accounts.Account; 30 import android.accounts.AccountManager; 31 import android.accounts.AccountManagerCallback; 32 import android.accounts.AccountManagerFuture; 33 import android.accounts.AuthenticatorException; 34 import android.accounts.OperationCanceledException; 35 import android.app.AlertDialog; 36 import android.app.admin.DevicePolicyManager; 37 import android.content.Context; 38 import android.content.Intent; 39 import android.content.res.Configuration; 40 import android.content.res.Resources; 41 import android.graphics.Bitmap; 42 import android.graphics.Canvas; 43 import android.graphics.Color; 44 import android.graphics.ColorFilter; 45 import android.graphics.PixelFormat; 46 import android.graphics.drawable.ColorDrawable; 47 import android.graphics.drawable.Drawable; 48 import android.os.Bundle; 49 import android.os.Parcelable; 50 import android.os.PowerManager; 51 import android.os.SystemClock; 52 import android.os.SystemProperties; 53 import android.telephony.TelephonyManager; 54 import android.text.TextUtils; 55 import android.util.Log; 56 import android.util.Slog; 57 import android.view.KeyEvent; 58 import android.view.MotionEvent; 59 import android.view.View; 60 import android.view.WindowManager; 61 import android.view.accessibility.AccessibilityManager; 62 63 import java.io.IOException; 64 65 66 /** 67 * The host view for all of the screens of the pattern unlock screen. There are 68 * two {@link Mode}s of operation, lock and unlock. This will show the appropriate 69 * screen, and listen for callbacks via 70 * {@link com.android.internal.policy.impl.KeyguardScreenCallback} 71 * from the current screen. 72 * 73 * This view, in turn, communicates back to 74 * {@link com.android.internal.policy.impl.KeyguardViewManager} 75 * via its {@link com.android.internal.policy.impl.KeyguardViewCallback}, as appropriate. 76 */ 77 public class LockPatternKeyguardView extends KeyguardViewBase { 78 79 private static final int TRANSPORT_USERACTIVITY_TIMEOUT = 10000; 80 81 static final boolean DEBUG_CONFIGURATION = false; 82 83 // time after launching EmergencyDialer before the screen goes blank. 84 private static final int EMERGENCY_CALL_TIMEOUT = 10000; 85 86 // intent action for launching emergency dialer activity. 87 static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL"; 88 89 private static final boolean DEBUG = false; 90 private static final String TAG = "LockPatternKeyguardView"; 91 92 private final KeyguardUpdateMonitor mUpdateMonitor; 93 private final KeyguardWindowController mWindowController; 94 95 private View mLockScreen; 96 private View mUnlockScreen; 97 98 private boolean mScreenOn; 99 private boolean mWindowFocused = false; 100 private boolean mEnableFallback = false; // assume no fallback UI until we know better 101 102 private boolean mShowLockBeforeUnlock = false; 103 104 // Interface to a biometric sensor that can optionally be used to unlock the device 105 private BiometricSensorUnlock mBiometricUnlock; 106 private final Object mBiometricUnlockStartupLock = new Object(); 107 // Long enough to stay visible while dialer comes up 108 // Short enough to not be visible if the user goes back immediately 109 private final int BIOMETRIC_AREA_EMERGENCY_DIALER_TIMEOUT = 1000; 110 111 private boolean mRequiresSim; 112 // True if the biometric unlock should not be displayed. For example, if there is an overlay on 113 // lockscreen or the user is plugging in / unplugging the device. 114 private boolean mSuppressBiometricUnlock; 115 //True if a dialog is currently displaying on top of this window 116 //Unlike other overlays, this does not close with a power button cycle 117 private boolean mHasDialog = false; 118 //True if this device is currently plugged in 119 private boolean mPluggedIn; 120 // True the first time lockscreen is showing after boot 121 private static boolean sIsFirstAppearanceAfterBoot = true; 122 123 // The music control widget 124 private TransportControlView mTransportControlView; 125 126 private Parcelable mSavedState; 127 128 /** 129 * Either a lock screen (an informational keyguard screen), or an unlock 130 * screen (a means for unlocking the device) is shown at any given time. 131 */ 132 enum Mode { 133 LockScreen, 134 UnlockScreen 135 } 136 137 /** 138 * The different types screens available for {@link Mode#UnlockScreen}. 139 * @see com.android.internal.policy.impl.LockPatternKeyguardView#getUnlockMode() 140 */ 141 enum UnlockMode { 142 143 /** 144 * Unlock by drawing a pattern. 145 */ 146 Pattern, 147 148 /** 149 * Unlock by entering a sim pin. 150 */ 151 SimPin, 152 153 /** 154 * Unlock by entering a sim puk. 155 */ 156 SimPuk, 157 158 /** 159 * Unlock by entering an account's login and password. 160 */ 161 Account, 162 163 /** 164 * Unlock by entering a password or PIN 165 */ 166 Password, 167 168 /** 169 * Unknown (uninitialized) value 170 */ 171 Unknown 172 } 173 174 /** 175 * The current mode. 176 */ 177 private Mode mMode = Mode.LockScreen; 178 179 /** 180 * Keeps track of what mode the current unlock screen is (cached from most recent computation in 181 * {@link #getUnlockMode}). 182 */ 183 private UnlockMode mUnlockScreenMode = UnlockMode.Unknown; 184 185 private boolean mForgotPattern; 186 187 /** 188 * If true, it means we are in the process of verifying that the user 189 * can get past the lock screen per {@link #verifyUnlock()} 190 */ 191 private boolean mIsVerifyUnlockOnly = false; 192 193 /** 194 * Used to lookup the state of the lock pattern 195 */ 196 private final LockPatternUtils mLockPatternUtils; 197 198 /** 199 * The current configuration. 200 */ 201 private Configuration mConfiguration; 202 203 private Runnable mRecreateRunnable = new Runnable() { 204 public void run() { 205 Mode mode = mMode; 206 // If we were previously in a locked state but now it's Unknown, it means the phone 207 // was previously locked because of SIM state and has since been resolved. This 208 // bit of code checks this condition and dismisses keyguard. 209 boolean dismissAfterCreation = false; 210 if (mode == Mode.UnlockScreen && getUnlockMode() == UnlockMode.Unknown) { 211 if (DEBUG) Log.v(TAG, "Switch to Mode.LockScreen because SIM unlocked"); 212 mode = Mode.LockScreen; 213 dismissAfterCreation = true; 214 } 215 updateScreen(mode, true); 216 restoreWidgetState(); 217 if (dismissAfterCreation) { 218 mKeyguardScreenCallback.keyguardDone(false); 219 } 220 } 221 }; 222 223 private LockScreenWidgetCallback mWidgetCallback = new LockScreenWidgetCallback() { 224 public void userActivity(View self) { 225 mKeyguardScreenCallback.pokeWakelock(TRANSPORT_USERACTIVITY_TIMEOUT); 226 } 227 228 public void requestShow(View view) { 229 if (DEBUG) Log.v(TAG, "View " + view + " requested show transports"); 230 view.setVisibility(View.VISIBLE); 231 232 // TODO: examine all widgets to derive clock status 233 mUpdateMonitor.reportClockVisible(false); 234 235 // If there's not a bg protection view containing the transport, then show a black 236 // background. Otherwise, allow the normal background to show. 237 if (findViewById(R.id.transport_bg_protect) == null) { 238 // TODO: We should disable the wallpaper instead 239 setBackgroundColor(0xff000000); 240 } else { 241 resetBackground(); 242 } 243 } 244 245 public void requestHide(View view) { 246 if (DEBUG) Log.v(TAG, "View " + view + " requested hide transports"); 247 view.setVisibility(View.GONE); 248 249 // TODO: examine all widgets to derive clock status 250 mUpdateMonitor.reportClockVisible(true); 251 resetBackground(); 252 } 253 254 public boolean isVisible(View self) { 255 // TODO: this should be up to the lockscreen to determine if the view 256 // is currently showing. The idea is it can be used for the widget to 257 // avoid doing work if it's not visible. For now just returns the view's 258 // actual visibility. 259 return self.getVisibility() == View.VISIBLE; 260 } 261 }; 262 263 /** 264 * @return Whether we are stuck on the lock screen because the sim is 265 * missing. 266 */ 267 private boolean stuckOnLockScreenBecauseSimMissing() { 268 return mRequiresSim 269 && (!mUpdateMonitor.isDeviceProvisioned()) 270 && (mUpdateMonitor.getSimState() == IccCard.State.ABSENT || 271 mUpdateMonitor.getSimState() == IccCard.State.PERM_DISABLED); 272 } 273 274 /** 275 * The current {@link KeyguardScreen} will use this to communicate back to us. 276 */ 277 KeyguardScreenCallback mKeyguardScreenCallback = new KeyguardScreenCallback() { 278 279 public void goToLockScreen() { 280 mForgotPattern = false; 281 if (mIsVerifyUnlockOnly) { 282 // navigating away from unlock screen during verify mode means 283 // we are done and the user failed to authenticate. 284 mIsVerifyUnlockOnly = false; 285 getCallback().keyguardDone(false); 286 } else { 287 updateScreen(Mode.LockScreen, false); 288 } 289 } 290 291 public void goToUnlockScreen() { 292 final IccCard.State simState = mUpdateMonitor.getSimState(); 293 if (stuckOnLockScreenBecauseSimMissing() 294 || (simState == IccCard.State.PUK_REQUIRED 295 && !mLockPatternUtils.isPukUnlockScreenEnable())){ 296 // stuck on lock screen when sim missing or 297 // puk'd but puk unlock screen is disabled 298 return; 299 } 300 if (!isSecure()) { 301 getCallback().keyguardDone(true); 302 } else { 303 updateScreen(Mode.UnlockScreen, false); 304 } 305 } 306 307 public void forgotPattern(boolean isForgotten) { 308 if (mEnableFallback) { 309 mForgotPattern = isForgotten; 310 updateScreen(Mode.UnlockScreen, false); 311 } 312 } 313 314 public boolean isSecure() { 315 return LockPatternKeyguardView.this.isSecure(); 316 } 317 318 public boolean isVerifyUnlockOnly() { 319 return mIsVerifyUnlockOnly; 320 } 321 322 public void recreateMe(Configuration config) { 323 if (DEBUG) Log.v(TAG, "recreateMe()"); 324 removeCallbacks(mRecreateRunnable); 325 post(mRecreateRunnable); 326 } 327 328 public void takeEmergencyCallAction() { 329 mSuppressBiometricUnlock = true; 330 331 if (mBiometricUnlock != null) { 332 if (mBiometricUnlock.isRunning()) { 333 // Continue covering backup lock until dialer comes up or call is resumed 334 mBiometricUnlock.show(BIOMETRIC_AREA_EMERGENCY_DIALER_TIMEOUT); 335 } 336 337 // We must ensure the biometric unlock is stopped when emergency call is pressed 338 mBiometricUnlock.stop(); 339 } 340 341 pokeWakelock(EMERGENCY_CALL_TIMEOUT); 342 if (TelephonyManager.getDefault().getCallState() 343 == TelephonyManager.CALL_STATE_OFFHOOK) { 344 mLockPatternUtils.resumeCall(); 345 } else { 346 Intent intent = new Intent(ACTION_EMERGENCY_DIAL); 347 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 348 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 349 getContext().startActivity(intent); 350 } 351 } 352 353 public void pokeWakelock() { 354 getCallback().pokeWakelock(); 355 } 356 357 public void pokeWakelock(int millis) { 358 getCallback().pokeWakelock(millis); 359 } 360 361 public void keyguardDone(boolean authenticated) { 362 getCallback().keyguardDone(authenticated); 363 mSavedState = null; // clear state so we re-establish when locked again 364 } 365 366 public void keyguardDoneDrawing() { 367 // irrelevant to keyguard screen, they shouldn't be calling this 368 } 369 370 public void reportFailedUnlockAttempt() { 371 mUpdateMonitor.reportFailedAttempt(); 372 final int failedAttempts = mUpdateMonitor.getFailedAttempts(); 373 if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts + 374 " (enableFallback=" + mEnableFallback + ")"); 375 376 final boolean usingPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality() 377 == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; 378 379 final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager() 380 .getMaximumFailedPasswordsForWipe(null); 381 382 final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET 383 - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; 384 385 final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ? 386 (failedAttemptsBeforeWipe - failedAttempts) 387 : Integer.MAX_VALUE; // because DPM returns 0 if no restriction 388 389 if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) { 390 // If we reach this code, it means the user has installed a DevicePolicyManager 391 // that requests device wipe after N attempts. Once we get below the grace 392 // period, we'll post this dialog every time as a clear warning until the 393 // bombshell hits and the device is wiped. 394 if (remainingBeforeWipe > 0) { 395 showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe); 396 } else { 397 // Too many attempts. The device will be wiped shortly. 398 Slog.i(TAG, "Too many unlock attempts; device will be wiped!"); 399 showWipeDialog(failedAttempts); 400 } 401 } else { 402 boolean showTimeout = 403 (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0; 404 if (usingPattern && mEnableFallback) { 405 if (failedAttempts == failedAttemptWarning) { 406 showAlmostAtAccountLoginDialog(); 407 showTimeout = false; // don't show both dialogs 408 } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) { 409 mLockPatternUtils.setPermanentlyLocked(true); 410 updateScreen(mMode, false); 411 // don't show timeout dialog because we show account unlock screen next 412 showTimeout = false; 413 } 414 } 415 if (showTimeout) { 416 showTimeoutDialog(); 417 } 418 } 419 mLockPatternUtils.reportFailedPasswordAttempt(); 420 } 421 422 public boolean doesFallbackUnlockScreenExist() { 423 return mEnableFallback; 424 } 425 426 public void reportSuccessfulUnlockAttempt() { 427 mLockPatternUtils.reportSuccessfulPasswordAttempt(); 428 } 429 }; 430 431 /** 432 * @param context Used to inflate, and create views. 433 * @param callback Keyguard callback object for pokewakelock(), etc. 434 * @param updateMonitor Knows the state of the world, and passed along to each 435 * screen so they can use the knowledge, and also register for callbacks 436 * on dynamic information. 437 * @param lockPatternUtils Used to look up state of lock pattern. 438 */ 439 public LockPatternKeyguardView( 440 Context context, KeyguardViewCallback callback, KeyguardUpdateMonitor updateMonitor, 441 LockPatternUtils lockPatternUtils, KeyguardWindowController controller) { 442 super(context, callback); 443 444 mConfiguration = context.getResources().getConfiguration(); 445 mEnableFallback = false; 446 mRequiresSim = TextUtils.isEmpty(SystemProperties.get("keyguard.no_require_sim")); 447 mUpdateMonitor = updateMonitor; 448 mLockPatternUtils = lockPatternUtils; 449 mWindowController = controller; 450 mSuppressBiometricUnlock = sIsFirstAppearanceAfterBoot; 451 sIsFirstAppearanceAfterBoot = false; 452 mPluggedIn = mUpdateMonitor.isDevicePluggedIn(); 453 mScreenOn = ((PowerManager)context.getSystemService(Context.POWER_SERVICE)).isScreenOn(); 454 mUpdateMonitor.registerInfoCallback(mInfoCallback); 455 456 /** 457 * We'll get key events the current screen doesn't use. see 458 * {@link KeyguardViewBase#onKeyDown(int, android.view.KeyEvent)} 459 */ 460 setFocusableInTouchMode(true); 461 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); 462 463 updateScreen(getInitialMode(), false); 464 maybeEnableFallback(context); 465 } 466 467 private class AccountAnalyzer implements AccountManagerCallback<Bundle> { 468 private final AccountManager mAccountManager; 469 private final Account[] mAccounts; 470 private int mAccountIndex; 471 472 private AccountAnalyzer(AccountManager accountManager) { 473 mAccountManager = accountManager; 474 mAccounts = accountManager.getAccountsByType("com.google"); 475 } 476 477 private void next() { 478 // if we are ready to enable the fallback or if we depleted the list of accounts 479 // then finish and get out 480 if (mEnableFallback || mAccountIndex >= mAccounts.length) { 481 if (mUnlockScreen == null) { 482 if (DEBUG) Log.w(TAG, "no unlock screen when trying to enable fallback"); 483 } else if (mUnlockScreen instanceof PatternUnlockScreen) { 484 ((PatternUnlockScreen)mUnlockScreen).setEnableFallback(mEnableFallback); 485 } 486 return; 487 } 488 489 // lookup the confirmCredentials intent for the current account 490 mAccountManager.confirmCredentials(mAccounts[mAccountIndex], null, null, this, null); 491 } 492 493 public void start() { 494 mEnableFallback = false; 495 mAccountIndex = 0; 496 next(); 497 } 498 499 public void run(AccountManagerFuture<Bundle> future) { 500 try { 501 Bundle result = future.getResult(); 502 if (result.getParcelable(AccountManager.KEY_INTENT) != null) { 503 mEnableFallback = true; 504 } 505 } catch (OperationCanceledException e) { 506 // just skip the account if we are unable to query it 507 } catch (IOException e) { 508 // just skip the account if we are unable to query it 509 } catch (AuthenticatorException e) { 510 // just skip the account if we are unable to query it 511 } finally { 512 mAccountIndex++; 513 next(); 514 } 515 } 516 } 517 518 private void maybeEnableFallback(Context context) { 519 // Ask the account manager if we have an account that can be used as a 520 // fallback in case the user forgets his pattern. 521 AccountAnalyzer accountAnalyzer = new AccountAnalyzer(AccountManager.get(context)); 522 accountAnalyzer.start(); 523 } 524 525 526 // TODO: 527 // This overloaded method was added to workaround a race condition in the framework between 528 // notification for orientation changed, layout() and switching resources. This code attempts 529 // to avoid drawing the incorrect layout while things are in transition. The method can just 530 // be removed once the race condition is fixed. See bugs 2262578 and 2292713. 531 @Override 532 protected void dispatchDraw(Canvas canvas) { 533 if (DEBUG) Log.v(TAG, "*** dispatchDraw() time: " + SystemClock.elapsedRealtime()); 534 super.dispatchDraw(canvas); 535 } 536 537 @Override 538 public void reset() { 539 mIsVerifyUnlockOnly = false; 540 mForgotPattern = false; 541 if (DEBUG) Log.v(TAG, "reset()"); 542 post(mRecreateRunnable); 543 } 544 545 @Override 546 public void onScreenTurnedOff() { 547 if (DEBUG) Log.d(TAG, "screen off"); 548 mScreenOn = false; 549 mForgotPattern = false; 550 551 // Emulate activity life-cycle for both lock and unlock screen. 552 if (mLockScreen != null) { 553 ((KeyguardScreen) mLockScreen).onPause(); 554 } 555 if (mUnlockScreen != null) { 556 ((KeyguardScreen) mUnlockScreen).onPause(); 557 } 558 559 saveWidgetState(); 560 561 if (mBiometricUnlock != null) { 562 // The biometric unlock must stop when screen turns off. 563 mBiometricUnlock.stop(); 564 } 565 } 566 567 @Override 568 public void onScreenTurnedOn() { 569 if (DEBUG) Log.d(TAG, "screen on"); 570 boolean startBiometricUnlock = false; 571 // Start the biometric unlock if and only if the screen is both on and focused 572 synchronized(mBiometricUnlockStartupLock) { 573 mScreenOn = true; 574 startBiometricUnlock = mWindowFocused; 575 } 576 577 show(); 578 579 restoreWidgetState(); 580 581 if (mBiometricUnlock != null && startBiometricUnlock) { 582 maybeStartBiometricUnlock(); 583 } 584 } 585 586 private void saveWidgetState() { 587 if (mTransportControlView != null) { 588 if (DEBUG) Log.v(TAG, "Saving widget state"); 589 mSavedState = mTransportControlView.onSaveInstanceState(); 590 } 591 } 592 593 private void restoreWidgetState() { 594 if (mTransportControlView != null) { 595 if (DEBUG) Log.v(TAG, "Restoring widget state"); 596 if (mSavedState != null) { 597 mTransportControlView.onRestoreInstanceState(mSavedState); 598 } 599 } 600 } 601 602 /** 603 * Stop the biometric unlock if something covers this window (such as an alarm) 604 * Start the biometric unlock if the lockscreen window just came into focus and the screen is on 605 */ 606 @Override 607 public void onWindowFocusChanged (boolean hasWindowFocus) { 608 if (DEBUG) Log.d(TAG, hasWindowFocus ? "focused" : "unfocused"); 609 610 boolean startBiometricUnlock = false; 611 // Start the biometric unlock if and only if the screen is both on and focused 612 synchronized(mBiometricUnlockStartupLock) { 613 if (mScreenOn && !mWindowFocused) startBiometricUnlock = hasWindowFocus; 614 mWindowFocused = hasWindowFocus; 615 } 616 if (!hasWindowFocus) { 617 if (mBiometricUnlock != null) { 618 mSuppressBiometricUnlock = true; 619 mBiometricUnlock.stop(); 620 mBiometricUnlock.hide(); 621 } 622 } else { 623 mHasDialog = false; 624 if (mBiometricUnlock != null && startBiometricUnlock) { 625 maybeStartBiometricUnlock(); 626 } 627 } 628 } 629 630 @Override 631 public void show() { 632 // Emulate activity life-cycle for both lock and unlock screen. 633 if (mLockScreen != null) { 634 ((KeyguardScreen) mLockScreen).onResume(); 635 } 636 if (mUnlockScreen != null) { 637 ((KeyguardScreen) mUnlockScreen).onResume(); 638 } 639 640 if (mBiometricUnlock != null && mSuppressBiometricUnlock) { 641 mBiometricUnlock.hide(); 642 } 643 } 644 645 private void recreateLockScreen() { 646 if (mLockScreen != null) { 647 ((KeyguardScreen) mLockScreen).onPause(); 648 ((KeyguardScreen) mLockScreen).cleanUp(); 649 removeView(mLockScreen); 650 } 651 652 mLockScreen = createLockScreen(); 653 mLockScreen.setVisibility(View.INVISIBLE); 654 addView(mLockScreen); 655 } 656 657 private void recreateUnlockScreen(UnlockMode unlockMode) { 658 if (mUnlockScreen != null) { 659 ((KeyguardScreen) mUnlockScreen).onPause(); 660 ((KeyguardScreen) mUnlockScreen).cleanUp(); 661 removeView(mUnlockScreen); 662 } 663 664 mUnlockScreen = createUnlockScreenFor(unlockMode); 665 mUnlockScreen.setVisibility(View.INVISIBLE); 666 addView(mUnlockScreen); 667 } 668 669 @Override 670 protected void onDetachedFromWindow() { 671 mUpdateMonitor.removeCallback(mInfoCallback); 672 673 removeCallbacks(mRecreateRunnable); 674 675 if (mBiometricUnlock != null) { 676 // When view is hidden, we need to stop the biometric unlock 677 // e.g., when device becomes unlocked 678 mBiometricUnlock.stop(); 679 } 680 681 super.onDetachedFromWindow(); 682 } 683 684 protected void onConfigurationChanged(Configuration newConfig) { 685 Resources resources = getResources(); 686 mShowLockBeforeUnlock = resources.getBoolean(R.bool.config_enableLockBeforeUnlockScreen); 687 mConfiguration = newConfig; 688 if (DEBUG_CONFIGURATION) Log.v(TAG, "**** re-creating lock screen since config changed"); 689 saveWidgetState(); 690 removeCallbacks(mRecreateRunnable); 691 if (DEBUG) Log.v(TAG, "recreating lockscreen because config changed"); 692 post(mRecreateRunnable); 693 } 694 695 InfoCallbackImpl mInfoCallback = new InfoCallbackImpl() { 696 697 @Override 698 public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, 699 int batteryLevel) { 700 // When someone plugs in or unplugs the device, we hide the biometric sensor area and 701 // suppress its startup for the next onScreenTurnedOn(). Since plugging/unplugging 702 // causes the screen to turn on, the biometric unlock would start if it wasn't 703 // suppressed. 704 // 705 // However, if the biometric unlock is already running, we do not want to interrupt it. 706 if (mBiometricUnlock != null && mPluggedIn != pluggedIn 707 && !mBiometricUnlock.isRunning()) { 708 mBiometricUnlock.stop(); 709 mBiometricUnlock.hide(); 710 mSuppressBiometricUnlock = true; 711 } 712 mPluggedIn = pluggedIn; 713 } 714 715 @Override 716 public void onClockVisibilityChanged() { 717 int visFlags = (getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_CLOCK) 718 | (mUpdateMonitor.isClockVisible() ? View.STATUS_BAR_DISABLE_CLOCK : 0); 719 Log.v(TAG, "Set visibility on " + this + " to " + visFlags); 720 setSystemUiVisibility(visFlags); 721 } 722 723 // We need to stop the biometric unlock when a phone call comes in 724 @Override 725 public void onPhoneStateChanged(int phoneState) { 726 if (DEBUG) Log.d(TAG, "phone state: " + phoneState); 727 if (mBiometricUnlock != null && phoneState == TelephonyManager.CALL_STATE_RINGING) { 728 mSuppressBiometricUnlock = true; 729 mBiometricUnlock.stop(); 730 mBiometricUnlock.hide(); 731 } 732 } 733 734 @Override 735 public void onUserChanged(int userId) { 736 if (mBiometricUnlock != null) { 737 mBiometricUnlock.stop(); 738 } 739 mLockPatternUtils.setCurrentUser(userId); 740 updateScreen(getInitialMode(), true); 741 } 742 }; 743 744 @Override 745 protected boolean dispatchHoverEvent(MotionEvent event) { 746 // Do not let the screen to get locked while the user is disabled and touch 747 // exploring. A blind user will need significantly more time to find and 748 // interact with the lock screen views. 749 AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext); 750 if (accessibilityManager.isEnabled() && accessibilityManager.isTouchExplorationEnabled()) { 751 getCallback().pokeWakelock(); 752 } 753 return super.dispatchHoverEvent(event); 754 } 755 756 @Override 757 public void wakeWhenReadyTq(int keyCode) { 758 if (DEBUG) Log.d(TAG, "onWakeKey"); 759 if (keyCode == KeyEvent.KEYCODE_MENU && isSecure() && (mMode == Mode.LockScreen) 760 && (mUpdateMonitor.getSimState() != IccCard.State.PUK_REQUIRED)) { 761 if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU"); 762 updateScreen(Mode.UnlockScreen, false); 763 getCallback().pokeWakelock(); 764 } else { 765 if (DEBUG) Log.d(TAG, "poking wake lock immediately"); 766 getCallback().pokeWakelock(); 767 } 768 } 769 770 @Override 771 public void verifyUnlock() { 772 if (!isSecure()) { 773 // non-secure keyguard screens are successfull by default 774 getCallback().keyguardDone(true); 775 } else if (mUnlockScreenMode != UnlockMode.Pattern 776 && mUnlockScreenMode != UnlockMode.Password) { 777 // can only verify unlock when in pattern/password mode 778 getCallback().keyguardDone(false); 779 } else { 780 // otherwise, go to the unlock screen, see if they can verify it 781 mIsVerifyUnlockOnly = true; 782 updateScreen(Mode.UnlockScreen, false); 783 } 784 } 785 786 @Override 787 public void cleanUp() { 788 if (mLockScreen != null) { 789 ((KeyguardScreen) mLockScreen).onPause(); 790 ((KeyguardScreen) mLockScreen).cleanUp(); 791 this.removeView(mLockScreen); 792 mLockScreen = null; 793 } 794 if (mUnlockScreen != null) { 795 ((KeyguardScreen) mUnlockScreen).onPause(); 796 ((KeyguardScreen) mUnlockScreen).cleanUp(); 797 this.removeView(mUnlockScreen); 798 mUnlockScreen = null; 799 } 800 mUpdateMonitor.removeCallback(this); 801 if (mBiometricUnlock != null) { 802 mBiometricUnlock.cleanUp(); 803 } 804 } 805 806 private boolean isSecure() { 807 UnlockMode unlockMode = getUnlockMode(); 808 boolean secure = false; 809 switch (unlockMode) { 810 case Pattern: 811 secure = mLockPatternUtils.isLockPatternEnabled(); 812 break; 813 case SimPin: 814 secure = mUpdateMonitor.getSimState() == IccCard.State.PIN_REQUIRED; 815 break; 816 case SimPuk: 817 secure = mUpdateMonitor.getSimState() == IccCard.State.PUK_REQUIRED; 818 break; 819 case Account: 820 secure = true; 821 break; 822 case Password: 823 secure = mLockPatternUtils.isLockPasswordEnabled(); 824 break; 825 case Unknown: 826 // This means no security is set up 827 break; 828 default: 829 throw new IllegalStateException("unknown unlock mode " + unlockMode); 830 } 831 return secure; 832 } 833 834 private void updateScreen(Mode mode, boolean force) { 835 836 if (DEBUG_CONFIGURATION) Log.v(TAG, "**** UPDATE SCREEN: mode=" + mode 837 + " last mode=" + mMode + ", force = " + force, new RuntimeException()); 838 839 mMode = mode; 840 841 // Re-create the lock screen if necessary 842 if (mode == Mode.LockScreen || mShowLockBeforeUnlock) { 843 if (force || mLockScreen == null) { 844 recreateLockScreen(); 845 } 846 } 847 848 // Re-create the unlock screen if necessary. 849 final UnlockMode unlockMode = getUnlockMode(); 850 if (mode == Mode.UnlockScreen && unlockMode != UnlockMode.Unknown) { 851 if (force || mUnlockScreen == null || unlockMode != mUnlockScreenMode) { 852 recreateUnlockScreen(unlockMode); 853 } 854 } 855 856 // visibleScreen should never be null 857 final View goneScreen = (mode == Mode.LockScreen) ? mUnlockScreen : mLockScreen; 858 final View visibleScreen = (mode == Mode.LockScreen) ? mLockScreen : mUnlockScreen; 859 860 // do this before changing visibility so focus isn't requested before the input 861 // flag is set 862 mWindowController.setNeedsInput(((KeyguardScreen)visibleScreen).needsInput()); 863 864 if (DEBUG_CONFIGURATION) { 865 Log.v(TAG, "Gone=" + goneScreen); 866 Log.v(TAG, "Visible=" + visibleScreen); 867 } 868 869 if (mScreenOn) { 870 if (goneScreen != null && goneScreen.getVisibility() == View.VISIBLE) { 871 ((KeyguardScreen) goneScreen).onPause(); 872 } 873 if (visibleScreen.getVisibility() != View.VISIBLE) { 874 ((KeyguardScreen) visibleScreen).onResume(); 875 } 876 } 877 878 if (goneScreen != null) { 879 goneScreen.setVisibility(View.GONE); 880 } 881 visibleScreen.setVisibility(View.VISIBLE); 882 requestLayout(); 883 884 if (!visibleScreen.requestFocus()) { 885 throw new IllegalStateException("keyguard screen must be able to take " 886 + "focus when shown " + visibleScreen.getClass().getCanonicalName()); 887 } 888 } 889 890 View createLockScreen() { 891 View lockView = new LockScreen( 892 mContext, 893 mConfiguration, 894 mLockPatternUtils, 895 mUpdateMonitor, 896 mKeyguardScreenCallback); 897 initializeTransportControlView(lockView); 898 return lockView; 899 } 900 901 View createUnlockScreenFor(UnlockMode unlockMode) { 902 View unlockView = null; 903 904 if (DEBUG) Log.d(TAG, 905 "createUnlockScreenFor(" + unlockMode + "): mEnableFallback=" + mEnableFallback); 906 907 if (unlockMode == UnlockMode.Pattern) { 908 PatternUnlockScreen view = new PatternUnlockScreen( 909 mContext, 910 mConfiguration, 911 mLockPatternUtils, 912 mUpdateMonitor, 913 mKeyguardScreenCallback, 914 mUpdateMonitor.getFailedAttempts()); 915 view.setEnableFallback(mEnableFallback); 916 unlockView = view; 917 } else if (unlockMode == UnlockMode.SimPuk) { 918 unlockView = new SimPukUnlockScreen( 919 mContext, 920 mConfiguration, 921 mUpdateMonitor, 922 mKeyguardScreenCallback, 923 mLockPatternUtils); 924 } else if (unlockMode == UnlockMode.SimPin) { 925 unlockView = new SimUnlockScreen( 926 mContext, 927 mConfiguration, 928 mUpdateMonitor, 929 mKeyguardScreenCallback, 930 mLockPatternUtils); 931 } else if (unlockMode == UnlockMode.Account) { 932 try { 933 unlockView = new AccountUnlockScreen( 934 mContext, 935 mConfiguration, 936 mUpdateMonitor, 937 mKeyguardScreenCallback, 938 mLockPatternUtils); 939 } catch (IllegalStateException e) { 940 Log.i(TAG, "Couldn't instantiate AccountUnlockScreen" 941 + " (IAccountsService isn't available)"); 942 // TODO: Need a more general way to provide a 943 // platform-specific fallback UI here. 944 // For now, if we can't display the account login 945 // unlock UI, just bring back the regular "Pattern" unlock mode. 946 947 // (We do this by simply returning a regular UnlockScreen 948 // here. This means that the user will still see the 949 // regular pattern unlock UI, regardless of the value of 950 // mUnlockScreenMode or whether or not we're in the 951 // "permanently locked" state.) 952 return createUnlockScreenFor(UnlockMode.Pattern); 953 } 954 } else if (unlockMode == UnlockMode.Password) { 955 unlockView = new PasswordUnlockScreen( 956 mContext, 957 mConfiguration, 958 mLockPatternUtils, 959 mUpdateMonitor, 960 mKeyguardScreenCallback); 961 } else { 962 throw new IllegalArgumentException("unknown unlock mode " + unlockMode); 963 } 964 initializeTransportControlView(unlockView); 965 initializeBiometricUnlockView(unlockView); 966 967 mUnlockScreenMode = unlockMode; 968 return unlockView; 969 } 970 971 private void initializeTransportControlView(View view) { 972 mTransportControlView = (TransportControlView) view.findViewById(R.id.transport); 973 if (mTransportControlView == null) { 974 if (DEBUG) Log.w(TAG, "Couldn't find transport control widget"); 975 } else { 976 mUpdateMonitor.reportClockVisible(true); 977 mTransportControlView.setVisibility(View.GONE); // hide until it requests being shown. 978 mTransportControlView.setCallback(mWidgetCallback); 979 } 980 } 981 982 /** 983 * This returns false if there is any condition that indicates that the biometric unlock should 984 * not be used before the next time the unlock screen is recreated. In other words, if this 985 * returns false there is no need to even construct the biometric unlock. 986 */ 987 private boolean useBiometricUnlock() { 988 final UnlockMode unlockMode = getUnlockMode(); 989 final boolean backupIsTimedOut = (mUpdateMonitor.getFailedAttempts() >= 990 LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT); 991 return (mLockPatternUtils.usingBiometricWeak() && 992 mLockPatternUtils.isBiometricWeakInstalled() && 993 !mUpdateMonitor.getMaxBiometricUnlockAttemptsReached() && 994 !backupIsTimedOut && 995 (unlockMode == UnlockMode.Pattern || unlockMode == UnlockMode.Password)); 996 } 997 998 private void initializeBiometricUnlockView(View view) { 999 boolean restartBiometricUnlock = false; 1000 1001 if (mBiometricUnlock != null) { 1002 restartBiometricUnlock = mBiometricUnlock.stop(); 1003 } 1004 1005 // Prevents biometric unlock from coming up immediately after a phone call or if there 1006 // is a dialog on top of lockscreen. It is only updated if the screen is off because if the 1007 // screen is on it's either because of an orientation change, or when it first boots. 1008 // In both those cases, we don't want to override the current value of 1009 // mSuppressBiometricUnlock and instead want to use the previous value. 1010 if (!mScreenOn) { 1011 mSuppressBiometricUnlock = 1012 mUpdateMonitor.getPhoneState() != TelephonyManager.CALL_STATE_IDLE 1013 || mHasDialog; 1014 } 1015 1016 // If the biometric unlock is not being used, we don't bother constructing it. Then we can 1017 // simply check if it is null when deciding whether we should make calls to it. 1018 mBiometricUnlock = null; 1019 if (useBiometricUnlock()) { 1020 // TODO: make faceLockAreaView a more general biometricUnlockView 1021 // We will need to add our Face Unlock specific child views programmatically in 1022 // initializeView rather than having them in the XML files. 1023 View biometricUnlockView = view.findViewById(R.id.faceLockAreaView); 1024 if (biometricUnlockView != null) { 1025 mBiometricUnlock = new FaceUnlock(mContext, mUpdateMonitor, mLockPatternUtils, 1026 mKeyguardScreenCallback); 1027 mBiometricUnlock.initializeView(biometricUnlockView); 1028 1029 // If this is being called because the screen turned off, we want to cover the 1030 // backup lock so it is covered when the screen turns back on. 1031 if (!mScreenOn) mBiometricUnlock.show(0); 1032 } else { 1033 Log.w(TAG, "Couldn't find biometric unlock view"); 1034 } 1035 } 1036 1037 if (mBiometricUnlock != null && restartBiometricUnlock) { 1038 maybeStartBiometricUnlock(); 1039 } 1040 } 1041 1042 /** 1043 * Given the current state of things, what should be the initial mode of 1044 * the lock screen (lock or unlock). 1045 */ 1046 private Mode getInitialMode() { 1047 final IccCard.State simState = mUpdateMonitor.getSimState(); 1048 if (stuckOnLockScreenBecauseSimMissing() || 1049 (simState == IccCard.State.PUK_REQUIRED && 1050 !mLockPatternUtils.isPukUnlockScreenEnable())) { 1051 return Mode.LockScreen; 1052 } else { 1053 if (!isSecure() || mShowLockBeforeUnlock) { 1054 return Mode.LockScreen; 1055 } else { 1056 return Mode.UnlockScreen; 1057 } 1058 } 1059 } 1060 1061 /** 1062 * Given the current state of things, what should the unlock screen be? 1063 */ 1064 private UnlockMode getUnlockMode() { 1065 final IccCard.State simState = mUpdateMonitor.getSimState(); 1066 UnlockMode currentMode; 1067 if (simState == IccCard.State.PIN_REQUIRED) { 1068 currentMode = UnlockMode.SimPin; 1069 } else if (simState == IccCard.State.PUK_REQUIRED) { 1070 currentMode = UnlockMode.SimPuk; 1071 } else { 1072 final int mode = mLockPatternUtils.getKeyguardStoredPasswordQuality(); 1073 switch (mode) { 1074 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: 1075 case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: 1076 case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: 1077 case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: 1078 currentMode = UnlockMode.Password; 1079 break; 1080 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: 1081 case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED: 1082 if (mLockPatternUtils.isLockPatternEnabled()) { 1083 // "forgot pattern" button is only available in the pattern mode... 1084 if (mForgotPattern || mLockPatternUtils.isPermanentlyLocked()) { 1085 currentMode = UnlockMode.Account; 1086 } else { 1087 currentMode = UnlockMode.Pattern; 1088 } 1089 } else { 1090 currentMode = UnlockMode.Unknown; 1091 } 1092 break; 1093 default: 1094 throw new IllegalStateException("Unknown unlock mode:" + mode); 1095 } 1096 } 1097 return currentMode; 1098 } 1099 1100 private void showDialog(String title, String message) { 1101 mHasDialog = true; 1102 final AlertDialog dialog = new AlertDialog.Builder(mContext) 1103 .setTitle(title) 1104 .setMessage(message) 1105 .setNeutralButton(R.string.ok, null) 1106 .create(); 1107 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 1108 dialog.show(); 1109 } 1110 1111 private void showTimeoutDialog() { 1112 int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; 1113 int messageId = R.string.lockscreen_too_many_failed_attempts_dialog_message; 1114 if (getUnlockMode() == UnlockMode.Password) { 1115 if(mLockPatternUtils.getKeyguardStoredPasswordQuality() == 1116 DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) { 1117 messageId = R.string.lockscreen_too_many_failed_pin_attempts_dialog_message; 1118 } else { 1119 messageId = R.string.lockscreen_too_many_failed_password_attempts_dialog_message; 1120 } 1121 } 1122 String message = mContext.getString(messageId, mUpdateMonitor.getFailedAttempts(), 1123 timeoutInSeconds); 1124 1125 showDialog(null, message); 1126 } 1127 1128 private void showAlmostAtAccountLoginDialog() { 1129 final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; 1130 final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET 1131 - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; 1132 String message = mContext.getString(R.string.lockscreen_failed_attempts_almost_glogin, 1133 count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds); 1134 showDialog(null, message); 1135 } 1136 1137 private void showAlmostAtWipeDialog(int attempts, int remaining) { 1138 int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; 1139 String message = mContext.getString( 1140 R.string.lockscreen_failed_attempts_almost_at_wipe, attempts, remaining); 1141 showDialog(null, message); 1142 } 1143 1144 private void showWipeDialog(int attempts) { 1145 String message = mContext.getString( 1146 R.string.lockscreen_failed_attempts_now_wiping, attempts); 1147 showDialog(null, message); 1148 } 1149 1150 /** 1151 * Used to put wallpaper on the background of the lock screen. Centers it 1152 * Horizontally and pins the bottom (assuming that the lock screen is aligned 1153 * with the bottom, so the wallpaper should extend above the top into the 1154 * status bar). 1155 */ 1156 static private class FastBitmapDrawable extends Drawable { 1157 private Bitmap mBitmap; 1158 private int mOpacity; 1159 1160 private FastBitmapDrawable(Bitmap bitmap) { 1161 mBitmap = bitmap; 1162 mOpacity = mBitmap.hasAlpha() ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE; 1163 } 1164 1165 @Override 1166 public void draw(Canvas canvas) { 1167 canvas.drawBitmap( 1168 mBitmap, 1169 (getBounds().width() - mBitmap.getWidth()) / 2, 1170 (getBounds().height() - mBitmap.getHeight()), 1171 null); 1172 } 1173 1174 @Override 1175 public int getOpacity() { 1176 return mOpacity; 1177 } 1178 1179 @Override 1180 public void setAlpha(int alpha) { 1181 } 1182 1183 @Override 1184 public void setColorFilter(ColorFilter cf) { 1185 } 1186 1187 @Override 1188 public int getIntrinsicWidth() { 1189 return mBitmap.getWidth(); 1190 } 1191 1192 @Override 1193 public int getIntrinsicHeight() { 1194 return mBitmap.getHeight(); 1195 } 1196 1197 @Override 1198 public int getMinimumWidth() { 1199 return mBitmap.getWidth(); 1200 } 1201 1202 @Override 1203 public int getMinimumHeight() { 1204 return mBitmap.getHeight(); 1205 } 1206 } 1207 1208 /** 1209 * Starts the biometric unlock if it should be started based on a number of factors including 1210 * the mSuppressBiometricUnlock flag. If it should not be started, it hides the biometric 1211 * unlock area. 1212 */ 1213 private void maybeStartBiometricUnlock() { 1214 if (mBiometricUnlock != null) { 1215 final boolean backupIsTimedOut = (mUpdateMonitor.getFailedAttempts() >= 1216 LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT); 1217 if (!mSuppressBiometricUnlock 1218 && mUpdateMonitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE 1219 && !mUpdateMonitor.getMaxBiometricUnlockAttemptsReached() 1220 && !backupIsTimedOut) { 1221 mBiometricUnlock.start(); 1222 } else { 1223 mBiometricUnlock.hide(); 1224 } 1225 } 1226 } 1227 } 1228