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