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