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