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