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