LockPatternKeyguardView.java revision 1c18828d20807342d37000746b18a3c1696f3b2e
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; 18 19import com.android.internal.R; 20import com.android.internal.policy.impl.LockPatternKeyguardView.UnlockMode; 21import com.android.internal.telephony.IccCard; 22import com.android.internal.widget.LockPatternUtils; 23import com.android.internal.widget.LockScreenWidgetCallback; 24import com.android.internal.widget.TransportControlView; 25 26import android.accounts.Account; 27import android.accounts.AccountManager; 28import android.accounts.AccountManagerCallback; 29import android.accounts.AccountManagerFuture; 30import android.accounts.AuthenticatorException; 31import android.accounts.OperationCanceledException; 32import android.app.AlertDialog; 33import android.app.admin.DevicePolicyManager; 34import android.content.Context; 35import android.content.Intent; 36import android.content.res.Configuration; 37import android.content.res.Resources; 38import android.graphics.Bitmap; 39import android.graphics.Canvas; 40import android.graphics.ColorFilter; 41import android.graphics.PixelFormat; 42import android.graphics.drawable.Drawable; 43import android.os.Bundle; 44import android.os.SystemClock; 45import android.os.SystemProperties; 46import android.telephony.TelephonyManager; 47import android.text.TextUtils; 48import android.util.Log; 49import android.util.Slog; 50import android.view.KeyEvent; 51import android.view.MotionEvent; 52import android.view.View; 53import android.view.WindowManager; 54import android.view.accessibility.AccessibilityManager; 55 56import java.io.IOException; 57 58/** 59 * The host view for all of the screens of the pattern unlock screen. There are 60 * two {@link Mode}s of operation, lock and unlock. This will show the appropriate 61 * screen, and listen for callbacks via 62 * {@link com.android.internal.policy.impl.KeyguardScreenCallback} 63 * from the current screen. 64 * 65 * This view, in turn, communicates back to 66 * {@link com.android.internal.policy.impl.KeyguardViewManager} 67 * via its {@link com.android.internal.policy.impl.KeyguardViewCallback}, as appropriate. 68 */ 69public class LockPatternKeyguardView extends KeyguardViewBase { 70 71 private static final int TRANSPORT_USERACTIVITY_TIMEOUT = 10000; 72 73 static final boolean DEBUG_CONFIGURATION = false; 74 75 // time after launching EmergencyDialer before the screen goes blank. 76 private static final int EMERGENCY_CALL_TIMEOUT = 10000; 77 78 // intent action for launching emergency dialer activity. 79 static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL"; 80 81 private static final boolean DEBUG = false; 82 private static final String TAG = "LockPatternKeyguardView"; 83 84 private final KeyguardUpdateMonitor mUpdateMonitor; 85 private final KeyguardWindowController mWindowController; 86 87 private View mLockScreen; 88 private View mUnlockScreen; 89 90 private boolean mScreenOn = false; 91 private boolean mEnableFallback = false; // assume no fallback UI until we know better 92 93 private boolean mShowLockBeforeUnlock = false; 94 95 /** 96 * The current {@link KeyguardScreen} will use this to communicate back to us. 97 */ 98 KeyguardScreenCallback mKeyguardScreenCallback; 99 100 101 private boolean mRequiresSim; 102 103 104 /** 105 * Either a lock screen (an informational keyguard screen), or an unlock 106 * screen (a means for unlocking the device) is shown at any given time. 107 */ 108 enum Mode { 109 LockScreen, 110 UnlockScreen 111 } 112 113 /** 114 * The different types screens available for {@link Mode#UnlockScreen}. 115 * @see com.android.internal.policy.impl.LockPatternKeyguardView#getUnlockMode() 116 */ 117 enum UnlockMode { 118 119 /** 120 * Unlock by drawing a pattern. 121 */ 122 Pattern, 123 124 /** 125 * Unlock by entering a sim pin. 126 */ 127 SimPin, 128 129 /** 130 * Unlock by entering a sim puk. 131 */ 132 SimPuk, 133 134 /** 135 * Unlock by entering an account's login and password. 136 */ 137 Account, 138 139 /** 140 * Unlock by entering a password or PIN 141 */ 142 Password, 143 144 /** 145 * Unknown (uninitialized) value 146 */ 147 Unknown 148 } 149 150 /** 151 * The current mode. 152 */ 153 private Mode mMode = Mode.LockScreen; 154 155 /** 156 * Keeps track of what mode the current unlock screen is (cached from most recent computation in 157 * {@link #getUnlockMode}). 158 */ 159 private UnlockMode mUnlockScreenMode = UnlockMode.Unknown; 160 161 private boolean mForgotPattern; 162 163 /** 164 * If true, it means we are in the process of verifying that the user 165 * can get past the lock screen per {@link #verifyUnlock()} 166 */ 167 private boolean mIsVerifyUnlockOnly = false; 168 169 170 /** 171 * Used to lookup the state of the lock pattern 172 */ 173 private final LockPatternUtils mLockPatternUtils; 174 175 /** 176 * The current configuration. 177 */ 178 private Configuration mConfiguration; 179 180 private Runnable mRecreateRunnable = new Runnable() { 181 public void run() { 182 updateScreen(mMode, false); 183 } 184 }; 185 186 private LockScreenWidgetCallback mWidgetCallback = new LockScreenWidgetCallback() { 187 public void userActivity(View self) { 188 mKeyguardScreenCallback.pokeWakelock(TRANSPORT_USERACTIVITY_TIMEOUT); 189 } 190 191 public void requestShow(View view) { 192 if (DEBUG) Log.v(TAG, "View " + view + " requested show transports"); 193 view.setVisibility(View.VISIBLE); 194 } 195 196 public void requestHide(View view) { 197 if (DEBUG) Log.v(TAG, "View " + view + " requested hide transports"); 198 view.setVisibility(View.GONE); 199 } 200 }; 201 202 /** 203 * @return Whether we are stuck on the lock screen because the sim is 204 * missing. 205 */ 206 private boolean stuckOnLockScreenBecauseSimMissing() { 207 return mRequiresSim 208 && (!mUpdateMonitor.isDeviceProvisioned()) 209 && (mUpdateMonitor.getSimState() == IccCard.State.ABSENT || 210 mUpdateMonitor.getSimState() == IccCard.State.PERM_DISABLED); 211 } 212 213 /** 214 * @param context Used to inflate, and create views. 215 * @param updateMonitor Knows the state of the world, and passed along to each 216 * screen so they can use the knowledge, and also register for callbacks 217 * on dynamic information. 218 * @param lockPatternUtils Used to look up state of lock pattern. 219 */ 220 public LockPatternKeyguardView( 221 Context context, 222 KeyguardUpdateMonitor updateMonitor, 223 LockPatternUtils lockPatternUtils, 224 KeyguardWindowController controller) { 225 super(context); 226 227 mConfiguration = context.getResources().getConfiguration(); 228 mEnableFallback = false; 229 mRequiresSim = TextUtils.isEmpty(SystemProperties.get("keyguard.no_require_sim")); 230 mUpdateMonitor = updateMonitor; 231 mLockPatternUtils = lockPatternUtils; 232 mWindowController = controller; 233 234 mKeyguardScreenCallback = new KeyguardScreenCallback() { 235 236 public void goToLockScreen() { 237 mForgotPattern = false; 238 if (mIsVerifyUnlockOnly) { 239 // navigating away from unlock screen during verify mode means 240 // we are done and the user failed to authenticate. 241 mIsVerifyUnlockOnly = false; 242 getCallback().keyguardDone(false); 243 } else { 244 updateScreen(Mode.LockScreen, false); 245 } 246 } 247 248 public void goToUnlockScreen() { 249 final IccCard.State simState = mUpdateMonitor.getSimState(); 250 if (stuckOnLockScreenBecauseSimMissing() 251 || (simState == IccCard.State.PUK_REQUIRED 252 && !mLockPatternUtils.isPukUnlockScreenEnable())){ 253 // stuck on lock screen when sim missing or 254 // puk'd but puk unlock screen is disabled 255 return; 256 } 257 if (!isSecure()) { 258 getCallback().keyguardDone(true); 259 } else { 260 updateScreen(Mode.UnlockScreen, false); 261 } 262 } 263 264 public void forgotPattern(boolean isForgotten) { 265 if (mEnableFallback) { 266 mForgotPattern = isForgotten; 267 updateScreen(Mode.UnlockScreen, false); 268 } 269 } 270 271 public boolean isSecure() { 272 return LockPatternKeyguardView.this.isSecure(); 273 } 274 275 public boolean isVerifyUnlockOnly() { 276 return mIsVerifyUnlockOnly; 277 } 278 279 public void recreateMe(Configuration config) { 280 removeCallbacks(mRecreateRunnable); 281 post(mRecreateRunnable); 282 } 283 284 public void takeEmergencyCallAction() { 285 pokeWakelock(EMERGENCY_CALL_TIMEOUT); 286 if (TelephonyManager.getDefault().getCallState() 287 == TelephonyManager.CALL_STATE_OFFHOOK) { 288 mLockPatternUtils.resumeCall(); 289 } else { 290 Intent intent = new Intent(ACTION_EMERGENCY_DIAL); 291 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 292 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 293 getContext().startActivity(intent); 294 } 295 } 296 297 public void pokeWakelock() { 298 getCallback().pokeWakelock(); 299 } 300 301 public void pokeWakelock(int millis) { 302 getCallback().pokeWakelock(millis); 303 } 304 305 public void keyguardDone(boolean authenticated) { 306 getCallback().keyguardDone(authenticated); 307 } 308 309 public void keyguardDoneDrawing() { 310 // irrelevant to keyguard screen, they shouldn't be calling this 311 } 312 313 public void reportFailedUnlockAttempt() { 314 mUpdateMonitor.reportFailedAttempt(); 315 final int failedAttempts = mUpdateMonitor.getFailedAttempts(); 316 if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts + 317 " (enableFallback=" + mEnableFallback + ")"); 318 319 final boolean usingPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality() 320 == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING; 321 322 final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager() 323 .getMaximumFailedPasswordsForWipe(null); 324 325 final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET 326 - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; 327 328 final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ? 329 (failedAttemptsBeforeWipe - failedAttempts) 330 : Integer.MAX_VALUE; // because DPM returns 0 if no restriction 331 332 if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) { 333 // If we reach this code, it means the user has installed a DevicePolicyManager 334 // that requests device wipe after N attempts. Once we get below the grace 335 // period, we'll post this dialog every time as a clear warning until the 336 // bombshell hits and the device is wiped. 337 if (remainingBeforeWipe > 0) { 338 showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe); 339 } else { 340 // Too many attempts. The device will be wiped shortly. 341 Slog.i(TAG, "Too many unlock attempts; device will be wiped!"); 342 showWipeDialog(failedAttempts); 343 } 344 } else if (usingPattern && mEnableFallback) { 345 if (failedAttempts == failedAttemptWarning) { 346 showAlmostAtAccountLoginDialog(); 347 } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) { 348 mLockPatternUtils.setPermanentlyLocked(true); 349 updateScreen(mMode, false); 350 } 351 } else { 352 final boolean showTimeout = 353 (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0; 354 if (showTimeout) { 355 showTimeoutDialog(); 356 } 357 } 358 mLockPatternUtils.reportFailedPasswordAttempt(); 359 } 360 361 public boolean doesFallbackUnlockScreenExist() { 362 return mEnableFallback; 363 } 364 365 public void reportSuccessfulUnlockAttempt() { 366 mLockPatternUtils.reportSuccessfulPasswordAttempt(); 367 } 368 }; 369 370 /** 371 * We'll get key events the current screen doesn't use. see 372 * {@link KeyguardViewBase#onKeyDown(int, android.view.KeyEvent)} 373 */ 374 setFocusableInTouchMode(true); 375 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); 376 377 updateScreen(getInitialMode(), false); 378 maybeEnableFallback(context); 379 } 380 381 private class AccountAnalyzer implements AccountManagerCallback<Bundle> { 382 private final AccountManager mAccountManager; 383 private final Account[] mAccounts; 384 private int mAccountIndex; 385 386 private AccountAnalyzer(AccountManager accountManager) { 387 mAccountManager = accountManager; 388 mAccounts = accountManager.getAccountsByType("com.google"); 389 } 390 391 private void next() { 392 // if we are ready to enable the fallback or if we depleted the list of accounts 393 // then finish and get out 394 if (mEnableFallback || mAccountIndex >= mAccounts.length) { 395 if (mUnlockScreen == null) { 396 Log.w(TAG, "no unlock screen when trying to enable fallback"); 397 } else if (mUnlockScreen instanceof PatternUnlockScreen) { 398 ((PatternUnlockScreen)mUnlockScreen).setEnableFallback(mEnableFallback); 399 } 400 return; 401 } 402 403 // lookup the confirmCredentials intent for the current account 404 mAccountManager.confirmCredentials(mAccounts[mAccountIndex], null, null, this, null); 405 } 406 407 public void start() { 408 mEnableFallback = false; 409 mAccountIndex = 0; 410 next(); 411 } 412 413 public void run(AccountManagerFuture<Bundle> future) { 414 try { 415 Bundle result = future.getResult(); 416 if (result.getParcelable(AccountManager.KEY_INTENT) != null) { 417 mEnableFallback = true; 418 } 419 } catch (OperationCanceledException e) { 420 // just skip the account if we are unable to query it 421 } catch (IOException e) { 422 // just skip the account if we are unable to query it 423 } catch (AuthenticatorException e) { 424 // just skip the account if we are unable to query it 425 } finally { 426 mAccountIndex++; 427 next(); 428 } 429 } 430 } 431 432 private void maybeEnableFallback(Context context) { 433 // Ask the account manager if we have an account that can be used as a 434 // fallback in case the user forgets his pattern. 435 AccountAnalyzer accountAnalyzer = new AccountAnalyzer(AccountManager.get(context)); 436 accountAnalyzer.start(); 437 } 438 439 440 // TODO: 441 // This overloaded method was added to workaround a race condition in the framework between 442 // notification for orientation changed, layout() and switching resources. This code attempts 443 // to avoid drawing the incorrect layout while things are in transition. The method can just 444 // be removed once the race condition is fixed. See bugs 2262578 and 2292713. 445 @Override 446 protected void dispatchDraw(Canvas canvas) { 447 if (DEBUG) Log.v(TAG, "*** dispatchDraw() time: " + SystemClock.elapsedRealtime()); 448 super.dispatchDraw(canvas); 449 } 450 451 @Override 452 public void reset() { 453 mIsVerifyUnlockOnly = false; 454 mForgotPattern = false; 455 updateScreen(getInitialMode(), false); 456 } 457 458 @Override 459 public void onScreenTurnedOff() { 460 mScreenOn = false; 461 mForgotPattern = false; 462 if (mMode == Mode.LockScreen) { 463 ((KeyguardScreen) mLockScreen).onPause(); 464 } else { 465 ((KeyguardScreen) mUnlockScreen).onPause(); 466 } 467 } 468 469 @Override 470 public void onScreenTurnedOn() { 471 mScreenOn = true; 472 if (mMode == Mode.LockScreen) { 473 ((KeyguardScreen) mLockScreen).onResume(); 474 } else { 475 ((KeyguardScreen) mUnlockScreen).onResume(); 476 } 477 } 478 479 private void recreateLockScreen() { 480 if (mLockScreen != null) { 481 if (mLockScreen.getVisibility() == View.VISIBLE) { 482 ((KeyguardScreen) mLockScreen).onPause(); 483 } 484 ((KeyguardScreen) mLockScreen).cleanUp(); 485 removeView(mLockScreen); 486 } 487 488 mLockScreen = createLockScreen(); 489 mLockScreen.setVisibility(View.INVISIBLE); 490 addView(mLockScreen); 491 } 492 493 private void recreateUnlockScreen(UnlockMode unlockMode) { 494 if (mUnlockScreen != null) { 495 if (mUnlockScreen.getVisibility() == View.VISIBLE) { 496 ((KeyguardScreen) mUnlockScreen).onPause(); 497 } 498 ((KeyguardScreen) mUnlockScreen).cleanUp(); 499 removeView(mUnlockScreen); 500 } 501 502 mUnlockScreen = createUnlockScreenFor(unlockMode); 503 mUnlockScreen.setVisibility(View.INVISIBLE); 504 addView(mUnlockScreen); 505 } 506 507 @Override 508 protected void onDetachedFromWindow() { 509 removeCallbacks(mRecreateRunnable); 510 super.onDetachedFromWindow(); 511 } 512 513 protected void onConfigurationChanged(Configuration newConfig) { 514 Resources resources = getResources(); 515 mShowLockBeforeUnlock = resources.getBoolean(R.bool.config_enableLockBeforeUnlockScreen); 516 mConfiguration = newConfig; 517 if (DEBUG_CONFIGURATION) Log.v(TAG, "**** re-creating lock screen since config changed"); 518 updateScreen(mMode, true /* force */); 519 } 520 521 @Override 522 protected boolean dispatchHoverEvent(MotionEvent event) { 523 // Do not let the screen to get locked while the user is disabled and touch 524 // exploring. A blind user will need significantly more time to find and 525 // interact with the lock screen views. 526 AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext); 527 if (accessibilityManager.isEnabled() && accessibilityManager.isTouchExplorationEnabled()) { 528 getCallback().pokeWakelock(); 529 } 530 return super.dispatchHoverEvent(event); 531 } 532 533 @Override 534 public void wakeWhenReadyTq(int keyCode) { 535 if (DEBUG) Log.d(TAG, "onWakeKey"); 536 if (keyCode == KeyEvent.KEYCODE_MENU && isSecure() && (mMode == Mode.LockScreen) 537 && (mUpdateMonitor.getSimState() != IccCard.State.PUK_REQUIRED)) { 538 if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU"); 539 updateScreen(Mode.UnlockScreen, false); 540 getCallback().pokeWakelock(); 541 } else { 542 if (DEBUG) Log.d(TAG, "poking wake lock immediately"); 543 getCallback().pokeWakelock(); 544 } 545 } 546 547 @Override 548 public void verifyUnlock() { 549 if (!isSecure()) { 550 // non-secure keyguard screens are successfull by default 551 getCallback().keyguardDone(true); 552 } else if (mUnlockScreenMode != UnlockMode.Pattern 553 && mUnlockScreenMode != UnlockMode.Password) { 554 // can only verify unlock when in pattern/password mode 555 getCallback().keyguardDone(false); 556 } else { 557 // otherwise, go to the unlock screen, see if they can verify it 558 mIsVerifyUnlockOnly = true; 559 updateScreen(Mode.UnlockScreen, false); 560 } 561 } 562 563 @Override 564 public void cleanUp() { 565 if (mLockScreen != null) { 566 ((KeyguardScreen) mLockScreen).onPause(); 567 ((KeyguardScreen) mLockScreen).cleanUp(); 568 this.removeView(mLockScreen); 569 mLockScreen = null; 570 } 571 if (mUnlockScreen != null) { 572 ((KeyguardScreen) mUnlockScreen).onPause(); 573 ((KeyguardScreen) mUnlockScreen).cleanUp(); 574 this.removeView(mUnlockScreen); 575 mUnlockScreen = null; 576 } 577 } 578 579 private boolean isSecure() { 580 UnlockMode unlockMode = getUnlockMode(); 581 boolean secure = false; 582 switch (unlockMode) { 583 case Pattern: 584 secure = mLockPatternUtils.isLockPatternEnabled(); 585 break; 586 case SimPin: 587 secure = mUpdateMonitor.getSimState() == IccCard.State.PIN_REQUIRED; 588 break; 589 case SimPuk: 590 secure = mUpdateMonitor.getSimState() == IccCard.State.PUK_REQUIRED; 591 break; 592 case Account: 593 secure = true; 594 break; 595 case Password: 596 secure = mLockPatternUtils.isLockPasswordEnabled(); 597 break; 598 default: 599 throw new IllegalStateException("unknown unlock mode " + unlockMode); 600 } 601 return secure; 602 } 603 604 private void updateScreen(Mode mode, boolean force) { 605 606 if (DEBUG_CONFIGURATION) Log.v(TAG, "**** UPDATE SCREEN: mode=" + mode 607 + " last mode=" + mMode, new RuntimeException()); 608 609 mMode = mode; 610 611 // Re-create the lock screen if necessary 612 if (mode == Mode.LockScreen || mShowLockBeforeUnlock) { 613 if (force || mLockScreen == null) { 614 recreateLockScreen(); 615 } 616 } 617 618 // Re-create the unlock screen if necessary. This is primarily required to properly handle 619 // SIM state changes. This typically happens when this method is called by reset() 620 if (mode == Mode.UnlockScreen) { 621 final UnlockMode unlockMode = getUnlockMode(); 622 if (force || mUnlockScreen == null || unlockMode != mUnlockScreenMode) { 623 recreateUnlockScreen(unlockMode); 624 } 625 } 626 627 // visibleScreen should never be null 628 final View goneScreen = (mode == Mode.LockScreen) ? mUnlockScreen : mLockScreen; 629 final View visibleScreen = (mode == Mode.LockScreen) ? mLockScreen : mUnlockScreen; 630 631 // do this before changing visibility so focus isn't requested before the input 632 // flag is set 633 mWindowController.setNeedsInput(((KeyguardScreen)visibleScreen).needsInput()); 634 635 if (DEBUG_CONFIGURATION) { 636 Log.v(TAG, "Gone=" + goneScreen); 637 Log.v(TAG, "Visible=" + visibleScreen); 638 } 639 640 if (mScreenOn) { 641 if (goneScreen != null && goneScreen.getVisibility() == View.VISIBLE) { 642 ((KeyguardScreen) goneScreen).onPause(); 643 } 644 if (visibleScreen.getVisibility() != View.VISIBLE) { 645 ((KeyguardScreen) visibleScreen).onResume(); 646 } 647 } 648 649 if (goneScreen != null) { 650 goneScreen.setVisibility(View.GONE); 651 } 652 visibleScreen.setVisibility(View.VISIBLE); 653 requestLayout(); 654 655 if (!visibleScreen.requestFocus()) { 656 throw new IllegalStateException("keyguard screen must be able to take " 657 + "focus when shown " + visibleScreen.getClass().getCanonicalName()); 658 } 659 } 660 661 View createLockScreen() { 662 View lockView = new LockScreen( 663 mContext, 664 mConfiguration, 665 mLockPatternUtils, 666 mUpdateMonitor, 667 mKeyguardScreenCallback); 668 initializeTransportControlView(lockView); 669 return lockView; 670 } 671 672 View createUnlockScreenFor(UnlockMode unlockMode) { 673 View unlockView = null; 674 675 if (DEBUG) Log.d(TAG, 676 "createUnlockScreenFor(" + unlockMode + "): mEnableFallback=" + mEnableFallback); 677 678 if (unlockMode == UnlockMode.Pattern) { 679 PatternUnlockScreen view = new PatternUnlockScreen( 680 mContext, 681 mConfiguration, 682 mLockPatternUtils, 683 mUpdateMonitor, 684 mKeyguardScreenCallback, 685 mUpdateMonitor.getFailedAttempts()); 686 view.setEnableFallback(mEnableFallback); 687 unlockView = view; 688 } else if (unlockMode == UnlockMode.SimPuk) { 689 unlockView = new SimPukUnlockScreen( 690 mContext, 691 mConfiguration, 692 mUpdateMonitor, 693 mKeyguardScreenCallback, 694 mLockPatternUtils); 695 } else if (unlockMode == UnlockMode.SimPin) { 696 unlockView = new SimUnlockScreen( 697 mContext, 698 mConfiguration, 699 mUpdateMonitor, 700 mKeyguardScreenCallback, 701 mLockPatternUtils); 702 } else if (unlockMode == UnlockMode.Account) { 703 try { 704 unlockView = new AccountUnlockScreen( 705 mContext, 706 mConfiguration, 707 mUpdateMonitor, 708 mKeyguardScreenCallback, 709 mLockPatternUtils); 710 } catch (IllegalStateException e) { 711 Log.i(TAG, "Couldn't instantiate AccountUnlockScreen" 712 + " (IAccountsService isn't available)"); 713 // TODO: Need a more general way to provide a 714 // platform-specific fallback UI here. 715 // For now, if we can't display the account login 716 // unlock UI, just bring back the regular "Pattern" unlock mode. 717 718 // (We do this by simply returning a regular UnlockScreen 719 // here. This means that the user will still see the 720 // regular pattern unlock UI, regardless of the value of 721 // mUnlockScreenMode or whether or not we're in the 722 // "permanently locked" state.) 723 return createUnlockScreenFor(UnlockMode.Pattern); 724 } 725 } else if (unlockMode == UnlockMode.Password) { 726 unlockView = new PasswordUnlockScreen( 727 mContext, 728 mConfiguration, 729 mLockPatternUtils, 730 mUpdateMonitor, 731 mKeyguardScreenCallback); 732 } else { 733 throw new IllegalArgumentException("unknown unlock mode " + unlockMode); 734 } 735 initializeTransportControlView(unlockView); 736 mUnlockScreenMode = unlockMode; 737 return unlockView; 738 } 739 740 private void initializeTransportControlView(View view) { 741 com.android.internal.widget.TransportControlView tcv = 742 (TransportControlView) view.findViewById(R.id.transport); 743 if (tcv == null) { 744 if (DEBUG) Log.w(TAG, "Couldn't find transport control widget"); 745 } else { 746 tcv.setVisibility(View.GONE); // hide tcv until we get the callback below to show it. 747 tcv.setCallback(mWidgetCallback); 748 } 749 } 750 751 /** 752 * Given the current state of things, what should be the initial mode of 753 * the lock screen (lock or unlock). 754 */ 755 private Mode getInitialMode() { 756 final IccCard.State simState = mUpdateMonitor.getSimState(); 757 if (stuckOnLockScreenBecauseSimMissing() || 758 (simState == IccCard.State.PUK_REQUIRED && 759 !mLockPatternUtils.isPukUnlockScreenEnable())) { 760 return Mode.LockScreen; 761 } else { 762 if (!isSecure() || mShowLockBeforeUnlock) { 763 return Mode.LockScreen; 764 } else { 765 return Mode.UnlockScreen; 766 } 767 } 768 } 769 770 /** 771 * Given the current state of things, what should the unlock screen be? 772 */ 773 private UnlockMode getUnlockMode() { 774 final IccCard.State simState = mUpdateMonitor.getSimState(); 775 UnlockMode currentMode; 776 if (simState == IccCard.State.PIN_REQUIRED) { 777 currentMode = UnlockMode.SimPin; 778 } else if (simState == IccCard.State.PUK_REQUIRED) { 779 currentMode = UnlockMode.SimPuk; 780 } else { 781 final int mode = mLockPatternUtils.getKeyguardStoredPasswordQuality(); 782 switch (mode) { 783 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC: 784 case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC: 785 case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC: 786 case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX: 787 currentMode = UnlockMode.Password; 788 break; 789 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING: 790 case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED: 791 // "forgot pattern" button is only available in the pattern mode... 792 if (mForgotPattern || mLockPatternUtils.isPermanentlyLocked()) { 793 currentMode = UnlockMode.Account; 794 } else { 795 currentMode = UnlockMode.Pattern; 796 } 797 break; 798 default: 799 throw new IllegalStateException("Unknown unlock mode:" + mode); 800 } 801 } 802 return currentMode; 803 } 804 805 private void showDialog(String title, String message) { 806 final AlertDialog dialog = new AlertDialog.Builder(mContext) 807 .setTitle(title) 808 .setMessage(message) 809 .setNeutralButton(R.string.ok, null) 810 .create(); 811 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 812 dialog.show(); 813 } 814 815 private void showTimeoutDialog() { 816 int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; 817 int messageId = R.string.lockscreen_too_many_failed_attempts_dialog_message; 818 if (getUnlockMode() == UnlockMode.Password) { 819 if(mLockPatternUtils.getKeyguardStoredPasswordQuality() == 820 DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) { 821 messageId = R.string.lockscreen_too_many_failed_pin_attempts_dialog_message; 822 } else { 823 messageId = R.string.lockscreen_too_many_failed_password_attempts_dialog_message; 824 } 825 } 826 String message = mContext.getString(messageId, mUpdateMonitor.getFailedAttempts(), 827 timeoutInSeconds); 828 829 showDialog(null, message); 830 } 831 832 private void showAlmostAtAccountLoginDialog() { 833 final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; 834 final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET 835 - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; 836 String message = mContext.getString(R.string.lockscreen_failed_attempts_almost_glogin, 837 count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds); 838 showDialog(null, message); 839 } 840 841 private void showAlmostAtWipeDialog(int attempts, int remaining) { 842 int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; 843 String message = mContext.getString( 844 R.string.lockscreen_failed_attempts_almost_at_wipe, attempts, remaining); 845 showDialog(null, message); 846 } 847 848 private void showWipeDialog(int attempts) { 849 String message = mContext.getString( 850 R.string.lockscreen_failed_attempts_now_wiping, attempts); 851 showDialog(null, message); 852 } 853 854 /** 855 * Used to put wallpaper on the background of the lock screen. Centers it 856 * Horizontally and pins the bottom (assuming that the lock screen is aligned 857 * with the bottom, so the wallpaper should extend above the top into the 858 * status bar). 859 */ 860 static private class FastBitmapDrawable extends Drawable { 861 private Bitmap mBitmap; 862 private int mOpacity; 863 864 private FastBitmapDrawable(Bitmap bitmap) { 865 mBitmap = bitmap; 866 mOpacity = mBitmap.hasAlpha() ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE; 867 } 868 869 @Override 870 public void draw(Canvas canvas) { 871 canvas.drawBitmap( 872 mBitmap, 873 (getBounds().width() - mBitmap.getWidth()) / 2, 874 (getBounds().height() - mBitmap.getHeight()), 875 null); 876 } 877 878 @Override 879 public int getOpacity() { 880 return mOpacity; 881 } 882 883 @Override 884 public void setAlpha(int alpha) { 885 } 886 887 @Override 888 public void setColorFilter(ColorFilter cf) { 889 } 890 891 @Override 892 public int getIntrinsicWidth() { 893 return mBitmap.getWidth(); 894 } 895 896 @Override 897 public int getIntrinsicHeight() { 898 return mBitmap.getHeight(); 899 } 900 901 @Override 902 public int getMinimumWidth() { 903 return mBitmap.getWidth(); 904 } 905 906 @Override 907 public int getMinimumHeight() { 908 return mBitmap.getHeight(); 909 } 910 } 911} 912 913