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