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