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