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