KeyguardHostView.java revision 0207c0992cb05d0acc60540748c8ae6985917abb
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.Context; 29import android.content.Intent; 30import android.content.IntentSender; 31import android.content.pm.UserInfo; 32import android.content.res.Resources; 33import android.graphics.Canvas; 34import android.graphics.Rect; 35import android.os.Looper; 36import android.os.UserManager; 37import android.util.AttributeSet; 38import android.util.Log; 39import android.util.Slog; 40import android.view.KeyEvent; 41import android.view.LayoutInflater; 42import android.view.MotionEvent; 43import android.view.View; 44import android.view.ViewGroup; 45import android.view.WindowManager; 46import android.view.animation.AnimationUtils; 47import android.widget.RemoteViews.OnClickHandler; 48import android.widget.ViewFlipper; 49 50import com.android.internal.R; 51import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode; 52import com.android.internal.widget.LockPatternUtils; 53 54import java.io.File; 55import java.util.List; 56 57public class KeyguardHostView extends KeyguardViewBase { 58 private static final String TAG = "KeyguardViewHost"; 59 60 // Use this to debug all of keyguard 61 public static boolean DEBUG; 62 63 // also referenced in SecuritySettings.java 64 static final int APPWIDGET_HOST_ID = 0x4B455947; 65 private static final String KEYGUARD_WIDGET_PREFS = "keyguard_widget_prefs"; 66 67 private AppWidgetHost mAppWidgetHost; 68 private KeyguardWidgetPager mAppWidgetContainer; 69 private ViewFlipper mSecurityViewContainer; 70 private boolean mEnableMenuKey; 71 private boolean mIsVerifyUnlockOnly; 72 private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView 73 private SecurityMode mCurrentSecuritySelection = SecurityMode.None; 74 75 protected Runnable mLaunchRunnable; 76 77 protected int mFailedAttempts; 78 private LockPatternUtils mLockPatternUtils; 79 80 private KeyguardSecurityModel mSecurityModel; 81 82 private Rect mTempRect = new Rect(); 83 private KeyguardTransportControlView mTransportControl; 84 85 /*package*/ interface TransportCallback { 86 void hide(); 87 void show(); 88 } 89 90 /*package*/ interface UserSwitcherCallback { 91 void hideSecurityView(int duration); 92 void showSecurityView(); 93 } 94 95 public KeyguardHostView(Context context) { 96 this(context, null); 97 } 98 99 public KeyguardHostView(Context context, AttributeSet attrs) { 100 super(context, attrs); 101 mLockPatternUtils = new LockPatternUtils(context); 102 mAppWidgetHost = new AppWidgetHost( 103 context, APPWIDGET_HOST_ID, mOnClickHandler, Looper.myLooper()); 104 mSecurityModel = new KeyguardSecurityModel(context); 105 106 // The following enables the MENU key to work for testing automation 107 mEnableMenuKey = shouldEnableMenuKey(); 108 setFocusable(true); 109 setFocusableInTouchMode(true); 110 } 111 112 @Override 113 public boolean onTouchEvent(MotionEvent ev) { 114 boolean result = super.onTouchEvent(ev); 115 mTempRect.set(0, 0, 0, 0); 116 offsetRectIntoDescendantCoords(mSecurityViewContainer, mTempRect); 117 ev.offsetLocation(mTempRect.left, mTempRect.top); 118 result = mSecurityViewContainer.dispatchTouchEvent(ev) || result; 119 ev.offsetLocation(-mTempRect.left, -mTempRect.top); 120 return result; 121 } 122 123 @Override 124 protected void dispatchDraw(Canvas canvas) { 125 super.dispatchDraw(canvas); 126 if (mViewMediatorCallback != null) { 127 mViewMediatorCallback.keyguardDoneDrawing(); 128 } 129 } 130 131 private int getWidgetPosition(int id) { 132 final int children = mAppWidgetContainer.getChildCount(); 133 for (int i = 0; i < children; i++) { 134 if (mAppWidgetContainer.getChildAt(i).getId() == id) { 135 return i; 136 } 137 } 138 return -1; 139 } 140 141 @Override 142 protected void onFinishInflate() { 143 mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container); 144 KeyguardWidgetRegion kgwr = (KeyguardWidgetRegion) findViewById(R.id.kg_widget_region); 145 kgwr.setVisibility(VISIBLE); 146 mSecurityViewContainer = (ViewFlipper) findViewById(R.id.view_flipper); 147 148 // This code manages showing/hiding the transport control. We keep it around and only 149 // add it to the hierarchy if it needs to be present. 150 mTransportControl = 151 (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control); 152 if (mTransportControl != null) { 153 mTransportControl.setKeyguardCallback(new TransportCallback() { 154 boolean mSticky = false; 155 @Override 156 public void hide() { 157 int page = getWidgetPosition(R.id.keyguard_transport_control); 158 if (page != -1 && !mSticky) { 159 if (page == mAppWidgetContainer.getCurrentPage()) { 160 // Switch back to clock view if music was showing. 161 mAppWidgetContainer 162 .setCurrentPage(getWidgetPosition(R.id.keyguard_status_view)); 163 } 164 mAppWidgetContainer.removeView(mTransportControl); 165 // XXX keep view attached to hierarchy so we still get show/hide events 166 // from AudioManager 167 KeyguardHostView.this.addView(mTransportControl); 168 mTransportControl.setVisibility(View.GONE); 169 showAppropriateWidgetPage(); 170 } 171 } 172 173 @Override 174 public void show() { 175 if (getWidgetPosition(R.id.keyguard_transport_control) == -1) { 176 KeyguardHostView.this.removeView(mTransportControl); 177 mAppWidgetContainer.addView(mTransportControl, 178 getWidgetPosition(R.id.keyguard_status_view) + 1); 179 mTransportControl.setVisibility(View.VISIBLE); 180 // Once shown, leave it showing 181 mSticky = true; 182 showAppropriateWidgetPage(); 183 } 184 } 185 }); 186 } 187 updateSecurityViews(); 188 setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK); 189 } 190 191 private void updateSecurityViews() { 192 int children = mSecurityViewContainer.getChildCount(); 193 for (int i = 0; i < children; i++) { 194 updateSecurityView(mSecurityViewContainer.getChildAt(i)); 195 } 196 } 197 198 private void updateSecurityView(View view) { 199 if (view instanceof KeyguardSecurityView) { 200 KeyguardSecurityView ksv = (KeyguardSecurityView) view; 201 ksv.setKeyguardCallback(mCallback); 202 ksv.setLockPatternUtils(mLockPatternUtils); 203 } else { 204 Log.w(TAG, "View " + view + " is not a KeyguardSecurityView"); 205 } 206 } 207 208 void setLockPatternUtils(LockPatternUtils utils) { 209 mSecurityModel.setLockPatternUtils(utils); 210 mLockPatternUtils = utils; 211 updateSecurityViews(); 212 } 213 214 @Override 215 protected void onAttachedToWindow() { 216 super.onAttachedToWindow(); 217 mAppWidgetHost.startListening(); 218 maybePopulateWidgets(); 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) { 232 mAppWidgetContainer.addWidget(view); 233 } 234 235 private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() { 236 237 public void userActivity(long timeout) { 238 if (mViewMediatorCallback != null) { 239 mViewMediatorCallback.userActivity(timeout); 240 } 241 } 242 243 public void dismiss(boolean authenticated) { 244 showNextSecurityScreenOrFinish(authenticated); 245 } 246 247 public boolean isVerifyUnlockOnly() { 248 return mIsVerifyUnlockOnly; 249 } 250 251 public void reportSuccessfulUnlockAttempt() { 252 KeyguardUpdateMonitor.getInstance(mContext).clearFailedUnlockAttempts(); 253 mLockPatternUtils.reportSuccessfulPasswordAttempt(); 254 } 255 256 public void reportFailedUnlockAttempt() { 257 if (mCurrentSecuritySelection == SecurityMode.Biometric) { 258 KeyguardUpdateMonitor.getInstance(mContext).reportFailedBiometricUnlockAttempt(); 259 } else { 260 KeyguardHostView.this.reportFailedUnlockAttempt(); 261 } 262 } 263 264 public int getFailedAttempts() { 265 return KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(); 266 } 267 268 @Override 269 public void showBackupSecurity() { 270 KeyguardHostView.this.showBackupSecurity(); 271 } 272 273 @Override 274 public void setOnDismissRunnable(Runnable runnable) { 275 KeyguardHostView.this.setOnDismissRunnable(runnable); 276 } 277 278 }; 279 280 private void showDialog(String title, String message) { 281 final AlertDialog dialog = new AlertDialog.Builder(mContext) 282 .setTitle(title) 283 .setMessage(message) 284 .setNeutralButton(com.android.internal.R.string.ok, null) 285 .create(); 286 if (!(mContext instanceof Activity)) { 287 dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG); 288 } 289 dialog.show(); 290 } 291 292 @Override 293 public boolean dispatchKeyEvent(KeyEvent event) { 294 if (event.getAction() == KeyEvent.ACTION_UP 295 && event.getKeyCode() == KeyEvent.KEYCODE_BACK 296 && mCurrentSecuritySelection != SecurityMode.None) { 297 mCallback.dismiss(false); 298 return true; 299 } 300 return super.dispatchKeyEvent(event); 301 } 302 303 private void showTimeoutDialog() { 304 int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; 305 int messageId = 0; 306 307 switch (mSecurityModel.getSecurityMode()) { 308 case Pattern: 309 messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message; 310 break; 311 312 case Password: { 313 final boolean isPin = mLockPatternUtils.getKeyguardStoredPasswordQuality() == 314 DevicePolicyManager.PASSWORD_QUALITY_NUMERIC; 315 messageId = isPin ? R.string.kg_too_many_failed_pin_attempts_dialog_message 316 : R.string.kg_too_many_failed_password_attempts_dialog_message; 317 } 318 break; 319 } 320 321 if (messageId != 0) { 322 final String message = mContext.getString(messageId, 323 KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(), 324 timeoutInSeconds); 325 showDialog(null, message); 326 } 327 } 328 329 private void showAlmostAtWipeDialog(int attempts, int remaining) { 330 int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; 331 String message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe, 332 attempts, remaining); 333 showDialog(null, message); 334 } 335 336 private void showWipeDialog(int attempts) { 337 String message = mContext.getString(R.string.kg_failed_attempts_now_wiping, attempts); 338 showDialog(null, message); 339 } 340 341 private void showAlmostAtAccountLoginDialog() { 342 final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000; 343 final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET 344 - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; 345 String message = mContext.getString(R.string.kg_failed_attempts_almost_at_login, 346 count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds); 347 showDialog(null, message); 348 } 349 350 private void reportFailedUnlockAttempt() { 351 final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext); 352 final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time 353 354 if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts); 355 356 SecurityMode mode = mSecurityModel.getSecurityMode(); 357 final boolean usingPattern = mode == KeyguardSecurityModel.SecurityMode.Pattern; 358 359 final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager() 360 .getMaximumFailedPasswordsForWipe(null, mLockPatternUtils.getCurrentUser()); 361 362 final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET 363 - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT; 364 365 final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ? 366 (failedAttemptsBeforeWipe - failedAttempts) 367 : Integer.MAX_VALUE; // because DPM returns 0 if no restriction 368 369 boolean showTimeout = false; 370 if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) { 371 // If we reach this code, it means the user has installed a DevicePolicyManager 372 // that requests device wipe after N attempts. Once we get below the grace 373 // period, we'll post this dialog every time as a clear warning until the 374 // bombshell hits and the device is wiped. 375 if (remainingBeforeWipe > 0) { 376 showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe); 377 } else { 378 // Too many attempts. The device will be wiped shortly. 379 Slog.i(TAG, "Too many unlock attempts; device will be wiped!"); 380 showWipeDialog(failedAttempts); 381 } 382 } else { 383 showTimeout = 384 (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0; 385 if (usingPattern && mEnableFallback) { 386 if (failedAttempts == failedAttemptWarning) { 387 showAlmostAtAccountLoginDialog(); 388 showTimeout = false; // don't show both dialogs 389 } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) { 390 mLockPatternUtils.setPermanentlyLocked(true); 391 showSecurityScreen(SecurityMode.Account); 392 // don't show timeout dialog because we show account unlock screen next 393 showTimeout = false; 394 } 395 } 396 } 397 monitor.reportFailedUnlockAttempt(); 398 mLockPatternUtils.reportFailedPasswordAttempt(); 399 if (showTimeout) { 400 showTimeoutDialog(); 401 } 402 } 403 404 /** 405 * Shows the backup security screen for the current security mode. This could be used for 406 * password recovery screens but is currently only used for pattern unlock to show the 407 * account unlock screen and biometric unlock to show the user's normal unlock. 408 */ 409 private void showBackupSecurity() { 410 SecurityMode currentMode = mSecurityModel.getAlternateFor(mSecurityModel.getSecurityMode()); 411 showSecurityScreen(mSecurityModel.getBackupFor(currentMode)); 412 } 413 414 private void showNextSecurityScreenOrFinish(boolean authenticated) { 415 boolean finish = false; 416 if (SecurityMode.None == mCurrentSecuritySelection) { 417 SecurityMode securityMode = mSecurityModel.getSecurityMode(); 418 // Allow an alternate, such as biometric unlock 419 securityMode = mSecurityModel.getAlternateFor(securityMode); 420 if (SecurityMode.None == securityMode) { 421 finish = true; // no security required 422 } else { 423 showSecurityScreen(securityMode); // switch to the alternate security view 424 } 425 } else if (authenticated) { 426 switch (mCurrentSecuritySelection) { 427 case Pattern: 428 case Password: 429 case Account: 430 case Biometric: 431 finish = true; 432 break; 433 434 case SimPin: 435 case SimPuk: 436 // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home 437 SecurityMode securityMode = mSecurityModel.getSecurityMode(); 438 if (securityMode != SecurityMode.None) { 439 showSecurityScreen(securityMode); 440 } else { 441 finish = true; 442 } 443 break; 444 445 default: 446 showSecurityScreen(SecurityMode.None); 447 break; 448 } 449 } else { 450 // Not authenticated but we were asked to dismiss so go back to selector screen. 451 showSecurityScreen(SecurityMode.None); 452 } 453 if (finish) { 454 // If there's a pending runnable because the user interacted with a widget 455 // and we're leaving keyguard, then run it. 456 if (mLaunchRunnable != null) { 457 mLaunchRunnable.run(); 458 mLaunchRunnable = null; 459 } 460 if (mViewMediatorCallback != null) { 461 mViewMediatorCallback.keyguardDone(true); 462 } 463 } 464 } 465 466 private OnClickHandler mOnClickHandler = new OnClickHandler() { 467 @Override 468 public boolean onClickHandler(final View view, 469 final android.app.PendingIntent pendingIntent, 470 final Intent fillInIntent) { 471 if (pendingIntent.isActivity()) { 472 setOnDismissRunnable(new Runnable() { 473 public void run() { 474 try { 475 // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT? 476 Context context = view.getContext(); 477 ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view, 478 0, 0, 479 view.getMeasuredWidth(), view.getMeasuredHeight()); 480 context.startIntentSender( 481 pendingIntent.getIntentSender(), fillInIntent, 482 Intent.FLAG_ACTIVITY_NEW_TASK, 483 Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle()); 484 } catch (IntentSender.SendIntentException e) { 485 android.util.Log.e(TAG, "Cannot send pending intent: ", e); 486 } catch (Exception e) { 487 android.util.Log.e(TAG, "Cannot send pending intent due to " + 488 "unknown exception: ", e); 489 } 490 } 491 }); 492 493 mCallback.dismiss(false); 494 return true; 495 } else { 496 return super.onClickHandler(view, pendingIntent, fillInIntent); 497 } 498 }; 499 }; 500 501 @Override 502 public void reset() { 503 mIsVerifyUnlockOnly = false; 504 mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_status_view)); 505 requestFocus(); 506 } 507 508 /** 509 * Sets a runnable to run when keyguard is dismissed 510 * @param runnable 511 */ 512 protected void setOnDismissRunnable(Runnable runnable) { 513 mLaunchRunnable = runnable; 514 } 515 516 private KeyguardSecurityView getSecurityView(SecurityMode securityMode) { 517 final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); 518 KeyguardSecurityView view = null; 519 final int children = mSecurityViewContainer.getChildCount(); 520 for (int child = 0; child < children; child++) { 521 if (mSecurityViewContainer.getChildAt(child).getId() == securityViewIdForMode) { 522 view = ((KeyguardSecurityView)mSecurityViewContainer.getChildAt(child)); 523 break; 524 } 525 } 526 if (view == null) { 527 final LayoutInflater inflater = LayoutInflater.from(mContext); 528 View v = inflater.inflate(getLayoutIdFor(securityMode), this, false); 529 mSecurityViewContainer.addView(v); 530 updateSecurityView(v); 531 view = (KeyguardSecurityView) v; 532 } 533 return view; 534 } 535 536 /** 537 * Switches to the given security view unless it's already being shown, in which case 538 * this is a no-op. 539 * 540 * @param securityMode 541 */ 542 private void showSecurityScreen(SecurityMode securityMode) { 543 544 if (securityMode == mCurrentSecuritySelection) return; 545 546 KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection); 547 KeyguardSecurityView newView = getSecurityView(securityMode); 548 549 // Emulate Activity life cycle 550 oldView.onPause(); 551 newView.onResume(); 552 553 final boolean needsInput = newView.needsInput(); 554 if (mViewMediatorCallback != null) { 555 mViewMediatorCallback.setNeedsInput(needsInput); 556 } 557 558 // Find and show this child. 559 final int childCount = mSecurityViewContainer.getChildCount(); 560 561 // If we're go to/from the selector view, do flip animation, otherwise use fade animation. 562 final boolean doFlip = mCurrentSecuritySelection == SecurityMode.None 563 || securityMode == SecurityMode.None; 564 final int inAnimation = doFlip ? R.anim.keyguard_security_animate_in 565 : R.anim.keyguard_security_fade_in; 566 final int outAnimation = doFlip ? R.anim.keyguard_security_animate_out 567 : R.anim.keyguard_security_fade_out; 568 569 mSecurityViewContainer.setInAnimation(AnimationUtils.loadAnimation(mContext, inAnimation)); 570 mSecurityViewContainer.setOutAnimation(AnimationUtils.loadAnimation(mContext, outAnimation)); 571 final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); 572 for (int i = 0; i < childCount; i++) { 573 if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) { 574 mSecurityViewContainer.setDisplayedChild(i); 575 break; 576 } 577 } 578 579 580 if (securityMode == SecurityMode.None) { 581 // Discard current runnable if we're switching back to the selector view 582 setOnDismissRunnable(null); 583 setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK); 584 } else { 585 setSystemUiVisibility(getSystemUiVisibility() & (~View.STATUS_BAR_DISABLE_BACK)); 586 } 587 588 mCurrentSecuritySelection = securityMode; 589 } 590 591 @Override 592 public void onScreenTurnedOn() { 593 if (DEBUG) Log.d(TAG, "screen on"); 594 showSecurityScreen(mCurrentSecuritySelection); 595 getSecurityView(mCurrentSecuritySelection).onResume(); 596 597 // This is a an attempt to fix bug 7137389 where the device comes back on but the entire 598 // layout is blank but forcing a layout causes it to reappear (e.g. with with 599 // hierarchyviewer). 600 requestLayout(); 601 } 602 603 @Override 604 public void onScreenTurnedOff() { 605 if (DEBUG) Log.d(TAG, "screen off"); 606 showSecurityScreen(SecurityMode.None); 607 getSecurityView(mCurrentSecuritySelection).onPause(); 608 } 609 610 @Override 611 public void show() { 612 onScreenTurnedOn(); 613 } 614 615 private boolean isSecure() { 616 SecurityMode mode = mSecurityModel.getSecurityMode(); 617 switch (mode) { 618 case Pattern: 619 return mLockPatternUtils.isLockPatternEnabled(); 620 case Password: 621 return mLockPatternUtils.isLockPasswordEnabled(); 622 case SimPin: 623 case SimPuk: 624 case Account: 625 return true; 626 case None: 627 return false; 628 default: 629 throw new IllegalStateException("Unknown security mode " + mode); 630 } 631 } 632 633 @Override 634 public void wakeWhenReadyTq(int keyCode) { 635 if (DEBUG) Log.d(TAG, "onWakeKey"); 636 if (keyCode == KeyEvent.KEYCODE_MENU && isSecure()) { 637 if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU"); 638 showSecurityScreen(SecurityMode.None); 639 } else { 640 if (DEBUG) Log.d(TAG, "poking wake lock immediately"); 641 } 642 if (mViewMediatorCallback != null) { 643 mViewMediatorCallback.wakeUp(); 644 } 645 } 646 647 @Override 648 public void verifyUnlock() { 649 SecurityMode securityMode = mSecurityModel.getSecurityMode(); 650 if (securityMode == KeyguardSecurityModel.SecurityMode.None) { 651 if (mViewMediatorCallback != null) { 652 mViewMediatorCallback.keyguardDone(true); 653 } 654 } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern 655 && securityMode != KeyguardSecurityModel.SecurityMode.Password) { 656 // can only verify unlock when in pattern/password mode 657 if (mViewMediatorCallback != null) { 658 mViewMediatorCallback.keyguardDone(false); 659 } 660 } else { 661 // otherwise, go to the unlock screen, see if they can verify it 662 mIsVerifyUnlockOnly = true; 663 showSecurityScreen(securityMode); 664 } 665 } 666 667 private int getSecurityViewIdForMode(SecurityMode securityMode) { 668 switch (securityMode) { 669 case None: return R.id.keyguard_selector_view; 670 case Pattern: return R.id.keyguard_pattern_view; 671 case Password: return R.id.keyguard_password_view; 672 case Biometric: return R.id.keyguard_face_unlock_view; 673 case Account: return R.id.keyguard_account_view; 674 case SimPin: return R.id.keyguard_sim_pin_view; 675 case SimPuk: return R.id.keyguard_sim_puk_view; 676 } 677 return 0; 678 } 679 680 private int getLayoutIdFor(SecurityMode securityMode) { 681 switch (securityMode) { 682 case None: return R.layout.keyguard_selector_view; 683 case Pattern: return R.layout.keyguard_pattern_view; 684 case Password: return R.layout.keyguard_password_view; 685 case Biometric: return R.layout.keyguard_face_unlock_view; 686 case Account: return R.layout.keyguard_account_view; 687 case SimPin: return R.layout.keyguard_sim_pin_view; 688 case SimPuk: return R.layout.keyguard_sim_puk_view; 689 default: 690 throw new RuntimeException("No layout for securityMode " + securityMode); 691 } 692 } 693 694 private void addWidget(int appId) { 695 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext); 696 AppWidgetProviderInfo appWidgetInfo = appWidgetManager.getAppWidgetInfo(appId); 697 if (appWidgetInfo != null) { 698 AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo); 699 addWidget(view); 700 } else { 701 Log.w(TAG, "AppWidgetInfo was null; not adding widget id " + appId); 702 } 703 } 704 705 private void maybePopulateWidgets() { 706 DevicePolicyManager dpm = 707 (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); 708 if (dpm != null) { 709 final int currentUser = mLockPatternUtils.getCurrentUser(); 710 final int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUser); 711 if ((disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0) { 712 Log.v(TAG, "Keyguard widgets disabled because of device policy admin"); 713 return; 714 } 715 } 716 inflateAndAddUserSelectorWidgetIfNecessary(); 717 718 // Add status widget 719 int statusWidgetId = mLockPatternUtils.getStatusWidget(); 720 if (statusWidgetId != -1) { 721 addWidget(statusWidgetId); 722 View newStatusWidget = mAppWidgetContainer.getChildAt( 723 mAppWidgetContainer.getChildCount() - 1); 724 725 int oldStatusWidgetPosition = getWidgetPosition(R.id.keyguard_status_view); 726 mAppWidgetContainer.removeViewAt(oldStatusWidgetPosition); 727 728 // Re-add new status widget at position of old one 729 mAppWidgetContainer.removeView(newStatusWidget); 730 newStatusWidget.setId(R.id.keyguard_status_view); 731 mAppWidgetContainer.addView(newStatusWidget, oldStatusWidgetPosition); 732 } 733 734 // Add user-selected widget 735 final int[] widgets = mLockPatternUtils.getUserDefinedWidgets(); 736 for (int i = 0; i < widgets.length; i++) { 737 if (widgets[i] != -1) { 738 addWidget(widgets[i]); 739 } 740 } 741 showAppropriateWidgetPage(); 742 } 743 744 private void showAppropriateWidgetPage() { 745 int page = mAppWidgetContainer.indexOfChild(findViewById(R.id.keyguard_status_view)); 746 if (mAppWidgetContainer.indexOfChild(mTransportControl) != -1) { 747 page = mAppWidgetContainer.indexOfChild(mTransportControl); 748 } 749 mAppWidgetContainer.setCurrentPage(page); 750 } 751 752 private void inflateAndAddUserSelectorWidgetIfNecessary() { 753 // if there are multiple users, we need to add the multi-user switcher widget to the 754 // keyguard. 755 UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 756 List<UserInfo> users = mUm.getUsers(true); 757 758 if (users.size() > 1) { 759 KeyguardWidgetFrame userSwitcher = (KeyguardWidgetFrame) 760 LayoutInflater.from(mContext).inflate(R.layout.keyguard_multi_user_selector_widget, 761 mAppWidgetContainer, false); 762 763 // add the switcher in the first position 764 mAppWidgetContainer.addView(userSwitcher, getWidgetPosition(R.id.keyguard_status_view)); 765 KeyguardMultiUserSelectorView multiUser = (KeyguardMultiUserSelectorView) 766 userSwitcher.getChildAt(0); 767 768 UserSwitcherCallback callback = new UserSwitcherCallback() { 769 @Override 770 public void hideSecurityView(int duration) { 771 mSecurityViewContainer.animate().alpha(0).setDuration(duration); 772 } 773 774 @Override 775 public void showSecurityView() { 776 mSecurityViewContainer.setAlpha(1.0f); 777 } 778 }; 779 multiUser.setCallback(callback); 780 } 781 } 782 783 @Override 784 public void cleanUp() { 785 786 } 787 788 /** 789 * In general, we enable unlocking the insecure keyguard with the menu key. However, there are 790 * some cases where we wish to disable it, notably when the menu button placement or technology 791 * is prone to false positives. 792 * 793 * @return true if the menu key should be enabled 794 */ 795 private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key"; 796 private boolean shouldEnableMenuKey() { 797 final Resources res = getResources(); 798 final boolean configDisabled = res.getBoolean( 799 com.android.internal.R.bool.config_disableMenuKeyInLockScreen); 800 final boolean isTestHarness = ActivityManager.isRunningInTestHarness(); 801 final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists(); 802 return !configDisabled || isTestHarness || fileOverride; 803 } 804 805 @Override 806 public boolean onKeyDown(int keyCode, KeyEvent event) { 807 if (keyCode == KeyEvent.KEYCODE_MENU && mEnableMenuKey) { 808 showNextSecurityScreenOrFinish(false); 809 return true; 810 } else { 811 return super.onKeyDown(keyCode, event); 812 } 813 } 814 815} 816