KeyguardHostView.java revision 14adfe425e67c9ec12b0841a62c5c03ce00838ea
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.SearchManager; 24import android.app.admin.DevicePolicyManager; 25import android.appwidget.AppWidgetHost; 26import android.appwidget.AppWidgetHostView; 27import android.appwidget.AppWidgetManager; 28import android.appwidget.AppWidgetProviderInfo; 29import android.content.ActivityNotFoundException; 30import android.content.ComponentName; 31import android.content.Context; 32import android.content.Intent; 33import android.content.IntentSender; 34import android.content.pm.UserInfo; 35import android.content.res.Resources; 36import android.graphics.Canvas; 37import android.graphics.Rect; 38import android.os.Looper; 39import android.os.Parcel; 40import android.os.Parcelable; 41import android.os.SystemClock; 42import android.os.UserHandle; 43import android.os.UserManager; 44import android.util.AttributeSet; 45import android.util.Log; 46import android.util.Slog; 47import android.view.KeyEvent; 48import android.view.LayoutInflater; 49import android.view.MotionEvent; 50import android.view.View; 51import android.view.WindowManager; 52import android.view.animation.AnimationUtils; 53import android.widget.RemoteViews.OnClickHandler; 54 55import com.android.internal.R; 56import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode; 57import com.android.internal.widget.LockPatternUtils; 58 59import java.io.File; 60import java.util.List; 61 62public class KeyguardHostView extends KeyguardViewBase { 63 private static final String TAG = "KeyguardHostView"; 64 65 // Use this to debug all of keyguard 66 public static boolean DEBUG = KeyguardViewMediator.DEBUG; 67 68 // Found in KeyguardAppWidgetPickActivity.java 69 static final int APPWIDGET_HOST_ID = 0x4B455947; 70 71 private final int MAX_WIDGETS = 5; 72 73 private AppWidgetHost mAppWidgetHost; 74 private AppWidgetManager mAppWidgetManager; 75 private KeyguardWidgetPager mAppWidgetContainer; 76 private KeyguardSecurityViewFlipper mSecurityViewContainer; 77 private KeyguardSelectorView mKeyguardSelectorView; 78 private KeyguardTransportControlView mTransportControl; 79 private boolean mIsVerifyUnlockOnly; 80 private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView 81 private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid; 82 private int mAppWidgetToShow; 83 84 private boolean mCheckAppWidgetConsistencyOnBootCompleted = false; 85 86 protected OnDismissAction mDismissAction; 87 88 protected int mFailedAttempts; 89 private LockPatternUtils mLockPatternUtils; 90 91 private KeyguardSecurityModel mSecurityModel; 92 private KeyguardViewStateManager mViewStateManager; 93 94 boolean mPersitentStickyWidgetLoaded = false; 95 96 private Rect mTempRect = new Rect(); 97 98 private int mDisabledFeatures; 99 100 private boolean mCameraDisabled; 101 102 private boolean mSafeModeEnabled; 103 104 /*package*/ interface TransportCallback { 105 void onListenerDetached(); 106 void onListenerAttached(); 107 void onPlayStateChanged(); 108 } 109 110 /*package*/ interface UserSwitcherCallback { 111 void hideSecurityView(int duration); 112 void showSecurityView(); 113 void showUnlockHint(); 114 void userActivity(); 115 } 116 117 /*package*/ interface OnDismissAction { 118 /* returns true if the dismiss should be deferred */ 119 boolean onDismiss(); 120 } 121 122 public KeyguardHostView(Context context) { 123 this(context, null); 124 } 125 126 public KeyguardHostView(Context context, AttributeSet attrs) { 127 super(context, attrs); 128 mLockPatternUtils = new LockPatternUtils(context); 129 mAppWidgetHost = new AppWidgetHost( 130 context, APPWIDGET_HOST_ID, mOnClickHandler, Looper.myLooper()); 131 mAppWidgetManager = AppWidgetManager.getInstance(mContext); 132 mSecurityModel = new KeyguardSecurityModel(context); 133 134 mViewStateManager = new KeyguardViewStateManager(this); 135 136 DevicePolicyManager dpm = 137 (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); 138 if (dpm != null) { 139 mDisabledFeatures = getDisabledFeatures(dpm); 140 mCameraDisabled = dpm.getCameraDisabled(null); 141 } 142 143 mSafeModeEnabled = LockPatternUtils.isSafeModeEnabled(); 144 145 if (mSafeModeEnabled) { 146 Log.v(TAG, "Keyguard widgets disabled by safe mode"); 147 } 148 if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0) { 149 Log.v(TAG, "Keyguard widgets disabled by DPM"); 150 } 151 if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0) { 152 Log.v(TAG, "Keyguard secure camera disabled by DPM"); 153 } 154 } 155 156 private KeyguardUpdateMonitorCallback mUpdateMonitorCallbacks = 157 new KeyguardUpdateMonitorCallback() { 158 @Override 159 public void onBootCompleted() { 160 if (mCheckAppWidgetConsistencyOnBootCompleted) { 161 checkAppWidgetConsistency(); 162 mSwitchPageRunnable.run(); 163 mCheckAppWidgetConsistencyOnBootCompleted = false; 164 } 165 } 166 }; 167 168 private SlidingChallengeLayout mSlidingChallengeLayout; 169 170 @Override 171 public boolean onTouchEvent(MotionEvent ev) { 172 boolean result = super.onTouchEvent(ev); 173 mTempRect.set(0, 0, 0, 0); 174 offsetRectIntoDescendantCoords(mSecurityViewContainer, mTempRect); 175 ev.offsetLocation(mTempRect.left, mTempRect.top); 176 result = mSecurityViewContainer.dispatchTouchEvent(ev) || result; 177 ev.offsetLocation(-mTempRect.left, -mTempRect.top); 178 return result; 179 } 180 181 @Override 182 protected void dispatchDraw(Canvas canvas) { 183 super.dispatchDraw(canvas); 184 if (mViewMediatorCallback != null) { 185 mViewMediatorCallback.keyguardDoneDrawing(); 186 } 187 } 188 189 private int getWidgetPosition(int id) { 190 final int children = mAppWidgetContainer.getChildCount(); 191 for (int i = 0; i < children; i++) { 192 if (mAppWidgetContainer.getWidgetPageAt(i).getContent().getId() == id) { 193 return i; 194 } 195 } 196 return -1; 197 } 198 199 @Override 200 protected void onFinishInflate() { 201 // Grab instances of and make any necessary changes to the main layouts. Create 202 // view state manager and wire up necessary listeners / callbacks. 203 View deleteDropTarget = findViewById(R.id.keyguard_widget_pager_delete_target); 204 mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container); 205 mAppWidgetContainer.setVisibility(VISIBLE); 206 mAppWidgetContainer.setCallbacks(mWidgetCallbacks); 207 mAppWidgetContainer.setDeleteDropTarget(deleteDropTarget); 208 mAppWidgetContainer.setMinScale(0.5f); 209 210 mSlidingChallengeLayout = (SlidingChallengeLayout) findViewById(R.id.sliding_layout); 211 if (mSlidingChallengeLayout != null) { 212 mSlidingChallengeLayout.setOnChallengeScrolledListener(mViewStateManager); 213 } 214 mAppWidgetContainer.setViewStateManager(mViewStateManager); 215 mAppWidgetContainer.setLockPatternUtils(mLockPatternUtils); 216 217 ChallengeLayout challenge = mSlidingChallengeLayout != null ? mSlidingChallengeLayout : 218 (ChallengeLayout) findViewById(R.id.multi_pane_challenge); 219 challenge.setOnBouncerStateChangedListener(mViewStateManager); 220 mAppWidgetContainer.setBouncerAnimationDuration(challenge.getBouncerAnimationDuration()); 221 mViewStateManager.setPagedView(mAppWidgetContainer); 222 mViewStateManager.setChallengeLayout(challenge); 223 mSecurityViewContainer = (KeyguardSecurityViewFlipper) findViewById(R.id.view_flipper); 224 mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view); 225 mViewStateManager.setSecurityViewContainer(mSecurityViewContainer); 226 227 if (!(mContext instanceof Activity)) { 228 setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK); 229 } 230 231 addDefaultWidgets(); 232 233 addWidgetsFromSettings(); 234 if (numWidgets() >= MAX_WIDGETS) { 235 setAddWidgetEnabled(false); 236 } 237 checkAppWidgetConsistency(); 238 mSwitchPageRunnable.run(); 239 // This needs to be called after the pages are all added. 240 mViewStateManager.showUsabilityHints(); 241 242 showPrimarySecurityScreen(false); 243 updateSecurityViews(); 244 } 245 246 private int getDisabledFeatures(DevicePolicyManager dpm) { 247 int disabledFeatures = DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE; 248 if (dpm != null) { 249 final int currentUser = mLockPatternUtils.getCurrentUser(); 250 disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUser); 251 } 252 return disabledFeatures; 253 } 254 255 private boolean widgetsDisabledByDpm() { 256 return (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0; 257 } 258 259 private boolean cameraDisabledByDpm() { 260 return mCameraDisabled 261 || (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0; 262 } 263 264 private void updateSecurityViews() { 265 int children = mSecurityViewContainer.getChildCount(); 266 for (int i = 0; i < children; i++) { 267 updateSecurityView(mSecurityViewContainer.getChildAt(i)); 268 } 269 } 270 271 private void updateSecurityView(View view) { 272 if (view instanceof KeyguardSecurityView) { 273 KeyguardSecurityView ksv = (KeyguardSecurityView) view; 274 ksv.setKeyguardCallback(mCallback); 275 ksv.setLockPatternUtils(mLockPatternUtils); 276 if (mViewStateManager.isBouncing()) { 277 ksv.showBouncer(0); 278 } else { 279 ksv.hideBouncer(0); 280 } 281 } else { 282 Log.w(TAG, "View " + view + " is not a KeyguardSecurityView"); 283 } 284 } 285 286 void setLockPatternUtils(LockPatternUtils utils) { 287 mSecurityModel.setLockPatternUtils(utils); 288 mLockPatternUtils = utils; 289 updateSecurityViews(); 290 } 291 292 @Override 293 protected void onAttachedToWindow() { 294 super.onAttachedToWindow(); 295 mAppWidgetHost.startListening(); 296 KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallbacks); 297 } 298 299 @Override 300 protected void onDetachedFromWindow() { 301 super.onDetachedFromWindow(); 302 mAppWidgetHost.stopListening(); 303 KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallbacks); 304 } 305 306 private AppWidgetHost getAppWidgetHost() { 307 return mAppWidgetHost; 308 } 309 310 void addWidget(AppWidgetHostView view, int pageIndex) { 311 mAppWidgetContainer.addWidget(view, pageIndex); 312 } 313 314 private KeyguardWidgetPager.Callbacks mWidgetCallbacks 315 = new KeyguardWidgetPager.Callbacks() { 316 @Override 317 public void userActivity() { 318 KeyguardHostView.this.userActivity(); 319 } 320 321 @Override 322 public void onUserActivityTimeoutChanged() { 323 KeyguardHostView.this.onUserActivityTimeoutChanged(); 324 } 325 326 @Override 327 public void onRemoveView(View v) { 328 if (numWidgets() < MAX_WIDGETS) { 329 setAddWidgetEnabled(true); 330 } 331 } 332 }; 333 334 public void userActivity() { 335 if (mViewMediatorCallback != null) { 336 mViewMediatorCallback.userActivity(); 337 } 338 } 339 340 public void onUserActivityTimeoutChanged() { 341 if (mViewMediatorCallback != null) { 342 mViewMediatorCallback.onUserActivityTimeoutChanged(); 343 } 344 } 345 346 @Override 347 public long getUserActivityTimeout() { 348 // Currently only considering user activity timeouts needed by widgets. 349 // Could also take into account longer timeouts for certain security views. 350 if (mAppWidgetContainer != null) { 351 return mAppWidgetContainer.getUserActivityTimeout(); 352 } 353 return -1; 354 } 355 356 private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() { 357 358 public void userActivity(long timeout) { 359 if (mViewMediatorCallback != null) { 360 mViewMediatorCallback.userActivity(timeout); 361 } 362 } 363 364 public void dismiss(boolean authenticated) { 365 showNextSecurityScreenOrFinish(authenticated); 366 } 367 368 public boolean isVerifyUnlockOnly() { 369 return mIsVerifyUnlockOnly; 370 } 371 372 public void reportSuccessfulUnlockAttempt() { 373 KeyguardUpdateMonitor.getInstance(mContext).clearFailedUnlockAttempts(); 374 mLockPatternUtils.reportSuccessfulPasswordAttempt(); 375 } 376 377 public void reportFailedUnlockAttempt() { 378 if (mCurrentSecuritySelection == SecurityMode.Biometric) { 379 KeyguardUpdateMonitor.getInstance(mContext).reportFailedBiometricUnlockAttempt(); 380 } else { 381 KeyguardHostView.this.reportFailedUnlockAttempt(); 382 } 383 } 384 385 public int getFailedAttempts() { 386 return KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(); 387 } 388 389 @Override 390 public void showBackupSecurity() { 391 KeyguardHostView.this.showBackupSecurityScreen(); 392 } 393 394 @Override 395 public void setOnDismissAction(OnDismissAction action) { 396 KeyguardHostView.this.setOnDismissAction(action); 397 } 398 399 }; 400 401 private void showDialog(String title, String message) { 402 final AlertDialog dialog = new AlertDialog.Builder(mContext) 403 .setTitle(title) 404 .setMessage(message) 405 .setNeutralButton(com.android.internal.R.string.ok, null) 406 .create(); 407 if (!(mContext instanceof Activity)) { 408 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 409 } 410 dialog.show(); 411 } 412 413 private void showTimeoutDialog() { 414 int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; 415 int messageId = 0; 416 417 switch (mSecurityModel.getSecurityMode()) { 418 case Pattern: 419 messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message; 420 break; 421 case PIN: 422 messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message; 423 break; 424 case Password: 425 messageId = R.string.kg_too_many_failed_password_attempts_dialog_message; 426 break; 427 } 428 429 if (messageId != 0) { 430 final String message = mContext.getString(messageId, 431 KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(), 432 timeoutInSeconds); 433 showDialog(null, message); 434 } 435 } 436 437 private void showAlmostAtWipeDialog(int attempts, int remaining) { 438 int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; 439 String message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe, 440 attempts, remaining); 441 showDialog(null, message); 442 } 443 444 private void showWipeDialog(int attempts) { 445 String message = mContext.getString(R.string.kg_failed_attempts_now_wiping, attempts); 446 showDialog(null, message); 447 } 448 449 private void showAlmostAtAccountLoginDialog() { 450 final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; 451 final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET 452 - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; 453 String message = mContext.getString(R.string.kg_failed_attempts_almost_at_login, 454 count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds); 455 showDialog(null, message); 456 } 457 458 private void reportFailedUnlockAttempt() { 459 final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); 460 final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time 461 462 if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts); 463 464 SecurityMode mode = mSecurityModel.getSecurityMode(); 465 final boolean usingPattern = mode == KeyguardSecurityModel.SecurityMode.Pattern; 466 467 final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager() 468 .getMaximumFailedPasswordsForWipe(null, mLockPatternUtils.getCurrentUser()); 469 470 final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET 471 - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; 472 473 final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ? 474 (failedAttemptsBeforeWipe - failedAttempts) 475 : Integer.MAX_VALUE; // because DPM returns 0 if no restriction 476 477 boolean showTimeout = false; 478 if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) { 479 // If we reach this code, it means the user has installed a DevicePolicyManager 480 // that requests device wipe after N attempts. Once we get below the grace 481 // period, we'll post this dialog every time as a clear warning until the 482 // bombshell hits and the device is wiped. 483 if (remainingBeforeWipe > 0) { 484 showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe); 485 } else { 486 // Too many attempts. The device will be wiped shortly. 487 Slog.i(TAG, "Too many unlock attempts; device will be wiped!"); 488 showWipeDialog(failedAttempts); 489 } 490 } else { 491 showTimeout = 492 (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0; 493 if (usingPattern && mEnableFallback) { 494 if (failedAttempts == failedAttemptWarning) { 495 showAlmostAtAccountLoginDialog(); 496 showTimeout = false; // don't show both dialogs 497 } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) { 498 mLockPatternUtils.setPermanentlyLocked(true); 499 showSecurityScreen(SecurityMode.Account); 500 // don't show timeout dialog because we show account unlock screen next 501 showTimeout = false; 502 } 503 } 504 } 505 monitor.reportFailedUnlockAttempt(); 506 mLockPatternUtils.reportFailedPasswordAttempt(); 507 if (showTimeout) { 508 showTimeoutDialog(); 509 } 510 } 511 512 /** 513 * Shows the primary security screen for the user. This will be either the multi-selector 514 * or the user's security method. 515 * @param turningOff true if the device is being turned off 516 */ 517 void showPrimarySecurityScreen(boolean turningOff) { 518 SecurityMode securityMode = mSecurityModel.getSecurityMode(); 519 if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")"); 520 if (!turningOff && 521 KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()) { 522 // If we're not turning off, then allow biometric alternate. 523 // We'll reload it when the device comes back on. 524 securityMode = mSecurityModel.getAlternateFor(securityMode); 525 } 526 showSecurityScreen(securityMode); 527 } 528 529 /** 530 * Shows the backup security screen for the current security mode. This could be used for 531 * password recovery screens but is currently only used for pattern unlock to show the 532 * account unlock screen and biometric unlock to show the user's normal unlock. 533 */ 534 private void showBackupSecurityScreen() { 535 if (DEBUG) Log.d(TAG, "showBackupSecurity()"); 536 SecurityMode backup = mSecurityModel.getBackupSecurityMode(mCurrentSecuritySelection); 537 showSecurityScreen(backup); 538 } 539 540 public boolean showNextSecurityScreenIfPresent() { 541 SecurityMode securityMode = mSecurityModel.getSecurityMode(); 542 // Allow an alternate, such as biometric unlock 543 securityMode = mSecurityModel.getAlternateFor(securityMode); 544 if (SecurityMode.None == securityMode) { 545 return false; 546 } else { 547 showSecurityScreen(securityMode); // switch to the alternate security view 548 return true; 549 } 550 } 551 552 private void showNextSecurityScreenOrFinish(boolean authenticated) { 553 if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")"); 554 boolean finish = false; 555 if (SecurityMode.None == mCurrentSecuritySelection) { 556 SecurityMode securityMode = mSecurityModel.getSecurityMode(); 557 // Allow an alternate, such as biometric unlock 558 securityMode = mSecurityModel.getAlternateFor(securityMode); 559 if (SecurityMode.None == securityMode) { 560 finish = true; // no security required 561 } else { 562 showSecurityScreen(securityMode); // switch to the alternate security view 563 } 564 } else if (authenticated) { 565 switch (mCurrentSecuritySelection) { 566 case Pattern: 567 case Password: 568 case PIN: 569 case Account: 570 case Biometric: 571 finish = true; 572 break; 573 574 case SimPin: 575 case SimPuk: 576 // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home 577 SecurityMode securityMode = mSecurityModel.getSecurityMode(); 578 if (securityMode != SecurityMode.None) { 579 showSecurityScreen(securityMode); 580 } else { 581 finish = true; 582 } 583 break; 584 585 default: 586 Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe"); 587 showPrimarySecurityScreen(false); 588 break; 589 } 590 } else { 591 showPrimarySecurityScreen(false); 592 } 593 if (finish) { 594 // If the alternate unlock was suppressed, it can now be safely 595 // enabled because the user has left keyguard. 596 KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true); 597 598 // If there's a pending runnable because the user interacted with a widget 599 // and we're leaving keyguard, then run it. 600 boolean deferKeyguardDone = false; 601 if (mDismissAction != null) { 602 deferKeyguardDone = mDismissAction.onDismiss(); 603 mDismissAction = null; 604 } 605 if (mViewMediatorCallback != null) { 606 if (deferKeyguardDone) { 607 mViewMediatorCallback.keyguardDonePending(); 608 } else { 609 mViewMediatorCallback.keyguardDone(true); 610 } 611 } 612 } else { 613 mViewStateManager.showBouncer(true); 614 } 615 } 616 617 private OnClickHandler mOnClickHandler = new OnClickHandler() { 618 @Override 619 public boolean onClickHandler(final View view, 620 final android.app.PendingIntent pendingIntent, 621 final Intent fillInIntent) { 622 if (pendingIntent.isActivity()) { 623 setOnDismissAction(new OnDismissAction() { 624 public boolean onDismiss() { 625 try { 626 // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT? 627 Context context = view.getContext(); 628 ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view, 629 0, 0, 630 view.getMeasuredWidth(), view.getMeasuredHeight()); 631 context.startIntentSender( 632 pendingIntent.getIntentSender(), fillInIntent, 633 Intent.FLAG_ACTIVITY_NEW_TASK, 634 Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle()); 635 } catch (IntentSender.SendIntentException e) { 636 android.util.Log.e(TAG, "Cannot send pending intent: ", e); 637 } catch (Exception e) { 638 android.util.Log.e(TAG, "Cannot send pending intent due to " + 639 "unknown exception: ", e); 640 } 641 return false; 642 } 643 }); 644 645 if (mViewStateManager.isChallengeShowing()) { 646 mViewStateManager.showBouncer(true); 647 } else { 648 mCallback.dismiss(false); 649 } 650 return true; 651 } else { 652 return super.onClickHandler(view, pendingIntent, fillInIntent); 653 } 654 }; 655 }; 656 657 // Used to ignore callbacks from methods that are no longer current (e.g. face unlock). 658 // This avoids unwanted asynchronous events from messing with the state. 659 private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() { 660 661 @Override 662 public void userActivity(long timeout) { 663 } 664 665 @Override 666 public void showBackupSecurity() { 667 } 668 669 @Override 670 public void setOnDismissAction(OnDismissAction action) { 671 } 672 673 @Override 674 public void reportSuccessfulUnlockAttempt() { 675 } 676 677 @Override 678 public void reportFailedUnlockAttempt() { 679 } 680 681 @Override 682 public boolean isVerifyUnlockOnly() { 683 return false; 684 } 685 686 @Override 687 public int getFailedAttempts() { 688 return 0; 689 } 690 691 @Override 692 public void dismiss(boolean securityVerified) { 693 } 694 }; 695 696 protected boolean mShowSecurityWhenReturn; 697 698 @Override 699 public void reset() { 700 mIsVerifyUnlockOnly = false; 701 mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_status_view)); 702 } 703 704 /** 705 * Sets an action to perform when keyguard is dismissed. 706 * @param action 707 */ 708 protected void setOnDismissAction(OnDismissAction action) { 709 mDismissAction = action; 710 } 711 712 private KeyguardSecurityView getSecurityView(SecurityMode securityMode) { 713 final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); 714 KeyguardSecurityView view = null; 715 final int children = mSecurityViewContainer.getChildCount(); 716 for (int child = 0; child < children; child++) { 717 if (mSecurityViewContainer.getChildAt(child).getId() == securityViewIdForMode) { 718 view = ((KeyguardSecurityView)mSecurityViewContainer.getChildAt(child)); 719 break; 720 } 721 } 722 int layoutId = getLayoutIdFor(securityMode); 723 if (view == null && layoutId != 0) { 724 final LayoutInflater inflater = LayoutInflater.from(mContext); 725 if (DEBUG) Log.v(TAG, "inflating id = " + layoutId); 726 View v = inflater.inflate(layoutId, mSecurityViewContainer, false); 727 mSecurityViewContainer.addView(v); 728 updateSecurityView(v); 729 view = (KeyguardSecurityView)v; 730 } 731 732 if (view instanceof KeyguardSelectorView) { 733 KeyguardSelectorView selectorView = (KeyguardSelectorView) view; 734 View carrierText = selectorView.findViewById(R.id.keyguard_selector_fade_container); 735 selectorView.setCarrierArea(carrierText); 736 } 737 738 return view; 739 } 740 741 /** 742 * Switches to the given security view unless it's already being shown, in which case 743 * this is a no-op. 744 * 745 * @param securityMode 746 */ 747 private void showSecurityScreen(SecurityMode securityMode) { 748 if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")"); 749 750 if (securityMode == mCurrentSecuritySelection) return; 751 752 KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection); 753 KeyguardSecurityView newView = getSecurityView(securityMode); 754 755 // Enter full screen mode if we're in SIM or Account screen 756 boolean fullScreenEnabled = getResources().getBoolean( 757 com.android.internal.R.bool.kg_sim_puk_account_full_screen); 758 boolean isSimOrAccount = securityMode == SecurityMode.SimPin 759 || securityMode == SecurityMode.SimPuk 760 || securityMode == SecurityMode.Account; 761 mAppWidgetContainer.setVisibility( 762 isSimOrAccount && fullScreenEnabled ? View.GONE : View.VISIBLE); 763 764 if (mSlidingChallengeLayout != null) { 765 mSlidingChallengeLayout.setChallengeInteractive(!fullScreenEnabled); 766 } 767 768 // Emulate Activity life cycle 769 if (oldView != null) { 770 oldView.onPause(); 771 oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view 772 } 773 newView.onResume(KeyguardSecurityView.VIEW_REVEALED); 774 newView.setKeyguardCallback(mCallback); 775 776 final boolean needsInput = newView.needsInput(); 777 if (mViewMediatorCallback != null) { 778 mViewMediatorCallback.setNeedsInput(needsInput); 779 } 780 781 // Find and show this child. 782 final int childCount = mSecurityViewContainer.getChildCount(); 783 784 mSecurityViewContainer.setInAnimation( 785 AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_fade_in)); 786 mSecurityViewContainer.setOutAnimation( 787 AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_fade_out)); 788 final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); 789 for (int i = 0; i < childCount; i++) { 790 if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) { 791 mSecurityViewContainer.setDisplayedChild(i); 792 break; 793 } 794 } 795 796 if (securityMode == SecurityMode.None) { 797 // Discard current runnable if we're switching back to the selector view 798 setOnDismissAction(null); 799 } 800 mCurrentSecuritySelection = securityMode; 801 } 802 803 @Override 804 public void onScreenTurnedOn() { 805 if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode())); 806 showPrimarySecurityScreen(false); 807 getSecurityView(mCurrentSecuritySelection).onResume(KeyguardSecurityView.SCREEN_ON); 808 809 // This is a an attempt to fix bug 7137389 where the device comes back on but the entire 810 // layout is blank but forcing a layout causes it to reappear (e.g. with with 811 // hierarchyviewer). 812 requestLayout(); 813 814 if (mViewStateManager != null) { 815 mViewStateManager.showUsabilityHints(); 816 } 817 } 818 819 @Override 820 public void onScreenTurnedOff() { 821 if (DEBUG) Log.d(TAG, String.format("screen off, instance %s at %s", 822 Integer.toHexString(hashCode()), SystemClock.uptimeMillis())); 823 // Once the screen turns off, we no longer consider this to be first boot and we want the 824 // biometric unlock to start next time keyguard is shown. 825 KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true); 826 // We use mAppWidgetToShow to show a particular widget after you add it-- once the screen 827 // turns off we reset that behavior 828 clearAppWidgetToShow(); 829 checkAppWidgetConsistency(); 830 showPrimarySecurityScreen(true); 831 getSecurityView(mCurrentSecuritySelection).onPause(); 832 CameraWidgetFrame cameraPage = findCameraPage(); 833 if (cameraPage != null) { 834 cameraPage.onScreenTurnedOff(); 835 } 836 } 837 838 public void clearAppWidgetToShow() { 839 mAppWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID; 840 } 841 842 @Override 843 public void show() { 844 if (DEBUG) Log.d(TAG, "show()"); 845 showPrimarySecurityScreen(false); 846 } 847 848 private boolean isSecure() { 849 SecurityMode mode = mSecurityModel.getSecurityMode(); 850 switch (mode) { 851 case Pattern: 852 return mLockPatternUtils.isLockPatternEnabled(); 853 case Password: 854 case PIN: 855 return mLockPatternUtils.isLockPasswordEnabled(); 856 case SimPin: 857 case SimPuk: 858 case Account: 859 return true; 860 case None: 861 return false; 862 default: 863 throw new IllegalStateException("Unknown security mode " + mode); 864 } 865 } 866 867 @Override 868 public void wakeWhenReadyTq(int keyCode) { 869 if (DEBUG) Log.d(TAG, "onWakeKey"); 870 if (keyCode == KeyEvent.KEYCODE_MENU && isSecure()) { 871 if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU"); 872 showSecurityScreen(SecurityMode.None); 873 } else { 874 if (DEBUG) Log.d(TAG, "poking wake lock immediately"); 875 } 876 if (mViewMediatorCallback != null) { 877 mViewMediatorCallback.wakeUp(); 878 } 879 } 880 881 @Override 882 public void verifyUnlock() { 883 SecurityMode securityMode = mSecurityModel.getSecurityMode(); 884 if (securityMode == KeyguardSecurityModel.SecurityMode.None) { 885 if (mViewMediatorCallback != null) { 886 mViewMediatorCallback.keyguardDone(true); 887 } 888 } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern 889 && securityMode != KeyguardSecurityModel.SecurityMode.PIN 890 && securityMode != KeyguardSecurityModel.SecurityMode.Password) { 891 // can only verify unlock when in pattern/password mode 892 if (mViewMediatorCallback != null) { 893 mViewMediatorCallback.keyguardDone(false); 894 } 895 } else { 896 // otherwise, go to the unlock screen, see if they can verify it 897 mIsVerifyUnlockOnly = true; 898 showSecurityScreen(securityMode); 899 } 900 } 901 902 private int getSecurityViewIdForMode(SecurityMode securityMode) { 903 switch (securityMode) { 904 case None: return R.id.keyguard_selector_view; 905 case Pattern: return R.id.keyguard_pattern_view; 906 case PIN: return R.id.keyguard_pin_view; 907 case Password: return R.id.keyguard_password_view; 908 case Biometric: return R.id.keyguard_face_unlock_view; 909 case Account: return R.id.keyguard_account_view; 910 case SimPin: return R.id.keyguard_sim_pin_view; 911 case SimPuk: return R.id.keyguard_sim_puk_view; 912 } 913 return 0; 914 } 915 916 private int getLayoutIdFor(SecurityMode securityMode) { 917 switch (securityMode) { 918 case None: return R.layout.keyguard_selector_view; 919 case Pattern: return R.layout.keyguard_pattern_view; 920 case PIN: return R.layout.keyguard_pin_view; 921 case Password: return R.layout.keyguard_password_view; 922 case Biometric: return R.layout.keyguard_face_unlock_view; 923 case Account: return R.layout.keyguard_account_view; 924 case SimPin: return R.layout.keyguard_sim_pin_view; 925 case SimPuk: return R.layout.keyguard_sim_puk_view; 926 default: 927 return 0; 928 } 929 } 930 931 private boolean addWidget(int appId, int pageIndex, boolean updateDbIfFailed) { 932 AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appId); 933 if (appWidgetInfo != null) { 934 AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo); 935 addWidget(view, pageIndex); 936 return true; 937 } else { 938 if (updateDbIfFailed) { 939 Log.w(TAG, "AppWidgetInfo for app widget id " + appId + " was null, deleting"); 940 mAppWidgetHost.deleteAppWidgetId(appId); 941 mLockPatternUtils.removeAppWidget(appId); 942 } 943 return false; 944 } 945 } 946 947 private final CameraWidgetFrame.Callbacks mCameraWidgetCallbacks = 948 new CameraWidgetFrame.Callbacks() { 949 @Override 950 public void onLaunchingCamera() { 951 setSliderHandleAlpha(0); 952 } 953 954 @Override 955 public void onCameraLaunchedSuccessfully() { 956 if (mAppWidgetContainer.isCameraPage(mAppWidgetContainer.getCurrentPage())) { 957 mAppWidgetContainer.scrollLeft(); 958 } 959 setSliderHandleAlpha(1); 960 mShowSecurityWhenReturn = true; 961 } 962 963 @Override 964 public void onCameraLaunchedUnsuccessfully() { 965 setSliderHandleAlpha(1); 966 } 967 968 private void setSliderHandleAlpha(float alpha) { 969 SlidingChallengeLayout slider = 970 (SlidingChallengeLayout) findViewById(R.id.sliding_layout); 971 if (slider != null) { 972 slider.setHandleAlpha(alpha); 973 } 974 } 975 }; 976 977 private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() { 978 @Override 979 Context getContext() { 980 return mContext; 981 } 982 983 @Override 984 KeyguardSecurityCallback getCallback() { 985 return mCallback; 986 } 987 988 @Override 989 LockPatternUtils getLockPatternUtils() { 990 return mLockPatternUtils; 991 } 992 }; 993 994 private int numWidgets() { 995 final int childCount = mAppWidgetContainer.getChildCount(); 996 int widgetCount = 0; 997 for (int i = 0; i < childCount; i++) { 998 if (mAppWidgetContainer.isWidgetPage(i)) { 999 widgetCount++; 1000 } 1001 } 1002 return widgetCount; 1003 } 1004 1005 1006 private void setAddWidgetEnabled(boolean clickable) { 1007 View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget); 1008 if (addWidget != null) { 1009 View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view); 1010 addWidgetButton.setEnabled(clickable); 1011 } 1012 } 1013 1014 private void addDefaultWidgets() { 1015 LayoutInflater inflater = LayoutInflater.from(mContext); 1016 inflater.inflate(R.layout.keyguard_transport_control_view, this, true); 1017 1018 if (!mSafeModeEnabled && !widgetsDisabledByDpm()) { 1019 View addWidget = inflater.inflate(R.layout.keyguard_add_widget, this, false); 1020 mAppWidgetContainer.addWidget(addWidget, 0); 1021 View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view); 1022 addWidgetButton.setOnClickListener(new OnClickListener() { 1023 @Override 1024 public void onClick(View v) { 1025 // Pass in an invalid widget id... the picker will allocate an ID for us 1026 mActivityLauncher.launchWidgetPicker(AppWidgetManager.INVALID_APPWIDGET_ID); 1027 } 1028 }); 1029 } 1030 1031 // We currently disable cameras in safe mode because we support loading 3rd party 1032 // cameras we can't trust. TODO: plumb safe mode into camera creation code and only 1033 // inflate system-provided camera? 1034 if (!mSafeModeEnabled && !cameraDisabledByDpm() 1035 && mContext.getResources().getBoolean(R.bool.kg_enable_camera_default_widget)) { 1036 View cameraWidget = 1037 CameraWidgetFrame.create(mContext, mCameraWidgetCallbacks, mActivityLauncher); 1038 if (cameraWidget != null) { 1039 mAppWidgetContainer.addWidget(cameraWidget); 1040 } 1041 } 1042 1043 enableUserSelectorIfNecessary(); 1044 initializeTransportControl(); 1045 } 1046 1047 private boolean removeTransportFromWidgetPager() { 1048 int page = getWidgetPosition(R.id.keyguard_transport_control); 1049 if (page != -1) { 1050 mAppWidgetContainer.removeWidget(mTransportControl); 1051 1052 // XXX keep view attached so we still get show/hide events from AudioManager 1053 KeyguardHostView.this.addView(mTransportControl); 1054 mTransportControl.setVisibility(View.GONE); 1055 mViewStateManager.setTransportState(KeyguardViewStateManager.TRANSPORT_GONE); 1056 return true; 1057 } 1058 return false; 1059 } 1060 1061 private void addTransportToWidgetPager() { 1062 if (getWidgetPosition(R.id.keyguard_transport_control) == -1) { 1063 KeyguardHostView.this.removeView(mTransportControl); 1064 // insert to left of camera if it exists, otherwise after right-most widget 1065 int lastWidget = mAppWidgetContainer.getChildCount() - 1; 1066 int position = 0; // handle no widget case 1067 if (lastWidget >= 0) { 1068 position = mAppWidgetContainer.isCameraPage(lastWidget) ? 1069 lastWidget : lastWidget + 1; 1070 } 1071 mAppWidgetContainer.addWidget(mTransportControl, position); 1072 mTransportControl.setVisibility(View.VISIBLE); 1073 } 1074 } 1075 1076 private void initializeTransportControl() { 1077 mTransportControl = 1078 (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control); 1079 mTransportControl.setVisibility(View.GONE); 1080 1081 // This code manages showing/hiding the transport control. We keep it around and only 1082 // add it to the hierarchy if it needs to be present. 1083 if (mTransportControl != null) { 1084 mTransportControl.setKeyguardCallback(new TransportCallback() { 1085 @Override 1086 public void onListenerDetached() { 1087 if (removeTransportFromWidgetPager()) { 1088 mTransportControl.post(mSwitchPageRunnable); 1089 } 1090 } 1091 1092 @Override 1093 public void onListenerAttached() { 1094 // Transport will be added when playstate changes... 1095 mTransportControl.post(mSwitchPageRunnable); 1096 } 1097 1098 @Override 1099 public void onPlayStateChanged() { 1100 mTransportControl.post(mSwitchPageRunnable); 1101 } 1102 }); 1103 } 1104 } 1105 1106 private int getInsertPageIndex() { 1107 View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget); 1108 int insertionIndex = mAppWidgetContainer.indexOfChild(addWidget); 1109 if (insertionIndex < 0) { 1110 insertionIndex = 0; // no add widget page found 1111 } else { 1112 insertionIndex++; // place after add widget 1113 } 1114 return insertionIndex; 1115 } 1116 1117 private void addDefaultStatusWidget(int index) { 1118 LayoutInflater inflater = LayoutInflater.from(mContext); 1119 View statusWidget = inflater.inflate(R.layout.keyguard_status_view, null, true); 1120 mAppWidgetContainer.addWidget(statusWidget, index); 1121 } 1122 1123 private void addWidgetsFromSettings() { 1124 if (mSafeModeEnabled || widgetsDisabledByDpm()) { 1125 return; 1126 } 1127 1128 int insertionIndex = getInsertPageIndex(); 1129 1130 // Add user-selected widget 1131 final int[] widgets = mLockPatternUtils.getAppWidgets(); 1132 1133 if (widgets == null) { 1134 Log.d(TAG, "Problem reading widgets"); 1135 } else { 1136 for (int i = widgets.length -1; i >= 0; i--) { 1137 if (widgets[i] == LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) { 1138 addDefaultStatusWidget(insertionIndex); 1139 } else { 1140 // We add the widgets from left to right, starting after the first page after 1141 // the add page. We count down, since the order will be persisted from right 1142 // to left, starting after camera. 1143 addWidget(widgets[i], insertionIndex, true); 1144 } 1145 } 1146 } 1147 } 1148 1149 private int allocateIdForDefaultAppWidget() { 1150 int appWidgetId; 1151 Resources res = getContext().getResources(); 1152 ComponentName defaultAppWidget = new ComponentName( 1153 res.getString(R.string.widget_default_package_name), 1154 res.getString(R.string.widget_default_class_name)); 1155 1156 // Note: we don't support configuring the widget 1157 appWidgetId = mAppWidgetHost.allocateAppWidgetId(); 1158 1159 try { 1160 mAppWidgetManager.bindAppWidgetId(appWidgetId, defaultAppWidget); 1161 1162 } catch (IllegalArgumentException e) { 1163 Log.e(TAG, "Error when trying to bind default AppWidget: " + e); 1164 mAppWidgetHost.deleteAppWidgetId(appWidgetId); 1165 appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; 1166 } 1167 return appWidgetId; 1168 } 1169 public void checkAppWidgetConsistency() { 1170 // Since this method may bind a widget (which we can't do until boot completed) we 1171 // may have to defer it until after boot complete. 1172 if (!KeyguardUpdateMonitor.getInstance(mContext).hasBootCompleted()) { 1173 mCheckAppWidgetConsistencyOnBootCompleted = true; 1174 return; 1175 } 1176 final int childCount = mAppWidgetContainer.getChildCount(); 1177 boolean widgetPageExists = false; 1178 for (int i = 0; i < childCount; i++) { 1179 if (mAppWidgetContainer.isWidgetPage(i)) { 1180 widgetPageExists = true; 1181 break; 1182 } 1183 } 1184 if (!widgetPageExists) { 1185 final int insertPageIndex = getInsertPageIndex(); 1186 1187 final boolean userAddedWidgetsEnabled = !widgetsDisabledByDpm(); 1188 boolean addedDefaultAppWidget = false; 1189 1190 if (!mSafeModeEnabled) { 1191 if (userAddedWidgetsEnabled) { 1192 int appWidgetId = allocateIdForDefaultAppWidget(); 1193 if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) { 1194 addedDefaultAppWidget = addWidget(appWidgetId, insertPageIndex, true); 1195 } 1196 } else { 1197 // note: even if widgetsDisabledByDpm() returns true, we still bind/create 1198 // the default appwidget if possible 1199 int appWidgetId = mLockPatternUtils.getFallbackAppWidgetId(); 1200 if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) { 1201 appWidgetId = allocateIdForDefaultAppWidget(); 1202 if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) { 1203 mLockPatternUtils.writeFallbackAppWidgetId(appWidgetId); 1204 } 1205 } 1206 if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) { 1207 addedDefaultAppWidget = addWidget(appWidgetId, insertPageIndex, false); 1208 if (!addedDefaultAppWidget) { 1209 mAppWidgetHost.deleteAppWidgetId(appWidgetId); 1210 mLockPatternUtils.writeFallbackAppWidgetId( 1211 AppWidgetManager.INVALID_APPWIDGET_ID); 1212 } 1213 } 1214 } 1215 } 1216 1217 // Use the built-in status/clock view if we can't inflate the default widget 1218 if (!addedDefaultAppWidget) { 1219 addDefaultStatusWidget(insertPageIndex); 1220 } 1221 1222 // trigger DB updates only if user-added widgets are enabled 1223 if (!mSafeModeEnabled && userAddedWidgetsEnabled) { 1224 mAppWidgetContainer.onAddView( 1225 mAppWidgetContainer.getChildAt(insertPageIndex), insertPageIndex); 1226 } 1227 } 1228 } 1229 1230 Runnable mSwitchPageRunnable = new Runnable() { 1231 @Override 1232 public void run() { 1233 showAppropriateWidgetPage(); 1234 } 1235 }; 1236 1237 static class SavedState extends BaseSavedState { 1238 int transportState; 1239 int appWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID; 1240 1241 SavedState(Parcelable superState) { 1242 super(superState); 1243 } 1244 1245 private SavedState(Parcel in) { 1246 super(in); 1247 this.transportState = in.readInt(); 1248 this.appWidgetToShow = in.readInt(); 1249 } 1250 1251 @Override 1252 public void writeToParcel(Parcel out, int flags) { 1253 super.writeToParcel(out, flags); 1254 out.writeInt(this.transportState); 1255 out.writeInt(this.appWidgetToShow); 1256 } 1257 1258 public static final Parcelable.Creator<SavedState> CREATOR 1259 = new Parcelable.Creator<SavedState>() { 1260 public SavedState createFromParcel(Parcel in) { 1261 return new SavedState(in); 1262 } 1263 1264 public SavedState[] newArray(int size) { 1265 return new SavedState[size]; 1266 } 1267 }; 1268 } 1269 1270 @Override 1271 public Parcelable onSaveInstanceState() { 1272 if (DEBUG) Log.d(TAG, "onSaveInstanceState"); 1273 Parcelable superState = super.onSaveInstanceState(); 1274 SavedState ss = new SavedState(superState); 1275 ss.transportState = mViewStateManager.getTransportState(); 1276 ss.appWidgetToShow = mAppWidgetToShow; 1277 return ss; 1278 } 1279 1280 @Override 1281 public void onRestoreInstanceState(Parcelable state) { 1282 if (DEBUG) Log.d(TAG, "onRestoreInstanceState"); 1283 if (!(state instanceof SavedState)) { 1284 super.onRestoreInstanceState(state); 1285 return; 1286 } 1287 SavedState ss = (SavedState) state; 1288 super.onRestoreInstanceState(ss.getSuperState()); 1289 mViewStateManager.setTransportState(ss.transportState); 1290 mAppWidgetToShow = ss.appWidgetToShow; 1291 post(mSwitchPageRunnable); 1292 } 1293 1294 @Override 1295 public void onWindowFocusChanged(boolean hasWindowFocus) { 1296 super.onWindowFocusChanged(hasWindowFocus); 1297 if (DEBUG) Log.d(TAG, "Window is " + (hasWindowFocus ? "focused" : "unfocused")); 1298 if (hasWindowFocus && mShowSecurityWhenReturn) { 1299 SlidingChallengeLayout slider = 1300 (SlidingChallengeLayout) findViewById(R.id.sliding_layout); 1301 if (slider != null) { 1302 slider.setHandleAlpha(1); 1303 slider.showChallenge(true); 1304 } 1305 mShowSecurityWhenReturn = false; 1306 } 1307 } 1308 1309 private void showAppropriateWidgetPage() { 1310 int state = mViewStateManager.getTransportState(); 1311 boolean isMusicPlaying = mTransportControl.isMusicPlaying() 1312 || state == KeyguardViewStateManager.TRANSPORT_VISIBLE; 1313 if (isMusicPlaying) { 1314 mViewStateManager.setTransportState(KeyguardViewStateManager.TRANSPORT_VISIBLE); 1315 addTransportToWidgetPager(); 1316 } else if (state == KeyguardViewStateManager.TRANSPORT_VISIBLE) { 1317 mViewStateManager.setTransportState(KeyguardViewStateManager.TRANSPORT_INVISIBLE); 1318 } 1319 int pageToShow = getAppropriateWidgetPage(isMusicPlaying); 1320 mAppWidgetContainer.setCurrentPage(pageToShow); 1321 } 1322 1323 private CameraWidgetFrame findCameraPage() { 1324 for (int i = mAppWidgetContainer.getChildCount() - 1; i >= 0; i--) { 1325 if (mAppWidgetContainer.isCameraPage(i)) { 1326 return (CameraWidgetFrame) mAppWidgetContainer.getChildAt(i); 1327 } 1328 } 1329 return null; 1330 } 1331 1332 boolean isMusicPage(int pageIndex) { 1333 return pageIndex >= 0 && pageIndex == getWidgetPosition(R.id.keyguard_transport_control); 1334 } 1335 1336 private int getAppropriateWidgetPage(boolean isMusicPlaying) { 1337 // assumes at least one widget (besides camera + add) 1338 if (mAppWidgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) { 1339 final int childCount = mAppWidgetContainer.getChildCount(); 1340 for (int i = 0; i < childCount; i++) { 1341 if (mAppWidgetContainer.getWidgetPageAt(i).getContentAppWidgetId() 1342 == mAppWidgetToShow) { 1343 return i; 1344 } 1345 } 1346 mAppWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID; 1347 } 1348 // if music playing, show transport 1349 if (isMusicPlaying) { 1350 if (DEBUG) Log.d(TAG, "Music playing, show transport"); 1351 return mAppWidgetContainer.getWidgetPageIndex(mTransportControl); 1352 } 1353 1354 // else show the right-most widget (except for camera) 1355 int rightMost = mAppWidgetContainer.getChildCount() - 1; 1356 if (mAppWidgetContainer.isCameraPage(rightMost)) { 1357 rightMost--; 1358 } 1359 if (DEBUG) Log.d(TAG, "Show right-most page " + rightMost); 1360 return rightMost; 1361 } 1362 1363 private void enableUserSelectorIfNecessary() { 1364 if (!UserManager.supportsMultipleUsers()) { 1365 return; // device doesn't support multi-user mode 1366 } 1367 final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 1368 if (um == null) { 1369 Throwable t = new Throwable(); 1370 t.fillInStackTrace(); 1371 Log.e(TAG, "user service is null.", t); 1372 return; 1373 } 1374 1375 // if there are multiple users, we need to enable to multi-user switcher 1376 final List<UserInfo> users = um.getUsers(true); 1377 if (users == null) { 1378 Throwable t = new Throwable(); 1379 t.fillInStackTrace(); 1380 Log.e(TAG, "list of users is null.", t); 1381 return; 1382 } 1383 1384 final View multiUserView = findViewById(R.id.keyguard_user_selector); 1385 if (multiUserView == null) { 1386 Throwable t = new Throwable(); 1387 t.fillInStackTrace(); 1388 Log.e(TAG, "can't find user_selector in layout.", t); 1389 return; 1390 } 1391 1392 if (users.size() > 1) { 1393 if (multiUserView instanceof KeyguardMultiUserSelectorView) { 1394 KeyguardMultiUserSelectorView multiUser = 1395 (KeyguardMultiUserSelectorView) multiUserView; 1396 multiUser.setVisibility(View.VISIBLE); 1397 multiUser.addUsers(users); 1398 UserSwitcherCallback callback = new UserSwitcherCallback() { 1399 @Override 1400 public void hideSecurityView(int duration) { 1401 mSecurityViewContainer.animate().alpha(0).setDuration(duration); 1402 } 1403 1404 @Override 1405 public void showSecurityView() { 1406 mSecurityViewContainer.setAlpha(1.0f); 1407 } 1408 1409 @Override 1410 public void showUnlockHint() { 1411 if (mKeyguardSelectorView != null) { 1412 mKeyguardSelectorView.showUsabilityHint(); 1413 } 1414 } 1415 1416 @Override 1417 public void userActivity() { 1418 if (mViewMediatorCallback != null) { 1419 mViewMediatorCallback.userActivity(); 1420 } 1421 } 1422 }; 1423 multiUser.setCallback(callback); 1424 } else { 1425 Throwable t = new Throwable(); 1426 t.fillInStackTrace(); 1427 if (multiUserView == null) { 1428 Log.e(TAG, "could not find the user_selector.", t); 1429 } else { 1430 Log.e(TAG, "user_selector is the wrong type.", t); 1431 } 1432 } 1433 } 1434 } 1435 1436 @Override 1437 public void cleanUp() { 1438 1439 } 1440 1441 /** 1442 * In general, we enable unlocking the insecure keyguard with the menu key. However, there are 1443 * some cases where we wish to disable it, notably when the menu button placement or technology 1444 * is prone to false positives. 1445 * 1446 * @return true if the menu key should be enabled 1447 */ 1448 private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key"; 1449 private boolean shouldEnableMenuKey() { 1450 final Resources res = getResources(); 1451 final boolean configDisabled = res.getBoolean( 1452 com.android.internal.R.bool.config_disableMenuKeyInLockScreen); 1453 final boolean isTestHarness = ActivityManager.isRunningInTestHarness(); 1454 final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists(); 1455 return !configDisabled || isTestHarness || fileOverride; 1456 } 1457 1458 1459 1460 public void goToUserSwitcher() { 1461 mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_multi_user_selector)); 1462 } 1463 1464 public void goToWidget(int appWidgetId) { 1465 mAppWidgetToShow = appWidgetId; 1466 mSwitchPageRunnable.run(); 1467 } 1468 1469 public boolean handleMenuKey() { 1470 // The following enables the MENU key to work for testing automation 1471 if (shouldEnableMenuKey()) { 1472 showNextSecurityScreenOrFinish(false); 1473 return true; 1474 } 1475 return false; 1476 } 1477 1478 public boolean handleBackKey() { 1479 if (mCurrentSecuritySelection != SecurityMode.None) { 1480 mCallback.dismiss(false); 1481 return true; 1482 } 1483 return false; 1484 } 1485 1486 /** 1487 * Dismisses the keyguard by going to the next screen or making it gone. 1488 */ 1489 public void dismiss() { 1490 showNextSecurityScreenOrFinish(false); 1491 } 1492 1493 public void showAssistant() { 1494 final Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE)) 1495 .getAssistIntent(mContext, UserHandle.USER_CURRENT); 1496 1497 if (intent == null) return; 1498 1499 final ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext, 1500 R.anim.keyguard_action_assist_enter, R.anim.keyguard_action_assist_exit, 1501 getHandler(), null); 1502 1503 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1504 setOnDismissAction(new OnDismissAction() { 1505 @Override 1506 public boolean onDismiss() { 1507 try { 1508 mContext.startActivityAsUser(intent, opts.toBundle(), 1509 new UserHandle(UserHandle.USER_CURRENT)); 1510 } catch (ActivityNotFoundException e) { 1511 Slog.w(TAG, "Activity not found for " + intent.getAction()); 1512 } 1513 return false; 1514 } 1515 }); 1516 1517 mViewStateManager.showBouncer(true); 1518 } 1519} 1520