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