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