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