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