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