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