KeyguardHostView.java revision 26e6c2e44235428717d7152b9e6398649452f1d2
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 view = (KeyguardSecurityView)v; 629 } 630 631 if (view instanceof KeyguardSelectorView) { 632 KeyguardSelectorView selectorView = (KeyguardSelectorView) view; 633 View carrierText = selectorView.findViewById(R.id.keyguard_selector_fade_container); 634 selectorView.setCarrierArea(carrierText); 635 } 636 637 return view; 638 } 639 640 /** 641 * Switches to the given security view unless it's already being shown, in which case 642 * this is a no-op. 643 * 644 * @param securityMode 645 */ 646 private void showSecurityScreen(SecurityMode securityMode) { 647 if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")"); 648 649 if (securityMode == mCurrentSecuritySelection) return; 650 651 KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection); 652 KeyguardSecurityView newView = getSecurityView(securityMode); 653 654 // Enter full screen mode if we're in SIM or Account screen 655 boolean fullScreenEnabled = getResources().getBoolean( 656 com.android.internal.R.bool.kg_sim_puk_account_full_screen); 657 boolean isSimOrAccount = securityMode == SecurityMode.SimPin 658 || securityMode == SecurityMode.SimPuk 659 || securityMode == SecurityMode.Account; 660 mAppWidgetContainer.setVisibility( 661 isSimOrAccount && fullScreenEnabled ? View.GONE : View.VISIBLE); 662 663 // Emulate Activity life cycle 664 if (oldView != null) { 665 oldView.onPause(); 666 oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view 667 } 668 newView.onResume(); 669 newView.setKeyguardCallback(mCallback); 670 671 final boolean needsInput = newView.needsInput(); 672 if (mViewMediatorCallback != null) { 673 mViewMediatorCallback.setNeedsInput(needsInput); 674 } 675 676 // Find and show this child. 677 final int childCount = mSecurityViewContainer.getChildCount(); 678 679 // Do flip animation to the next screen 680 if (false) { 681 mSecurityViewContainer.setInAnimation( 682 AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_animate_in)); 683 mSecurityViewContainer.setOutAnimation( 684 AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_animate_out)); 685 } 686 final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); 687 for (int i = 0; i < childCount; i++) { 688 if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) { 689 mSecurityViewContainer.setDisplayedChild(i); 690 break; 691 } 692 } 693 694 if (securityMode == SecurityMode.None) { 695 // Discard current runnable if we're switching back to the selector view 696 setOnDismissRunnable(null); 697 } 698 mCurrentSecuritySelection = securityMode; 699 } 700 701 @Override 702 public void onScreenTurnedOn() { 703 if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode())); 704 showPrimarySecurityScreen(false); 705 getSecurityView(mCurrentSecuritySelection).onResume(); 706 707 // This is a an attempt to fix bug 7137389 where the device comes back on but the entire 708 // layout is blank but forcing a layout causes it to reappear (e.g. with with 709 // hierarchyviewer). 710 requestLayout(); 711 712 if (mViewStateManager != null) { 713 mViewStateManager.showUsabilityHints(); 714 } 715 } 716 717 @Override 718 public void onScreenTurnedOff() { 719 if (DEBUG) Log.d(TAG, "screen off, instance " + Integer.toHexString(hashCode())); 720 saveStickyWidgetIndex(); 721 showPrimarySecurityScreen(true); 722 getSecurityView(mCurrentSecuritySelection).onPause(); 723 } 724 725 @Override 726 public void show() { 727 if (DEBUG) Log.d(TAG, "show()"); 728 showPrimarySecurityScreen(false); 729 } 730 731 private boolean isSecure() { 732 SecurityMode mode = mSecurityModel.getSecurityMode(); 733 switch (mode) { 734 case Pattern: 735 return mLockPatternUtils.isLockPatternEnabled(); 736 case Password: 737 case PIN: 738 return mLockPatternUtils.isLockPasswordEnabled(); 739 case SimPin: 740 case SimPuk: 741 case Account: 742 return true; 743 case None: 744 return false; 745 default: 746 throw new IllegalStateException("Unknown security mode " + mode); 747 } 748 } 749 750 @Override 751 public void wakeWhenReadyTq(int keyCode) { 752 if (DEBUG) Log.d(TAG, "onWakeKey"); 753 if (keyCode == KeyEvent.KEYCODE_MENU && isSecure()) { 754 if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU"); 755 showSecurityScreen(SecurityMode.None); 756 } else { 757 if (DEBUG) Log.d(TAG, "poking wake lock immediately"); 758 } 759 if (mViewMediatorCallback != null) { 760 mViewMediatorCallback.wakeUp(); 761 } 762 } 763 764 @Override 765 public void verifyUnlock() { 766 SecurityMode securityMode = mSecurityModel.getSecurityMode(); 767 if (securityMode == KeyguardSecurityModel.SecurityMode.None) { 768 if (mViewMediatorCallback != null) { 769 mViewMediatorCallback.keyguardDone(true); 770 } 771 } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern 772 && securityMode != KeyguardSecurityModel.SecurityMode.PIN 773 && securityMode != KeyguardSecurityModel.SecurityMode.Password) { 774 // can only verify unlock when in pattern/password mode 775 if (mViewMediatorCallback != null) { 776 mViewMediatorCallback.keyguardDone(false); 777 } 778 } else { 779 // otherwise, go to the unlock screen, see if they can verify it 780 mIsVerifyUnlockOnly = true; 781 showSecurityScreen(securityMode); 782 } 783 } 784 785 private int getSecurityViewIdForMode(SecurityMode securityMode) { 786 switch (securityMode) { 787 case None: return R.id.keyguard_selector_view; 788 case Pattern: return R.id.keyguard_pattern_view; 789 case PIN: return R.id.keyguard_pin_view; 790 case Password: return R.id.keyguard_password_view; 791 case Biometric: return R.id.keyguard_face_unlock_view; 792 case Account: return R.id.keyguard_account_view; 793 case SimPin: return R.id.keyguard_sim_pin_view; 794 case SimPuk: return R.id.keyguard_sim_puk_view; 795 } 796 return 0; 797 } 798 799 private int getLayoutIdFor(SecurityMode securityMode) { 800 switch (securityMode) { 801 case None: return R.layout.keyguard_selector_view; 802 case Pattern: return R.layout.keyguard_pattern_view; 803 case PIN: return R.layout.keyguard_pin_view; 804 case Password: return R.layout.keyguard_password_view; 805 case Biometric: return R.layout.keyguard_face_unlock_view; 806 case Account: return R.layout.keyguard_account_view; 807 case SimPin: return R.layout.keyguard_sim_pin_view; 808 case SimPuk: return R.layout.keyguard_sim_puk_view; 809 default: 810 return 0; 811 } 812 } 813 814 private void addWidget(int appId, int pageIndex) { 815 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext); 816 AppWidgetProviderInfo appWidgetInfo = appWidgetManager.getAppWidgetInfo(appId); 817 if (appWidgetInfo != null) { 818 AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo); 819 addWidget(view, pageIndex); 820 } else { 821 Log.w(TAG, "AppWidgetInfo for app widget id " + appId + " was null, deleting"); 822 mLockPatternUtils.removeAppWidget(appId); 823 } 824 } 825 826 private final CameraWidgetFrame.Callbacks mCameraWidgetCallbacks = 827 new CameraWidgetFrame.Callbacks() { 828 @Override 829 public void onLaunchingCamera() { 830 SlidingChallengeLayout slider = locateSlider(); 831 if (slider != null) { 832 slider.showHandle(false); 833 } 834 } 835 836 @Override 837 public void onCameraLaunched() { 838 if (isCameraPage(mAppWidgetContainer.getCurrentPage())) { 839 mAppWidgetContainer.scrollLeft(); 840 } 841 SlidingChallengeLayout slider = locateSlider(); 842 if (slider != null) { 843 slider.showHandle(true); 844 slider.showChallenge(true); 845 } 846 } 847 848 private SlidingChallengeLayout locateSlider() { 849 return (SlidingChallengeLayout) findViewById(R.id.sliding_layout); 850 } 851 }; 852 853 private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() { 854 @Override 855 Context getContext() { 856 return mContext; 857 } 858 859 @Override 860 KeyguardSecurityCallback getCallback() { 861 return mCallback; 862 } 863 864 @Override 865 LockPatternUtils getLockPatternUtils() { 866 return mLockPatternUtils; 867 }}; 868 869 private void addDefaultWidgets() { 870 LayoutInflater inflater = LayoutInflater.from(mContext); 871 inflater.inflate(R.layout.keyguard_transport_control_view, this, true); 872 873 View addWidget = inflater.inflate(R.layout.keyguard_add_widget, null, true); 874 mAppWidgetContainer.addWidget(addWidget); 875 if (mContext.getResources().getBoolean(R.bool.kg_enable_camera_default_widget)) { 876 View cameraWidget = 877 CameraWidgetFrame.create(mContext, mCameraWidgetCallbacks, mActivityLauncher); 878 if (cameraWidget != null) { 879 mAppWidgetContainer.addWidget(cameraWidget); 880 } 881 } 882 883 View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view); 884 addWidgetButton.setOnClickListener(new OnClickListener() { 885 @Override 886 public void onClick(View v) { 887 mCallback.setOnDismissRunnable(new Runnable() { 888 889 @Override 890 public void run() { 891 Intent intent = new Intent(Settings.ACTION_SECURITY_SETTINGS); 892 intent.addFlags( 893 Intent.FLAG_ACTIVITY_NEW_TASK 894 | Intent.FLAG_ACTIVITY_SINGLE_TOP 895 | Intent.FLAG_ACTIVITY_CLEAR_TOP 896 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 897 mContext.startActivityAsUser(intent, 898 new UserHandle(UserHandle.USER_CURRENT)); 899 } 900 }); 901 mCallback.dismiss(false); 902 } 903 }); 904 905 enableUserSelectorIfNecessary(); 906 initializeTransportControl(); 907 } 908 909 private void initializeTransportControl() { 910 mTransportControl = 911 (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control); 912 mTransportControl.setVisibility(View.GONE); 913 914 // This code manages showing/hiding the transport control. We keep it around and only 915 // add it to the hierarchy if it needs to be present. 916 if (mTransportControl != null) { 917 mTransportControl.setKeyguardCallback(new TransportCallback() { 918 @Override 919 public void onListenerDetached() { 920 int page = getWidgetPosition(R.id.keyguard_transport_control); 921 if (page != -1) { 922 mAppWidgetContainer.removeView(mTransportControl); 923 // XXX keep view attached so we still get show/hide events from AudioManager 924 KeyguardHostView.this.addView(mTransportControl); 925 mTransportControl.setVisibility(View.GONE); 926 mTransportState = TRANSPORT_GONE; 927 mTransportControl.post(mSwitchPageRunnable); 928 } 929 } 930 931 @Override 932 public void onListenerAttached() { 933 if (getWidgetPosition(R.id.keyguard_transport_control) == -1) { 934 KeyguardHostView.this.removeView(mTransportControl); 935 mAppWidgetContainer.addView(mTransportControl, 0); 936 mTransportControl.setVisibility(View.VISIBLE); 937 } 938 } 939 940 @Override 941 public void onPlayStateChanged() { 942 mTransportControl.post(mSwitchPageRunnable); 943 } 944 }); 945 } 946 } 947 948 private void addWidgetsFromSettings() { 949 DevicePolicyManager dpm = 950 (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); 951 if (dpm != null) { 952 final int currentUser = mLockPatternUtils.getCurrentUser(); 953 final int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUser); 954 if ((disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0) { 955 Log.v(TAG, "Keyguard widgets disabled because of device policy admin"); 956 return; 957 } 958 } 959 960 View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget); 961 int addPageIndex = mAppWidgetContainer.indexOfChild(addWidget); 962 // This shouldn't happen, but just to be safe! 963 if (addPageIndex < 0) { 964 addPageIndex = 0; 965 } 966 967 // Add user-selected widget 968 final int[] widgets = mLockPatternUtils.getAppWidgets(); 969 if (widgets == null) { 970 Log.d(TAG, "Problem reading widgets"); 971 } else { 972 for (int i = widgets.length -1; i >= 0; i--) { 973 if (widgets[i] == LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) { 974 LayoutInflater inflater = LayoutInflater.from(mContext); 975 View statusWidget = inflater.inflate(R.layout.keyguard_status_view, null, true); 976 mAppWidgetContainer.addWidget(statusWidget, addPageIndex + 1); 977 } else { 978 // We add the widgets from left to right, starting after the first page after 979 // the add page. We count down, since the order will be persisted from right 980 // to left, starting after camera. 981 addWidget(widgets[i], addPageIndex + 1); 982 } 983 } 984 } 985 } 986 987 Runnable mSwitchPageRunnable = new Runnable() { 988 @Override 989 public void run() { 990 showAppropriateWidgetPage(); 991 } 992 }; 993 994 static class SavedState extends BaseSavedState { 995 int transportState; 996 997 SavedState(Parcelable superState) { 998 super(superState); 999 } 1000 1001 private SavedState(Parcel in) { 1002 super(in); 1003 this.transportState = in.readInt(); 1004 } 1005 1006 @Override 1007 public void writeToParcel(Parcel out, int flags) { 1008 super.writeToParcel(out, flags); 1009 out.writeInt(this.transportState); 1010 } 1011 1012 public static final Parcelable.Creator<SavedState> CREATOR 1013 = new Parcelable.Creator<SavedState>() { 1014 public SavedState createFromParcel(Parcel in) { 1015 return new SavedState(in); 1016 } 1017 1018 public SavedState[] newArray(int size) { 1019 return new SavedState[size]; 1020 } 1021 }; 1022 } 1023 1024 @Override 1025 public Parcelable onSaveInstanceState() { 1026 if (DEBUG) Log.d(TAG, "onSaveInstanceState"); 1027 saveStickyWidgetIndex(); 1028 Parcelable superState = super.onSaveInstanceState(); 1029 SavedState ss = new SavedState(superState); 1030 ss.transportState = mTransportState; 1031 return ss; 1032 } 1033 1034 @Override 1035 public void onRestoreInstanceState(Parcelable state) { 1036 if (DEBUG) Log.d(TAG, "onRestoreInstanceState"); 1037 if (!(state instanceof SavedState)) { 1038 super.onRestoreInstanceState(state); 1039 return; 1040 } 1041 SavedState ss = (SavedState) state; 1042 super.onRestoreInstanceState(ss.getSuperState()); 1043 mTransportState = ss.transportState; 1044 post(mSwitchPageRunnable); 1045 } 1046 1047 @Override 1048 public void onWindowFocusChanged(boolean hasWindowFocus) { 1049 super.onWindowFocusChanged(hasWindowFocus); 1050 if (DEBUG) Log.d(TAG, "Window is " + (hasWindowFocus ? "focused" : "unfocused")); 1051 if (!hasWindowFocus) { 1052 saveStickyWidgetIndex(); 1053 } 1054 } 1055 1056 private void showAppropriateWidgetPage() { 1057 boolean isMusicPlaying = 1058 mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE; 1059 if (isMusicPlaying) { 1060 mTransportState = TRANSPORT_VISIBLE; 1061 } else if (mTransportState == TRANSPORT_VISIBLE) { 1062 mTransportState = TRANSPORT_INVISIBLE; 1063 } 1064 int pageToShow = getAppropriateWidgetPage(isMusicPlaying); 1065 mAppWidgetContainer.setCurrentPage(pageToShow); 1066 } 1067 1068 private boolean isCameraPage(int pageIndex) { 1069 View v = mAppWidgetContainer.getChildAt(pageIndex); 1070 return v != null && v instanceof CameraWidgetFrame; 1071 } 1072 1073 private boolean isAddPage(int pageIndex) { 1074 View v = mAppWidgetContainer.getChildAt(pageIndex); 1075 return v != null && v.getId() == R.id.keyguard_add_widget; 1076 } 1077 1078 private int getAppropriateWidgetPage(boolean isMusicPlaying) { 1079 // assumes at least one widget (besides camera + add) 1080 1081 // if music playing, show transport 1082 if (isMusicPlaying) { 1083 if (DEBUG) Log.d(TAG, "Music playing, show transport"); 1084 return mAppWidgetContainer.indexOfChild(mTransportControl); 1085 } 1086 1087 // if we have a valid sticky widget, show it 1088 int stickyWidgetIndex = mLockPatternUtils.getStickyAppWidgetIndex(); 1089 if (stickyWidgetIndex > -1 1090 && stickyWidgetIndex < mAppWidgetContainer.getChildCount() 1091 && !isAddPage(stickyWidgetIndex) 1092 && !isCameraPage(stickyWidgetIndex)) { 1093 if (DEBUG) Log.d(TAG, "Valid sticky widget found, show page " + stickyWidgetIndex); 1094 return stickyWidgetIndex; 1095 } 1096 1097 // else show the right-most widget (except for camera) 1098 int rightMost = mAppWidgetContainer.getChildCount() - 1; 1099 if (isCameraPage(rightMost)) { 1100 rightMost--; 1101 } 1102 if (DEBUG) Log.d(TAG, "Show right-most page " + rightMost); 1103 return rightMost; 1104 } 1105 1106 private void saveStickyWidgetIndex() { 1107 int stickyWidgetIndex = mAppWidgetContainer.getCurrentPage(); 1108 if (isAddPage(stickyWidgetIndex)) { 1109 stickyWidgetIndex++; 1110 } 1111 if (isCameraPage(stickyWidgetIndex)) { 1112 stickyWidgetIndex--; 1113 } 1114 if (stickyWidgetIndex < 0 || stickyWidgetIndex >= mAppWidgetContainer.getChildCount()) { 1115 stickyWidgetIndex = -1; 1116 } 1117 if (DEBUG) Log.d(TAG, "saveStickyWidgetIndex: " + stickyWidgetIndex); 1118 mLockPatternUtils.setStickyAppWidgetIndex(stickyWidgetIndex); 1119 } 1120 1121 private void enableUserSelectorIfNecessary() { 1122 if (!UserManager.supportsMultipleUsers()) { 1123 return; // device doesn't support multi-user mode 1124 } 1125 1126 // if there are multiple users, we need to enable to multi-user switcher 1127 UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 1128 List<UserInfo> users = mUm.getUsers(true); 1129 1130 if (users.size() > 1) { 1131 KeyguardMultiUserSelectorView multiUser = 1132 (KeyguardMultiUserSelectorView) findViewById(R.id.keyguard_user_selector); 1133 multiUser.setVisibility(View.VISIBLE); 1134 multiUser.addUsers(mUm.getUsers(true)); 1135 UserSwitcherCallback callback = new UserSwitcherCallback() { 1136 @Override 1137 public void hideSecurityView(int duration) { 1138 mSecurityViewContainer.animate().alpha(0).setDuration(duration); 1139 } 1140 1141 @Override 1142 public void showSecurityView() { 1143 mSecurityViewContainer.setAlpha(1.0f); 1144 } 1145 1146 @Override 1147 public void showUnlockHint() { 1148 if (mKeyguardSelectorView != null) { 1149 mKeyguardSelectorView.showUsabilityHint(); 1150 } 1151 } 1152 1153 @Override 1154 public void userActivity() { 1155 if (mViewMediatorCallback != null) { 1156 mViewMediatorCallback.userActivity(); 1157 } 1158 } 1159 }; 1160 multiUser.setCallback(callback); 1161 } 1162 } 1163 1164 @Override 1165 public void cleanUp() { 1166 1167 } 1168 1169 /** 1170 * In general, we enable unlocking the insecure keyguard with the menu key. However, there are 1171 * some cases where we wish to disable it, notably when the menu button placement or technology 1172 * is prone to false positives. 1173 * 1174 * @return true if the menu key should be enabled 1175 */ 1176 private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key"; 1177 private boolean shouldEnableMenuKey() { 1178 final Resources res = getResources(); 1179 final boolean configDisabled = res.getBoolean( 1180 com.android.internal.R.bool.config_disableMenuKeyInLockScreen); 1181 final boolean isTestHarness = ActivityManager.isRunningInTestHarness(); 1182 final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists(); 1183 return !configDisabled || isTestHarness || fileOverride; 1184 } 1185 1186 @Override 1187 public boolean onKeyDown(int keyCode, KeyEvent event) { 1188 if (keyCode == KeyEvent.KEYCODE_MENU && mEnableMenuKey) { 1189 showNextSecurityScreenOrFinish(false); 1190 return true; 1191 } else { 1192 return super.onKeyDown(keyCode, event); 1193 } 1194 } 1195 1196 public void goToUserSwitcher() { 1197 mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_multi_user_selector)); 1198 } 1199 1200 public boolean handleBackKey() { 1201 if (mCurrentSecuritySelection != SecurityMode.None) { 1202 mCallback.dismiss(false); 1203 return true; 1204 } 1205 return false; 1206 } 1207 1208} 1209