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