KeyguardHostView.java revision cc4104fd71225ed092f16b11882d9a10084e34ac
1/* 2 * Copyright (C) 2012 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; 18 19import android.app.Activity; 20import android.app.ActivityManager; 21import android.app.ActivityOptions; 22import android.app.AlertDialog; 23import android.app.admin.DevicePolicyManager; 24import android.appwidget.AppWidgetHost; 25import android.appwidget.AppWidgetHostView; 26import android.appwidget.AppWidgetManager; 27import android.appwidget.AppWidgetProviderInfo; 28import android.content.Context; 29import android.content.Intent; 30import android.content.IntentSender; 31import android.content.pm.UserInfo; 32import android.content.res.Resources; 33import android.graphics.Canvas; 34import android.graphics.Rect; 35import android.os.Looper; 36import android.os.UserManager; 37import android.util.AttributeSet; 38import android.util.Log; 39import android.util.Slog; 40import android.view.KeyEvent; 41import android.view.LayoutInflater; 42import android.view.MotionEvent; 43import android.view.View; 44import android.view.ViewGroup; 45import android.view.WindowManager; 46import android.view.animation.AnimationUtils; 47import android.widget.RemoteViews.OnClickHandler; 48import android.widget.ViewFlipper; 49 50import com.android.internal.R; 51import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode; 52import com.android.internal.widget.LockPatternUtils; 53 54import java.io.File; 55import java.util.List; 56 57public class KeyguardHostView extends KeyguardViewBase { 58 private static final String TAG = "KeyguardViewHost"; 59 60 // Use this to debug all of keyguard 61 public static boolean DEBUG; 62 63 // also referenced in SecuritySettings.java 64 static final int APPWIDGET_HOST_ID = 0x4B455947; 65 private static final String KEYGUARD_WIDGET_PREFS = "keyguard_widget_prefs"; 66 67 private AppWidgetHost mAppWidgetHost; 68 private KeyguardWidgetRegion mAppWidgetRegion; 69 private KeyguardWidgetPager mAppWidgetContainer; 70 private ViewFlipper mSecurityViewContainer; 71 private KeyguardSelectorView mKeyguardSelectorView; 72 private KeyguardTransportControlView mTransportControl; 73 private boolean mEnableMenuKey; 74 private boolean mIsVerifyUnlockOnly; 75 private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView 76 private SecurityMode mCurrentSecuritySelection = SecurityMode.None; 77 78 protected Runnable mLaunchRunnable; 79 80 protected int mFailedAttempts; 81 private LockPatternUtils mLockPatternUtils; 82 83 private KeyguardSecurityModel mSecurityModel; 84 85 private Rect mTempRect = new Rect(); 86 87 /*package*/ interface TransportCallback { 88 void hide(); 89 void show(); 90 } 91 92 /*package*/ interface UserSwitcherCallback { 93 void hideSecurityView(int duration); 94 void showSecurityView(); 95 void showUnlockHint(); 96 } 97 98 public KeyguardHostView(Context context) { 99 this(context, null); 100 } 101 102 public KeyguardHostView(Context context, AttributeSet attrs) { 103 super(context, attrs); 104 mLockPatternUtils = new LockPatternUtils(context); 105 mAppWidgetHost = new AppWidgetHost( 106 context, APPWIDGET_HOST_ID, mOnClickHandler, Looper.myLooper()); 107 mSecurityModel = new KeyguardSecurityModel(context); 108 109 // The following enables the MENU key to work for testing automation 110 mEnableMenuKey = shouldEnableMenuKey(); 111 setFocusable(true); 112 setFocusableInTouchMode(true); 113 } 114 115 @Override 116 public boolean onTouchEvent(MotionEvent ev) { 117 boolean result = super.onTouchEvent(ev); 118 mTempRect.set(0, 0, 0, 0); 119 offsetRectIntoDescendantCoords(mSecurityViewContainer, mTempRect); 120 ev.offsetLocation(mTempRect.left, mTempRect.top); 121 result = mSecurityViewContainer.dispatchTouchEvent(ev) || result; 122 ev.offsetLocation(-mTempRect.left, -mTempRect.top); 123 return result; 124 } 125 126 @Override 127 protected void dispatchDraw(Canvas canvas) { 128 super.dispatchDraw(canvas); 129 if (mViewMediatorCallback != null) { 130 mViewMediatorCallback.keyguardDoneDrawing(); 131 } 132 } 133 134 private int getWidgetPosition(int id) { 135 final int children = mAppWidgetContainer.getChildCount(); 136 for (int i = 0; i < children; i++) { 137 if (mAppWidgetContainer.getChildAt(i).getId() == id) { 138 return i; 139 } 140 } 141 return -1; 142 } 143 144 @Override 145 protected void onFinishInflate() { 146 mAppWidgetRegion = (KeyguardWidgetRegion) findViewById(R.id.kg_widget_region); 147 mAppWidgetRegion.setVisibility(VISIBLE); 148 mAppWidgetRegion.setCallbacks(mWidgetCallbacks); 149 150 mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container); 151 mSecurityViewContainer = (ViewFlipper) findViewById(R.id.view_flipper); 152 mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view); 153 154 addDefaultWidgets(); 155 updateSecurityViews(); 156 setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK); 157 } 158 159 private void updateSecurityViews() { 160 int children = mSecurityViewContainer.getChildCount(); 161 for (int i = 0; i < children; i++) { 162 updateSecurityView(mSecurityViewContainer.getChildAt(i)); 163 } 164 } 165 166 private void updateSecurityView(View view) { 167 if (view instanceof KeyguardSecurityView) { 168 KeyguardSecurityView ksv = (KeyguardSecurityView) view; 169 ksv.setKeyguardCallback(mCallback); 170 ksv.setLockPatternUtils(mLockPatternUtils); 171 } else { 172 Log.w(TAG, "View " + view + " is not a KeyguardSecurityView"); 173 } 174 } 175 176 void setLockPatternUtils(LockPatternUtils utils) { 177 mSecurityModel.setLockPatternUtils(utils); 178 mLockPatternUtils = utils; 179 updateSecurityViews(); 180 } 181 182 @Override 183 protected void onAttachedToWindow() { 184 super.onAttachedToWindow(); 185 mAppWidgetHost.startListening(); 186 maybePopulateWidgets(); 187 disableStatusViewInteraction(); 188 showAppropriateWidgetPage(); 189 } 190 191 private void disableStatusViewInteraction() { 192 // Disable all user interaction on status view. This is done to prevent falsing in the 193 // pocket from triggering useractivity and prevents 3rd party replacement widgets 194 // from responding to user interaction while in this position. 195 View statusView = findViewById(R.id.keyguard_status_view); 196 if (statusView instanceof KeyguardWidgetFrame) { 197 ((KeyguardWidgetFrame) statusView).setDisableUserInteraction(true); 198 } 199 } 200 201 @Override 202 protected void onDetachedFromWindow() { 203 super.onDetachedFromWindow(); 204 mAppWidgetHost.stopListening(); 205 } 206 207 private AppWidgetHost getAppWidgetHost() { 208 return mAppWidgetHost; 209 } 210 211 void addWidget(AppWidgetHostView view) { 212 mAppWidgetContainer.addWidget(view); 213 } 214 215 private KeyguardWidgetRegion.Callbacks mWidgetCallbacks 216 = new KeyguardWidgetRegion.Callbacks() { 217 @Override 218 public void userActivity() { 219 if (mViewMediatorCallback != null) { 220 mViewMediatorCallback.userActivity(); 221 } 222 } 223 224 @Override 225 public void onUserActivityTimeoutChanged() { 226 if (mViewMediatorCallback != null) { 227 mViewMediatorCallback.onUserActivityTimeoutChanged(); 228 } 229 } 230 }; 231 232 @Override 233 public long getUserActivityTimeout() { 234 // Currently only considering user activity timeouts needed by widgets. 235 // Could also take into account longer timeouts for certain security views. 236 if (mAppWidgetRegion != null) { 237 return mAppWidgetRegion.getUserActivityTimeout(); 238 } 239 return -1; 240 } 241 242 private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() { 243 244 public void userActivity(long timeout) { 245 if (mViewMediatorCallback != null) { 246 mViewMediatorCallback.userActivity(timeout); 247 } 248 } 249 250 public void dismiss(boolean authenticated) { 251 // If the biometric unlock was suppressed due to a user switch, it can now be safely 252 // unsuppressed because the user has left the unlock screen. 253 KeyguardUpdateMonitor.getInstance(mContext).clearBiometricUnlockUserSwitched(); 254 showNextSecurityScreenOrFinish(authenticated); 255 } 256 257 public boolean isVerifyUnlockOnly() { 258 return mIsVerifyUnlockOnly; 259 } 260 261 public void reportSuccessfulUnlockAttempt() { 262 KeyguardUpdateMonitor.getInstance(mContext).clearFailedUnlockAttempts(); 263 mLockPatternUtils.reportSuccessfulPasswordAttempt(); 264 } 265 266 public void reportFailedUnlockAttempt() { 267 if (mCurrentSecuritySelection == SecurityMode.Biometric) { 268 KeyguardUpdateMonitor.getInstance(mContext).reportFailedBiometricUnlockAttempt(); 269 } else { 270 KeyguardHostView.this.reportFailedUnlockAttempt(); 271 } 272 } 273 274 public int getFailedAttempts() { 275 return KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(); 276 } 277 278 @Override 279 public void showBackupSecurity() { 280 KeyguardHostView.this.showBackupSecurity(); 281 } 282 283 @Override 284 public void setOnDismissRunnable(Runnable runnable) { 285 KeyguardHostView.this.setOnDismissRunnable(runnable); 286 } 287 288 }; 289 290 private void showDialog(String title, String message) { 291 final AlertDialog dialog = new AlertDialog.Builder(mContext) 292 .setTitle(title) 293 .setMessage(message) 294 .setNeutralButton(com.android.internal.R.string.ok, null) 295 .create(); 296 if (!(mContext instanceof Activity)) { 297 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 298 } 299 dialog.show(); 300 } 301 302 private void showTimeoutDialog() { 303 int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; 304 int messageId = 0; 305 306 switch (mSecurityModel.getSecurityMode()) { 307 case Pattern: 308 messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message; 309 break; 310 311 case Password: { 312 final boolean isPin = mLockPatternUtils.getKeyguardStoredPasswordQuality() == 313 DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; 314 messageId = isPin ? R.string.kg_too_many_failed_pin_attempts_dialog_message 315 : R.string.kg_too_many_failed_password_attempts_dialog_message; 316 } 317 break; 318 } 319 320 if (messageId != 0) { 321 final String message = mContext.getString(messageId, 322 KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(), 323 timeoutInSeconds); 324 showDialog(null, message); 325 } 326 } 327 328 private void showAlmostAtWipeDialog(int attempts, int remaining) { 329 int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; 330 String message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe, 331 attempts, remaining); 332 showDialog(null, message); 333 } 334 335 private void showWipeDialog(int attempts) { 336 String message = mContext.getString(R.string.kg_failed_attempts_now_wiping, attempts); 337 showDialog(null, message); 338 } 339 340 private void showAlmostAtAccountLoginDialog() { 341 final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; 342 final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET 343 - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; 344 String message = mContext.getString(R.string.kg_failed_attempts_almost_at_login, 345 count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds); 346 showDialog(null, message); 347 } 348 349 private void reportFailedUnlockAttempt() { 350 final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); 351 final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time 352 353 if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts); 354 355 SecurityMode mode = mSecurityModel.getSecurityMode(); 356 final boolean usingPattern = mode == KeyguardSecurityModel.SecurityMode.Pattern; 357 358 final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager() 359 .getMaximumFailedPasswordsForWipe(null, mLockPatternUtils.getCurrentUser()); 360 361 final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET 362 - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; 363 364 final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ? 365 (failedAttemptsBeforeWipe - failedAttempts) 366 : Integer.MAX_VALUE; // because DPM returns 0 if no restriction 367 368 boolean showTimeout = false; 369 if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) { 370 // If we reach this code, it means the user has installed a DevicePolicyManager 371 // that requests device wipe after N attempts. Once we get below the grace 372 // period, we'll post this dialog every time as a clear warning until the 373 // bombshell hits and the device is wiped. 374 if (remainingBeforeWipe > 0) { 375 showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe); 376 } else { 377 // Too many attempts. The device will be wiped shortly. 378 Slog.i(TAG, "Too many unlock attempts; device will be wiped!"); 379 showWipeDialog(failedAttempts); 380 } 381 } else { 382 showTimeout = 383 (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0; 384 if (usingPattern && mEnableFallback) { 385 if (failedAttempts == failedAttemptWarning) { 386 showAlmostAtAccountLoginDialog(); 387 showTimeout = false; // don't show both dialogs 388 } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) { 389 mLockPatternUtils.setPermanentlyLocked(true); 390 showSecurityScreen(SecurityMode.Account); 391 // don't show timeout dialog because we show account unlock screen next 392 showTimeout = false; 393 } 394 } 395 } 396 monitor.reportFailedUnlockAttempt(); 397 mLockPatternUtils.reportFailedPasswordAttempt(); 398 if (showTimeout) { 399 showTimeoutDialog(); 400 } 401 } 402 403 /** 404 * Shows the backup security screen for the current security mode. This could be used for 405 * password recovery screens but is currently only used for pattern unlock to show the 406 * account unlock screen and biometric unlock to show the user's normal unlock. 407 */ 408 private void showBackupSecurity() { 409 showSecurityScreen(mSecurityModel.getBackupSecurityMode()); 410 } 411 412 public boolean showNextSecurityScreenIfPresent() { 413 SecurityMode securityMode = mSecurityModel.getSecurityMode(); 414 // Allow an alternate, such as biometric unlock 415 securityMode = mSecurityModel.getAlternateFor(securityMode); 416 if (SecurityMode.None == securityMode) { 417 return false; 418 } else { 419 showSecurityScreen(securityMode); // switch to the alternate security view 420 return true; 421 } 422 } 423 424 private void showNextSecurityScreenOrFinish(boolean authenticated) { 425 boolean finish = false; 426 if (SecurityMode.None == mCurrentSecuritySelection) { 427 SecurityMode securityMode = mSecurityModel.getSecurityMode(); 428 // Allow an alternate, such as biometric unlock 429 securityMode = mSecurityModel.getAlternateFor(securityMode); 430 if (SecurityMode.None == securityMode) { 431 finish = true; // no security required 432 } else { 433 showSecurityScreen(securityMode); // switch to the alternate security view 434 } 435 } else if (authenticated) { 436 switch (mCurrentSecuritySelection) { 437 case Pattern: 438 case Password: 439 case Account: 440 case Biometric: 441 finish = true; 442 break; 443 444 case SimPin: 445 case SimPuk: 446 // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home 447 SecurityMode securityMode = mSecurityModel.getSecurityMode(); 448 if (securityMode != SecurityMode.None) { 449 showSecurityScreen(securityMode); 450 } else { 451 finish = true; 452 } 453 break; 454 455 default: 456 showSecurityScreen(SecurityMode.None); 457 break; 458 } 459 } else { 460 // Not authenticated but we were asked to dismiss so go back to selector screen. 461 showSecurityScreen(SecurityMode.None); 462 } 463 if (finish) { 464 // If there's a pending runnable because the user interacted with a widget 465 // and we're leaving keyguard, then run it. 466 if (mLaunchRunnable != null) { 467 mLaunchRunnable.run(); 468 mLaunchRunnable = null; 469 } 470 if (mViewMediatorCallback != null) { 471 mViewMediatorCallback.keyguardDone(true); 472 } 473 } 474 } 475 476 private OnClickHandler mOnClickHandler = new OnClickHandler() { 477 @Override 478 public boolean onClickHandler(final View view, 479 final android.app.PendingIntent pendingIntent, 480 final Intent fillInIntent) { 481 if (pendingIntent.isActivity()) { 482 setOnDismissRunnable(new Runnable() { 483 public void run() { 484 try { 485 // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT? 486 Context context = view.getContext(); 487 ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view, 488 0, 0, 489 view.getMeasuredWidth(), view.getMeasuredHeight()); 490 context.startIntentSender( 491 pendingIntent.getIntentSender(), fillInIntent, 492 Intent.FLAG_ACTIVITY_NEW_TASK, 493 Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle()); 494 } catch (IntentSender.SendIntentException e) { 495 android.util.Log.e(TAG, "Cannot send pending intent: ", e); 496 } catch (Exception e) { 497 android.util.Log.e(TAG, "Cannot send pending intent due to " + 498 "unknown exception: ", e); 499 } 500 } 501 }); 502 503 mCallback.dismiss(false); 504 return true; 505 } else { 506 return super.onClickHandler(view, pendingIntent, fillInIntent); 507 } 508 }; 509 }; 510 511 @Override 512 public void reset() { 513 mIsVerifyUnlockOnly = false; 514 mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_status_view)); 515 requestFocus(); 516 } 517 518 /** 519 * Sets a runnable to run when keyguard is dismissed 520 * @param runnable 521 */ 522 protected void setOnDismissRunnable(Runnable runnable) { 523 mLaunchRunnable = runnable; 524 } 525 526 private KeyguardSecurityView getSecurityView(SecurityMode securityMode) { 527 final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); 528 KeyguardSecurityView view = null; 529 final int children = mSecurityViewContainer.getChildCount(); 530 for (int child = 0; child < children; child++) { 531 if (mSecurityViewContainer.getChildAt(child).getId() == securityViewIdForMode) { 532 view = ((KeyguardSecurityView)mSecurityViewContainer.getChildAt(child)); 533 break; 534 } 535 } 536 if (view == null) { 537 final LayoutInflater inflater = LayoutInflater.from(mContext); 538 View v = inflater.inflate(getLayoutIdFor(securityMode), this, false); 539 mSecurityViewContainer.addView(v); 540 updateSecurityView(v); 541 view = (KeyguardSecurityView) v; 542 } 543 return view; 544 } 545 546 /** 547 * Switches to the given security view unless it's already being shown, in which case 548 * this is a no-op. 549 * 550 * @param securityMode 551 */ 552 private void showSecurityScreen(SecurityMode securityMode) { 553 554 if (securityMode == mCurrentSecuritySelection) return; 555 556 KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection); 557 KeyguardSecurityView newView = getSecurityView(securityMode); 558 559 // Emulate Activity life cycle 560 oldView.onPause(); 561 newView.onResume(); 562 563 final boolean needsInput = newView.needsInput(); 564 if (mViewMediatorCallback != null) { 565 mViewMediatorCallback.setNeedsInput(needsInput); 566 } 567 568 // Find and show this child. 569 final int childCount = mSecurityViewContainer.getChildCount(); 570 571 // If we're go to/from the selector view, do flip animation, otherwise use fade animation. 572 final boolean doFlip = mCurrentSecuritySelection == SecurityMode.None 573 || securityMode == SecurityMode.None; 574 final int inAnimation = doFlip ? R.anim.keyguard_security_animate_in 575 : R.anim.keyguard_security_fade_in; 576 final int outAnimation = doFlip ? R.anim.keyguard_security_animate_out 577 : R.anim.keyguard_security_fade_out; 578 579 mSecurityViewContainer.setInAnimation(AnimationUtils.loadAnimation(mContext, inAnimation)); 580 mSecurityViewContainer.setOutAnimation(AnimationUtils.loadAnimation(mContext, outAnimation)); 581 final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); 582 for (int i = 0; i < childCount; i++) { 583 if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) { 584 mSecurityViewContainer.setDisplayedChild(i); 585 break; 586 } 587 } 588 589 590 if (securityMode == SecurityMode.None) { 591 // Discard current runnable if we're switching back to the selector view 592 setOnDismissRunnable(null); 593 setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK); 594 } else { 595 setSystemUiVisibility(getSystemUiVisibility() & (~View.STATUS_BAR_DISABLE_BACK)); 596 } 597 598 mCurrentSecuritySelection = securityMode; 599 } 600 601 @Override 602 public void onScreenTurnedOn() { 603 if (DEBUG) Log.d(TAG, "screen on"); 604 showSecurityScreen(mCurrentSecuritySelection); 605 getSecurityView(mCurrentSecuritySelection).onResume(); 606 607 // This is a an attempt to fix bug 7137389 where the device comes back on but the entire 608 // layout is blank but forcing a layout causes it to reappear (e.g. with with 609 // hierarchyviewer). 610 requestLayout(); 611 } 612 613 @Override 614 public void onScreenTurnedOff() { 615 if (DEBUG) Log.d(TAG, "screen off"); 616 showSecurityScreen(SecurityMode.None); 617 getSecurityView(mCurrentSecuritySelection).onPause(); 618 } 619 620 @Override 621 public void show() { 622 onScreenTurnedOn(); 623 } 624 625 private boolean isSecure() { 626 SecurityMode mode = mSecurityModel.getSecurityMode(); 627 switch (mode) { 628 case Pattern: 629 return mLockPatternUtils.isLockPatternEnabled(); 630 case Password: 631 return mLockPatternUtils.isLockPasswordEnabled(); 632 case SimPin: 633 case SimPuk: 634 case Account: 635 return true; 636 case None: 637 return false; 638 default: 639 throw new IllegalStateException("Unknown security mode " + mode); 640 } 641 } 642 643 @Override 644 public void wakeWhenReadyTq(int keyCode) { 645 if (DEBUG) Log.d(TAG, "onWakeKey"); 646 if (keyCode == KeyEvent.KEYCODE_MENU && isSecure()) { 647 if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU"); 648 showSecurityScreen(SecurityMode.None); 649 } else { 650 if (DEBUG) Log.d(TAG, "poking wake lock immediately"); 651 } 652 if (mViewMediatorCallback != null) { 653 mViewMediatorCallback.wakeUp(); 654 } 655 } 656 657 @Override 658 public void verifyUnlock() { 659 SecurityMode securityMode = mSecurityModel.getSecurityMode(); 660 if (securityMode == KeyguardSecurityModel.SecurityMode.None) { 661 if (mViewMediatorCallback != null) { 662 mViewMediatorCallback.keyguardDone(true); 663 } 664 } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern 665 && securityMode != KeyguardSecurityModel.SecurityMode.Password) { 666 // can only verify unlock when in pattern/password mode 667 if (mViewMediatorCallback != null) { 668 mViewMediatorCallback.keyguardDone(false); 669 } 670 } else { 671 // otherwise, go to the unlock screen, see if they can verify it 672 mIsVerifyUnlockOnly = true; 673 showSecurityScreen(securityMode); 674 } 675 } 676 677 private int getSecurityViewIdForMode(SecurityMode securityMode) { 678 switch (securityMode) { 679 case None: return R.id.keyguard_selector_view; 680 case Pattern: return R.id.keyguard_pattern_view; 681 case Password: return R.id.keyguard_password_view; 682 case Biometric: return R.id.keyguard_face_unlock_view; 683 case Account: return R.id.keyguard_account_view; 684 case SimPin: return R.id.keyguard_sim_pin_view; 685 case SimPuk: return R.id.keyguard_sim_puk_view; 686 } 687 return 0; 688 } 689 690 private int getLayoutIdFor(SecurityMode securityMode) { 691 switch (securityMode) { 692 case None: return R.layout.keyguard_selector_view; 693 case Pattern: return R.layout.keyguard_pattern_view; 694 case Password: return R.layout.keyguard_password_view; 695 case Biometric: return R.layout.keyguard_face_unlock_view; 696 case Account: return R.layout.keyguard_account_view; 697 case SimPin: return R.layout.keyguard_sim_pin_view; 698 case SimPuk: return R.layout.keyguard_sim_puk_view; 699 default: 700 throw new RuntimeException("No layout for securityMode " + securityMode); 701 } 702 } 703 704 private void addWidget(int appId) { 705 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext); 706 AppWidgetProviderInfo appWidgetInfo = appWidgetManager.getAppWidgetInfo(appId); 707 if (appWidgetInfo != null) { 708 AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo); 709 addWidget(view); 710 } else { 711 Log.w(TAG, "AppWidgetInfo was null; not adding widget id " + appId); 712 } 713 } 714 715 private void addDefaultWidgets() { 716 LayoutInflater inflater = LayoutInflater.from(mContext); 717 inflater.inflate(R.layout.keyguard_status_view, mAppWidgetContainer, true); 718 inflater.inflate(R.layout.keyguard_transport_control_view, mAppWidgetContainer, true); 719 720 inflateAndAddUserSelectorWidgetIfNecessary(); 721 initializeTransportControl(); 722 } 723 724 private void initializeTransportControl() { 725 mTransportControl = 726 (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control); 727 728 // This code manages showing/hiding the transport control. We keep it around and only 729 // add it to the hierarchy if it needs to be present. 730 if (mTransportControl != null) { 731 mTransportControl.setKeyguardCallback(new TransportCallback() { 732 boolean mSticky = false; 733 @Override 734 public void hide() { 735 int page = getWidgetPosition(R.id.keyguard_transport_control); 736 if (page != -1 && !mSticky) { 737 if (page == mAppWidgetContainer.getCurrentPage()) { 738 // Switch back to clock view if music was showing. 739 mAppWidgetContainer 740 .setCurrentPage(getWidgetPosition(R.id.keyguard_status_view)); 741 } 742 mAppWidgetContainer.removeView(mTransportControl); 743 // XXX keep view attached to hierarchy so we still get show/hide events 744 // from AudioManager 745 KeyguardHostView.this.addView(mTransportControl); 746 mTransportControl.setVisibility(View.GONE); 747 } 748 } 749 750 @Override 751 public void show() { 752 if (getWidgetPosition(R.id.keyguard_transport_control) == -1) { 753 KeyguardHostView.this.removeView(mTransportControl); 754 mAppWidgetContainer.addView(mTransportControl, 755 getWidgetPosition(R.id.keyguard_status_view) + 1); 756 mTransportControl.setVisibility(View.VISIBLE); 757 // Once shown, leave it showing 758 mSticky = true; 759 } 760 } 761 }); 762 } 763 } 764 765 private void maybePopulateWidgets() { 766 DevicePolicyManager dpm = 767 (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); 768 if (dpm != null) { 769 final int currentUser = mLockPatternUtils.getCurrentUser(); 770 final int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUser); 771 if ((disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0) { 772 Log.v(TAG, "Keyguard widgets disabled because of device policy admin"); 773 return; 774 } 775 } 776 777 // Replace status widget if selected by user in Settings 778 int statusWidgetId = mLockPatternUtils.getStatusWidget(); 779 if (statusWidgetId != -1) { 780 addWidget(statusWidgetId); 781 View newStatusWidget = mAppWidgetContainer.getChildAt( 782 mAppWidgetContainer.getChildCount() - 1); 783 784 int oldStatusWidgetPosition = getWidgetPosition(R.id.keyguard_status_view); 785 mAppWidgetContainer.removeViewAt(oldStatusWidgetPosition); 786 787 // Re-add new status widget at position of old one 788 mAppWidgetContainer.removeView(newStatusWidget); 789 newStatusWidget.setId(R.id.keyguard_status_view); 790 mAppWidgetContainer.addView(newStatusWidget, oldStatusWidgetPosition); 791 } 792 793 // Add user-selected widget 794 final int[] widgets = mLockPatternUtils.getUserDefinedWidgets(); 795 for (int i = 0; i < widgets.length; i++) { 796 if (widgets[i] != -1) { 797 addWidget(widgets[i]); 798 } 799 } 800 } 801 802 private void showAppropriateWidgetPage() { 803 int page = mAppWidgetContainer.indexOfChild(findViewById(R.id.keyguard_status_view)); 804 if (mAppWidgetContainer.indexOfChild(mTransportControl) != -1) { 805 page = mAppWidgetContainer.indexOfChild(mTransportControl); 806 } 807 mAppWidgetContainer.setCurrentPage(page); 808 } 809 810 private void inflateAndAddUserSelectorWidgetIfNecessary() { 811 // if there are multiple users, we need to add the multi-user switcher widget to the 812 // keyguard. 813 UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 814 List<UserInfo> users = mUm.getUsers(true); 815 816 if (users.size() > 1) { 817 KeyguardWidgetFrame userSwitcher = (KeyguardWidgetFrame) 818 LayoutInflater.from(mContext).inflate(R.layout.keyguard_multi_user_selector_widget, 819 mAppWidgetContainer, false); 820 821 // add the switcher in the first position 822 mAppWidgetContainer.addView(userSwitcher, getWidgetPosition(R.id.keyguard_status_view)); 823 KeyguardMultiUserSelectorView multiUser = (KeyguardMultiUserSelectorView) 824 userSwitcher.getChildAt(0); 825 826 UserSwitcherCallback callback = new UserSwitcherCallback() { 827 @Override 828 public void hideSecurityView(int duration) { 829 mSecurityViewContainer.animate().alpha(0).setDuration(duration); 830 } 831 832 @Override 833 public void showSecurityView() { 834 mSecurityViewContainer.setAlpha(1.0f); 835 } 836 837 @Override 838 public void showUnlockHint() { 839 mKeyguardSelectorView.ping(); 840 } 841 842 }; 843 multiUser.setCallback(callback); 844 } 845 } 846 847 @Override 848 public void cleanUp() { 849 850 } 851 852 /** 853 * In general, we enable unlocking the insecure keyguard with the menu key. However, there are 854 * some cases where we wish to disable it, notably when the menu button placement or technology 855 * is prone to false positives. 856 * 857 * @return true if the menu key should be enabled 858 */ 859 private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key"; 860 private boolean shouldEnableMenuKey() { 861 final Resources res = getResources(); 862 final boolean configDisabled = res.getBoolean( 863 com.android.internal.R.bool.config_disableMenuKeyInLockScreen); 864 final boolean isTestHarness = ActivityManager.isRunningInTestHarness(); 865 final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists(); 866 return !configDisabled || isTestHarness || fileOverride; 867 } 868 869 @Override 870 public boolean onKeyDown(int keyCode, KeyEvent event) { 871 if (keyCode == KeyEvent.KEYCODE_MENU && mEnableMenuKey) { 872 showNextSecurityScreenOrFinish(false); 873 return true; 874 } else { 875 return super.onKeyDown(keyCode, event); 876 } 877 } 878 879 public void goToUserSwitcher() { 880 mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_multi_user_selector)); 881 } 882 883 public boolean handleBackKey() { 884 if (mCurrentSecuritySelection != SecurityMode.None) { 885 mCallback.dismiss(false); 886 return true; 887 } 888 return false; 889 } 890 891} 892