KeyguardHostView.java revision 99a3cae55bd1a4ea6d82aa7fa2a67170e7cd5b3e
1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.policy.impl.keyguard;
18
19import android.app.Activity;
20import android.app.ActivityManager;
21import android.app.ActivityOptions;
22import android.app.AlertDialog;
23import android.app.admin.DevicePolicyManager;
24import android.appwidget.AppWidgetHost;
25import android.appwidget.AppWidgetHostView;
26import android.appwidget.AppWidgetManager;
27import android.appwidget.AppWidgetProviderInfo;
28import android.content.Context;
29import android.content.Intent;
30import android.content.IntentSender;
31import android.content.pm.UserInfo;
32import android.content.res.Resources;
33import android.graphics.Canvas;
34import android.graphics.Rect;
35import android.os.Looper;
36import android.os.UserManager;
37import android.util.AttributeSet;
38import android.util.Log;
39import android.util.Slog;
40import android.view.KeyEvent;
41import android.view.LayoutInflater;
42import android.view.MotionEvent;
43import android.view.View;
44import android.view.WindowManager;
45import android.view.animation.AnimationUtils;
46import android.widget.RemoteViews.OnClickHandler;
47import android.widget.ViewFlipper;
48
49import com.android.internal.R;
50import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode;
51import com.android.internal.widget.LockPatternUtils;
52
53import java.io.File;
54import java.util.List;
55
56public class KeyguardHostView extends KeyguardViewBase {
57    private static final String TAG = "KeyguardViewHost";
58
59    // Use this to debug all of keyguard
60    public static boolean DEBUG;
61
62    // also referenced in SecuritySettings.java
63    static final int APPWIDGET_HOST_ID = 0x4B455947;
64    private static final String KEYGUARD_WIDGET_PREFS = "keyguard_widget_prefs";
65
66    private AppWidgetHost mAppWidgetHost;
67    private KeyguardWidgetPager mAppWidgetContainer;
68    private ViewFlipper mSecurityViewContainer;
69    private boolean mEnableMenuKey;
70    private boolean mIsVerifyUnlockOnly;
71    private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView
72    private SecurityMode mCurrentSecuritySelection = SecurityMode.None;
73
74    protected Runnable mLaunchRunnable;
75
76    protected int mFailedAttempts;
77    private LockPatternUtils mLockPatternUtils;
78
79    private KeyguardSecurityModel mSecurityModel;
80
81    private Rect mTempRect = new Rect();
82    private KeyguardTransportControlView mTransportControl;
83
84    /*package*/ interface TransportCallback {
85        void hide();
86        void show();
87    }
88
89    public KeyguardHostView(Context context) {
90        this(context, null);
91    }
92
93    public KeyguardHostView(Context context, AttributeSet attrs) {
94        super(context, attrs);
95        mLockPatternUtils = new LockPatternUtils(context);
96        mAppWidgetHost = new AppWidgetHost(
97                context, APPWIDGET_HOST_ID, mOnClickHandler, Looper.myLooper());
98        mSecurityModel = new KeyguardSecurityModel(context);
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    private 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        boolean showTimeout = false;
352        if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
353            // If we reach this code, it means the user has installed a DevicePolicyManager
354            // that requests device wipe after N attempts.  Once we get below the grace
355            // period, we'll post this dialog every time as a clear warning until the
356            // bombshell hits and the device is wiped.
357            if (remainingBeforeWipe > 0) {
358                showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe);
359            } else {
360                // Too many attempts. The device will be wiped shortly.
361                Slog.i(TAG, "Too many unlock attempts; device will be wiped!");
362                showWipeDialog(failedAttempts);
363            }
364        } else {
365            showTimeout =
366                (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
367            if (usingPattern && mEnableFallback) {
368                if (failedAttempts == failedAttemptWarning) {
369                    showAlmostAtAccountLoginDialog();
370                    showTimeout = false; // don't show both dialogs
371                } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
372                    mLockPatternUtils.setPermanentlyLocked(true);
373                    showSecurityScreen(SecurityMode.Account);
374                    // don't show timeout dialog because we show account unlock screen next
375                    showTimeout = false;
376                }
377            }
378        }
379        monitor.reportFailedUnlockAttempt();
380        mLockPatternUtils.reportFailedPasswordAttempt();
381        if (showTimeout) {
382            showTimeoutDialog();
383        }
384    }
385
386    /**
387     * Shows the backup security screen for the current security mode.  This could be used for
388     * password recovery screens but is currently only used for pattern unlock to show the
389     * account unlock screen and biometric unlock to show the user's normal unlock.
390     */
391    private void showBackupSecurity() {
392        SecurityMode currentMode = mSecurityModel.getAlternateFor(mSecurityModel.getSecurityMode());
393        showSecurityScreen(mSecurityModel.getBackupFor(currentMode));
394    }
395
396    private void showNextSecurityScreenOrFinish(boolean authenticated) {
397        boolean finish = false;
398        if (SecurityMode.None == mCurrentSecuritySelection) {
399            SecurityMode securityMode = mSecurityModel.getSecurityMode();
400            // Allow an alternate, such as biometric unlock
401            securityMode = mSecurityModel.getAlternateFor(securityMode);
402            if (SecurityMode.None == securityMode) {
403                finish = true; // no security required
404            } else {
405                showSecurityScreen(securityMode); // switch to the alternate security view
406            }
407        } else if (authenticated) {
408            switch (mCurrentSecuritySelection) {
409                case Pattern:
410                case Password:
411                case Account:
412                case Biometric:
413                    finish = true;
414                    break;
415
416                case SimPin:
417                case SimPuk:
418                    // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
419                    SecurityMode securityMode = mSecurityModel.getSecurityMode();
420                    if (securityMode != SecurityMode.None) {
421                        showSecurityScreen(securityMode);
422                    } else {
423                        finish = true;
424                    }
425                    break;
426
427                default:
428                    showSecurityScreen(SecurityMode.None);
429                    break;
430            }
431        } else {
432            // Not authenticated but we were asked to dismiss so go back to selector screen.
433            showSecurityScreen(SecurityMode.None);
434        }
435        if (finish) {
436            // If there's a pending runnable because the user interacted with a widget
437            // and we're leaving keyguard, then run it.
438            if (mLaunchRunnable != null) {
439                mLaunchRunnable.run();
440                mLaunchRunnable = null;
441            }
442            mViewMediatorCallback.keyguardDone(true);
443        }
444    }
445
446    private OnClickHandler mOnClickHandler = new OnClickHandler() {
447        @Override
448        public boolean onClickHandler(final View view,
449                final android.app.PendingIntent pendingIntent,
450                final Intent fillInIntent) {
451            if (pendingIntent.isActivity()) {
452                setOnDismissRunnable(new Runnable() {
453                    public void run() {
454                        try {
455                              // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
456                              Context context = view.getContext();
457                              ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view,
458                                      0, 0,
459                                      view.getMeasuredWidth(), view.getMeasuredHeight());
460                              context.startIntentSender(
461                                      pendingIntent.getIntentSender(), fillInIntent,
462                                      Intent.FLAG_ACTIVITY_NEW_TASK,
463                                      Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
464                          } catch (IntentSender.SendIntentException e) {
465                              android.util.Log.e(TAG, "Cannot send pending intent: ", e);
466                          } catch (Exception e) {
467                              android.util.Log.e(TAG, "Cannot send pending intent due to " +
468                                      "unknown exception: ", e);
469                          }
470                    }
471                });
472
473                mCallback.dismiss(false);
474                return true;
475            } else {
476                return super.onClickHandler(view, pendingIntent, fillInIntent);
477            }
478        };
479    };
480
481    @Override
482    public void reset() {
483        mIsVerifyUnlockOnly = false;
484        mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_status_view));
485        requestFocus();
486    }
487
488    /**
489     *  Sets a runnable to run when keyguard is dismissed
490     * @param runnable
491     */
492    protected void setOnDismissRunnable(Runnable runnable) {
493        mLaunchRunnable = runnable;
494    }
495
496    private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
497        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
498        KeyguardSecurityView view = null;
499        final int children = mSecurityViewContainer.getChildCount();
500        for (int child = 0; child < children; child++) {
501            if (mSecurityViewContainer.getChildAt(child).getId() == securityViewIdForMode) {
502                view = ((KeyguardSecurityView)mSecurityViewContainer.getChildAt(child));
503                break;
504            }
505        }
506        if (view == null) {
507            final LayoutInflater inflater = LayoutInflater.from(mContext);
508            View v = inflater.inflate(getLayoutIdFor(securityMode), this, false);
509            mSecurityViewContainer.addView(v);
510            updateSecurityView(v);
511            view = (KeyguardSecurityView) v;
512        }
513        return view;
514    }
515
516    /**
517     * Switches to the given security view unless it's already being shown, in which case
518     * this is a no-op.
519     *
520     * @param securityMode
521     */
522    private void showSecurityScreen(SecurityMode securityMode) {
523
524        if (securityMode == mCurrentSecuritySelection) return;
525
526        KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
527        KeyguardSecurityView newView = getSecurityView(securityMode);
528
529        // Emulate Activity life cycle
530        oldView.onPause();
531        newView.onResume();
532
533        mViewMediatorCallback.setNeedsInput(newView.needsInput());
534
535        // Find and show this child.
536        final int childCount = mSecurityViewContainer.getChildCount();
537
538        // If we're go to/from the selector view, do flip animation, otherwise use fade animation.
539        final boolean doFlip = mCurrentSecuritySelection == SecurityMode.None
540                || securityMode == SecurityMode.None;
541        final int inAnimation = doFlip ? R.anim.keyguard_security_animate_in
542                : R.anim.keyguard_security_fade_in;
543        final int outAnimation = doFlip ? R.anim.keyguard_security_animate_out
544                : R.anim.keyguard_security_fade_out;
545
546        mSecurityViewContainer.setInAnimation(AnimationUtils.loadAnimation(mContext, inAnimation));
547        mSecurityViewContainer.setOutAnimation(AnimationUtils.loadAnimation(mContext, outAnimation));
548        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
549        for (int i = 0; i < childCount; i++) {
550            if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) {
551                mSecurityViewContainer.setDisplayedChild(i);
552                break;
553            }
554        }
555
556        // Discard current runnable if we're switching back to the selector view
557        if (securityMode == SecurityMode.None) {
558            setOnDismissRunnable(null);
559        }
560
561        mCurrentSecuritySelection = securityMode;
562    }
563
564    @Override
565    public void onScreenTurnedOn() {
566        if (DEBUG) Log.d(TAG, "screen on");
567        showSecurityScreen(mCurrentSecuritySelection);
568        getSecurityView(mCurrentSecuritySelection).onResume();
569
570        // This is a an attempt to fix bug 7137389 where the device comes back on but the entire
571        // layout is blank but forcing a layout causes it to reappear (e.g. with with
572        // hierarchyviewer).
573        requestLayout();
574    }
575
576    @Override
577    public void onScreenTurnedOff() {
578        if (DEBUG) Log.d(TAG, "screen off");
579        showSecurityScreen(SecurityMode.None);
580        getSecurityView(mCurrentSecuritySelection).onPause();
581    }
582
583    @Override
584    public void show() {
585        onScreenTurnedOn();
586    }
587
588    private boolean isSecure() {
589        SecurityMode mode = mSecurityModel.getSecurityMode();
590        switch (mode) {
591            case Pattern:
592                return mLockPatternUtils.isLockPatternEnabled();
593            case Password:
594                return mLockPatternUtils.isLockPasswordEnabled();
595            case SimPin:
596            case SimPuk:
597            case Account:
598                return true;
599            case None:
600                return false;
601            default:
602                throw new IllegalStateException("Unknown security mode " + mode);
603        }
604    }
605
606    @Override
607    public void wakeWhenReadyTq(int keyCode) {
608        if (DEBUG) Log.d(TAG, "onWakeKey");
609        if (keyCode == KeyEvent.KEYCODE_MENU && isSecure()) {
610            if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU");
611            showSecurityScreen(SecurityMode.None);
612            mViewMediatorCallback.pokeWakelock();
613        } else {
614            if (DEBUG) Log.d(TAG, "poking wake lock immediately");
615            mViewMediatorCallback.pokeWakelock();
616        }
617    }
618
619    @Override
620    public void verifyUnlock() {
621        SecurityMode securityMode = mSecurityModel.getSecurityMode();
622        if (securityMode == KeyguardSecurityModel.SecurityMode.None) {
623            mViewMediatorCallback.keyguardDone(true);
624        } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
625                && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
626            // can only verify unlock when in pattern/password mode
627            mViewMediatorCallback.keyguardDone(false);
628        } else {
629            // otherwise, go to the unlock screen, see if they can verify it
630            mIsVerifyUnlockOnly = true;
631            showSecurityScreen(securityMode);
632        }
633    }
634
635    private int getSecurityViewIdForMode(SecurityMode securityMode) {
636        switch (securityMode) {
637            case None: return R.id.keyguard_selector_view;
638            case Pattern: return R.id.keyguard_pattern_view;
639            case Password: return R.id.keyguard_password_view;
640            case Biometric: return R.id.keyguard_face_unlock_view;
641            case Account: return R.id.keyguard_account_view;
642            case SimPin: return R.id.keyguard_sim_pin_view;
643            case SimPuk: return R.id.keyguard_sim_puk_view;
644        }
645        return 0;
646    }
647
648    private int getLayoutIdFor(SecurityMode securityMode) {
649        switch (securityMode) {
650            case None: return R.layout.keyguard_selector_view;
651            case Pattern: return R.layout.keyguard_pattern_view;
652            case Password: return R.layout.keyguard_password_view;
653            case Biometric: return R.layout.keyguard_face_unlock_view;
654            case Account: return R.layout.keyguard_account_view;
655            case SimPin: return R.layout.keyguard_sim_pin_view;
656            case SimPuk: return R.layout.keyguard_sim_puk_view;
657            default:
658                throw new RuntimeException("No layout for securityMode " + securityMode);
659        }
660    }
661
662    private void addWidget(int appId) {
663        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
664        AppWidgetProviderInfo appWidgetInfo = appWidgetManager.getAppWidgetInfo(appId);
665        if (appWidgetInfo != null) {
666            AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo);
667            addWidget(view);
668        } else {
669            Log.w(TAG, "AppWidgetInfo was null; not adding widget id " + appId);
670        }
671    }
672
673    private void maybePopulateWidgets() {
674        DevicePolicyManager dpm =
675                (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
676        if (dpm != null && dpm.getKeyguardWidgetsDisabled(null)
677                != DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_NONE) {
678            Log.v(TAG, "Keyguard widgets disabled because of device policy admin");
679            return;
680        }
681        inflateAndAddUserSelectorWidgetIfNecessary();
682
683        final int[] widgets = mLockPatternUtils.getUserDefinedWidgets();
684        for (int i = 0; i < widgets.length; i++) {
685            if (widgets[i] != -1) {
686                addWidget(widgets[i]);
687            }
688        }
689    }
690
691    private void inflateAndAddUserSelectorWidgetIfNecessary() {
692        // if there are multiple users, we need to add the multi-user switcher widget to the
693        // keyguard.
694        UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
695        List<UserInfo> users = mUm.getUsers();
696
697        if (users.size() > 1) {
698            KeyguardWidgetFrame userswitcher = (KeyguardWidgetFrame)
699                LayoutInflater.from(mContext).inflate(R.layout.keyguard_multi_user_selector_widget,
700                        mAppWidgetContainer, false);
701            // add the switcher to the left of status view
702            mAppWidgetContainer.addView(userswitcher, getWidgetPosition(R.id.keyguard_status_view));
703        }
704    }
705
706    @Override
707    public void cleanUp() {
708
709    }
710
711    /**
712     * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
713     * some cases where we wish to disable it, notably when the menu button placement or technology
714     * is prone to false positives.
715     *
716     * @return true if the menu key should be enabled
717     */
718    private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
719    private boolean shouldEnableMenuKey() {
720        final Resources res = getResources();
721        final boolean configDisabled = res.getBoolean(
722                com.android.internal.R.bool.config_disableMenuKeyInLockScreen);
723        final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
724        final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
725        return !configDisabled || isTestHarness || fileOverride;
726    }
727
728    @Override
729    public boolean onKeyDown(int keyCode, KeyEvent event) {
730        if (keyCode == KeyEvent.KEYCODE_MENU && mEnableMenuKey) {
731            showNextSecurityScreenOrFinish(false);
732            return true;
733        } else {
734            return super.onKeyDown(keyCode, event);
735        }
736    }
737
738}
739