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