KeyguardHostView.java revision 6fba0a0c72394e880833b5dcf08241b7ce133239
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.SharedPreferences; 32import android.content.pm.UserInfo; 33import android.content.res.Resources; 34import android.graphics.Canvas; 35import android.graphics.Rect; 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.ViewGroup.LayoutParams; 46import android.view.WindowManager; 47import android.view.animation.AnimationUtils; 48import android.widget.RemoteViews.OnClickHandler; 49import android.widget.ViewFlipper; 50 51import com.android.internal.R; 52import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode; 53import com.android.internal.widget.LockPatternUtils; 54 55import java.io.File; 56import java.util.List; 57 58public class KeyguardHostView extends KeyguardViewBase { 59 private static final String TAG = "KeyguardViewHost"; 60 61 // Use this to debug all of keyguard 62 public static boolean DEBUG; 63 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 public KeyguardHostView(Context context) { 91 this(context, null); 92 } 93 94 public KeyguardHostView(Context context, AttributeSet attrs) { 95 super(context, attrs); 96 mLockPatternUtils = new LockPatternUtils(context); 97 mAppWidgetHost = new AppWidgetHost(mContext, APPWIDGET_HOST_ID, mOnClickHandler); 98 mSecurityModel = new KeyguardSecurityModel(mContext); 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 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 if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) { 352 // If we reach this code, it means the user has installed a DevicePolicyManager 353 // that requests device wipe after N attempts. Once we get below the grace 354 // period, we'll post this dialog every time as a clear warning until the 355 // bombshell hits and the device is wiped. 356 if (remainingBeforeWipe > 0) { 357 showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe); 358 } else { 359 // Too many attempts. The device will be wiped shortly. 360 Slog.i(TAG, "Too many unlock attempts; device will be wiped!"); 361 showWipeDialog(failedAttempts); 362 } 363 } else { 364 boolean showTimeout = 365 (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0; 366 if (usingPattern && mEnableFallback) { 367 if (failedAttempts == failedAttemptWarning) { 368 showAlmostAtAccountLoginDialog(); 369 showTimeout = false; // don't show both dialogs 370 } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) { 371 mLockPatternUtils.setPermanentlyLocked(true); 372 showSecurityScreen(SecurityMode.Account); 373 // don't show timeout dialog because we show account unlock screen next 374 showTimeout = false; 375 } 376 } 377 if (showTimeout) { 378 showTimeoutDialog(); 379 } 380 } 381 monitor.reportFailedUnlockAttempt(); 382 mLockPatternUtils.reportFailedPasswordAttempt(); 383 } 384 385 /** 386 * Shows the backup security screen for the current security mode. This could be used for 387 * password recovery screens but is currently only used for pattern unlock to show the 388 * account unlock screen and biometric unlock to show the user's normal unlock. 389 */ 390 private void showBackupSecurity() { 391 SecurityMode currentMode = mSecurityModel.getAlternateFor(mSecurityModel.getSecurityMode()); 392 showSecurityScreen(mSecurityModel.getBackupFor(currentMode)); 393 } 394 395 private void showNextSecurityScreenOrFinish(boolean authenticated) { 396 boolean finish = false; 397 if (SecurityMode.None == mCurrentSecuritySelection) { 398 SecurityMode securityMode = mSecurityModel.getSecurityMode(); 399 // Allow an alternate, such as biometric unlock 400 securityMode = mSecurityModel.getAlternateFor(securityMode); 401 if (SecurityMode.None == securityMode) { 402 finish = true; // no security required 403 } else { 404 showSecurityScreen(securityMode); // switch to the alternate security view 405 } 406 } else if (authenticated) { 407 switch (mCurrentSecuritySelection) { 408 case Pattern: 409 case Password: 410 case Account: 411 case Biometric: 412 finish = true; 413 break; 414 415 case SimPin: 416 case SimPuk: 417 // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home 418 SecurityMode securityMode = mSecurityModel.getSecurityMode(); 419 if (securityMode != SecurityMode.None) { 420 showSecurityScreen(securityMode); 421 } else { 422 finish = true; 423 } 424 break; 425 426 default: 427 showSecurityScreen(SecurityMode.None); 428 break; 429 } 430 } else { 431 // Not authenticated but we were asked to dismiss so go back to selector screen. 432 showSecurityScreen(SecurityMode.None); 433 } 434 if (finish) { 435 // If there's a pending runnable because the user interacted with a widget 436 // and we're leaving keyguard, then run it. 437 if (mLaunchRunnable != null) { 438 mLaunchRunnable.run(); 439 mLaunchRunnable = null; 440 } 441 mViewMediatorCallback.keyguardDone(true); 442 } 443 } 444 445 private OnClickHandler mOnClickHandler = new OnClickHandler() { 446 @Override 447 public boolean onClickHandler(final View view, 448 final android.app.PendingIntent pendingIntent, 449 final Intent fillInIntent) { 450 if (pendingIntent.isActivity()) { 451 setOnDismissRunnable(new Runnable() { 452 public void run() { 453 try { 454 // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT? 455 Context context = view.getContext(); 456 ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view, 457 0, 0, 458 view.getMeasuredWidth(), view.getMeasuredHeight()); 459 context.startIntentSender( 460 pendingIntent.getIntentSender(), fillInIntent, 461 Intent.FLAG_ACTIVITY_NEW_TASK, 462 Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle()); 463 } catch (IntentSender.SendIntentException e) { 464 android.util.Log.e(TAG, "Cannot send pending intent: ", e); 465 } catch (Exception e) { 466 android.util.Log.e(TAG, "Cannot send pending intent due to " + 467 "unknown exception: ", e); 468 } 469 } 470 }); 471 472 mCallback.dismiss(false); 473 return true; 474 } else { 475 return super.onClickHandler(view, pendingIntent, fillInIntent); 476 } 477 }; 478 }; 479 480 @Override 481 public void reset() { 482 mIsVerifyUnlockOnly = false; 483 mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_status_view)); 484 requestFocus(); 485 } 486 487 /** 488 * Sets a runnable to run when keyguard is dismissed 489 * @param runnable 490 */ 491 protected void setOnDismissRunnable(Runnable runnable) { 492 mLaunchRunnable = runnable; 493 } 494 495 private KeyguardSecurityView getSecurityView(SecurityMode securityMode) { 496 final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); 497 KeyguardSecurityView view = null; 498 final int children = mSecurityViewContainer.getChildCount(); 499 for (int child = 0; child < children; child++) { 500 if (mSecurityViewContainer.getChildAt(child).getId() == securityViewIdForMode) { 501 view = ((KeyguardSecurityView)mSecurityViewContainer.getChildAt(child)); 502 break; 503 } 504 } 505 if (view == null) { 506 final LayoutInflater inflater = LayoutInflater.from(mContext); 507 View v = inflater.inflate(getLayoutIdFor(securityMode), this, false); 508 mSecurityViewContainer.addView(v); 509 updateSecurityView(v); 510 view = (KeyguardSecurityView) v; 511 } 512 return view; 513 } 514 515 /** 516 * Switches to the given security view unless it's already being shown, in which case 517 * this is a no-op. 518 * 519 * @param securityMode 520 */ 521 private void showSecurityScreen(SecurityMode securityMode) { 522 523 if (securityMode == mCurrentSecuritySelection) return; 524 525 KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection); 526 KeyguardSecurityView newView = getSecurityView(securityMode); 527 528 // Emulate Activity life cycle 529 oldView.onPause(); 530 newView.onResume(); 531 532 mViewMediatorCallback.setNeedsInput(newView.needsInput()); 533 534 // Find and show this child. 535 final int childCount = mSecurityViewContainer.getChildCount(); 536 537 // If we're go to/from the selector view, do flip animation, otherwise use fade animation. 538 final boolean doFlip = mCurrentSecuritySelection == SecurityMode.None 539 || securityMode == SecurityMode.None; 540 final int inAnimation = doFlip ? R.anim.keyguard_security_animate_in 541 : R.anim.keyguard_security_fade_in; 542 final int outAnimation = doFlip ? R.anim.keyguard_security_animate_out 543 : R.anim.keyguard_security_fade_out; 544 545 mSecurityViewContainer.setInAnimation(AnimationUtils.loadAnimation(mContext, inAnimation)); 546 mSecurityViewContainer.setOutAnimation(AnimationUtils.loadAnimation(mContext, outAnimation)); 547 final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); 548 for (int i = 0; i < childCount; i++) { 549 if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) { 550 mSecurityViewContainer.setDisplayedChild(i); 551 break; 552 } 553 } 554 555 // Discard current runnable if we're switching back to the selector view 556 if (securityMode == SecurityMode.None) { 557 setOnDismissRunnable(null); 558 } 559 560 mCurrentSecuritySelection = securityMode; 561 } 562 563 @Override 564 public void onScreenTurnedOn() { 565 if (DEBUG) Log.d(TAG, "screen on"); 566 showSecurityScreen(mCurrentSecuritySelection); 567 getSecurityView(mCurrentSecuritySelection).onResume(); 568 569 // This is a an attempt to fix bug 7137389 where the device comes back on but the entire 570 // layout is blank but forcing a layout causes it to reappear (e.g. with with 571 // hierarchyviewer). 572 requestLayout(); 573 } 574 575 @Override 576 public void onScreenTurnedOff() { 577 if (DEBUG) Log.d(TAG, "screen off"); 578 showSecurityScreen(SecurityMode.None); 579 getSecurityView(mCurrentSecuritySelection).onPause(); 580 } 581 582 @Override 583 public void show() { 584 onScreenTurnedOn(); 585 } 586 587 private boolean isSecure() { 588 SecurityMode mode = mSecurityModel.getSecurityMode(); 589 switch (mode) { 590 case Pattern: 591 return mLockPatternUtils.isLockPatternEnabled(); 592 case Password: 593 return mLockPatternUtils.isLockPasswordEnabled(); 594 case SimPin: 595 case SimPuk: 596 case Account: 597 return true; 598 case None: 599 return false; 600 default: 601 throw new IllegalStateException("Unknown security mode " + mode); 602 } 603 } 604 605 @Override 606 public void wakeWhenReadyTq(int keyCode) { 607 if (DEBUG) Log.d(TAG, "onWakeKey"); 608 if (keyCode == KeyEvent.KEYCODE_MENU && isSecure()) { 609 if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU"); 610 showSecurityScreen(SecurityMode.None); 611 mViewMediatorCallback.pokeWakelock(); 612 } else { 613 if (DEBUG) Log.d(TAG, "poking wake lock immediately"); 614 mViewMediatorCallback.pokeWakelock(); 615 } 616 } 617 618 @Override 619 public void verifyUnlock() { 620 SecurityMode securityMode = mSecurityModel.getSecurityMode(); 621 if (securityMode == KeyguardSecurityModel.SecurityMode.None) { 622 mViewMediatorCallback.keyguardDone(true); 623 } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern 624 && securityMode != KeyguardSecurityModel.SecurityMode.Password) { 625 // can only verify unlock when in pattern/password mode 626 mViewMediatorCallback.keyguardDone(false); 627 } else { 628 // otherwise, go to the unlock screen, see if they can verify it 629 mIsVerifyUnlockOnly = true; 630 showSecurityScreen(securityMode); 631 } 632 } 633 634 private int getSecurityViewIdForMode(SecurityMode securityMode) { 635 switch (securityMode) { 636 case None: return R.id.keyguard_selector_view; 637 case Pattern: return R.id.keyguard_pattern_view; 638 case Password: return R.id.keyguard_password_view; 639 case Biometric: return R.id.keyguard_face_unlock_view; 640 case Account: return R.id.keyguard_account_view; 641 case SimPin: return R.id.keyguard_sim_pin_view; 642 case SimPuk: return R.id.keyguard_sim_puk_view; 643 } 644 return 0; 645 } 646 647 private int getLayoutIdFor(SecurityMode securityMode) { 648 switch (securityMode) { 649 case None: return R.layout.keyguard_selector_view; 650 case Pattern: return R.layout.keyguard_pattern_view; 651 case Password: return R.layout.keyguard_password_view; 652 case Biometric: return R.layout.keyguard_face_unlock_view; 653 case Account: return R.layout.keyguard_account_view; 654 case SimPin: return R.layout.keyguard_sim_pin_view; 655 case SimPuk: return R.layout.keyguard_sim_puk_view; 656 default: 657 throw new RuntimeException("No layout for securityMode " + securityMode); 658 } 659 } 660 661 private void addWidget(int appId) { 662 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext); 663 AppWidgetProviderInfo appWidgetInfo = appWidgetManager.getAppWidgetInfo(appId); 664 AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo); 665 addWidget(view); 666 } 667 668 private void maybePopulateWidgets() { 669 DevicePolicyManager dpm = 670 (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE); 671 if (dpm != null && dpm.getKeyguardWidgetsDisabled(null) 672 != DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_NONE) { 673 Log.v(TAG, "Keyguard widgets disabled because of device policy admin"); 674 return; 675 } 676 inflateAndAddUserSelectorWidgetIfNecessary(); 677 SharedPreferences prefs = mContext.getSharedPreferences( 678 KEYGUARD_WIDGET_PREFS, Context.MODE_PRIVATE); 679 for (String key : prefs.getAll().keySet()) { 680 int appId = prefs.getInt(key, -1); 681 if (appId != -1) { 682 Log.w(TAG, "populate: adding " + key); 683 addWidget(appId); 684 } else { 685 Log.w(TAG, "populate: can't find " + key); 686 } 687 } 688 } 689 690 private void inflateAndAddUserSelectorWidgetIfNecessary() { 691 // if there are multiple users, we need to add the multi-user switcher widget to the 692 // keyguard. 693 UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 694 List<UserInfo> users = mUm.getUsers(); 695 696 if (users.size() > 1) { 697 KeyguardWidgetFrame userswitcher = (KeyguardWidgetFrame) 698 LayoutInflater.from(mContext).inflate(R.layout.keyguard_multi_user_selector_widget, 699 mAppWidgetContainer, false); 700 // add the switcher to the left of status view 701 mAppWidgetContainer.addView(userswitcher, getWidgetPosition(R.id.keyguard_status_view)); 702 } 703 } 704 705 @Override 706 public void cleanUp() { 707 708 } 709 710 /** 711 * In general, we enable unlocking the insecure keyguard with the menu key. However, there are 712 * some cases where we wish to disable it, notably when the menu button placement or technology 713 * is prone to false positives. 714 * 715 * @return true if the menu key should be enabled 716 */ 717 private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key"; 718 private boolean shouldEnableMenuKey() { 719 final Resources res = getResources(); 720 final boolean configDisabled = res.getBoolean( 721 com.android.internal.R.bool.config_disableMenuKeyInLockScreen); 722 final boolean isTestHarness = ActivityManager.isRunningInTestHarness(); 723 final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists(); 724 return !configDisabled || isTestHarness || fileOverride; 725 } 726 727 @Override 728 public boolean onKeyDown(int keyCode, KeyEvent event) { 729 if (keyCode == KeyEvent.KEYCODE_MENU && mEnableMenuKey) { 730 showNextSecurityScreenOrFinish(false); 731 return true; 732 } else { 733 return super.onKeyDown(keyCode, event); 734 } 735 } 736 737} 738