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