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