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