KeyguardHostView.java revision 223ce5c7326f6d13a17ccf2624605bb62864a27a
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.Parcel;
37import android.os.Parcelable;
38import android.os.UserManager;
39import android.util.AttributeSet;
40import android.util.Log;
41import android.util.Slog;
42import android.view.KeyEvent;
43import android.view.LayoutInflater;
44import android.view.MotionEvent;
45import android.view.View;
46import android.view.ViewGroup;
47import android.view.WindowManager;
48import android.view.View.BaseSavedState;
49import android.view.animation.AnimationUtils;
50import android.widget.RemoteViews.OnClickHandler;
51import android.widget.ViewFlipper;
52
53import com.android.internal.R;
54import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode;
55import com.android.internal.policy.impl.keyguard.KeyguardTransportControlView.SavedState;
56import com.android.internal.widget.LockPatternUtils;
57
58import java.io.File;
59import java.util.List;
60
61public class KeyguardHostView extends KeyguardViewBase {
62    private static final String TAG = "KeyguardViewHost";
63
64    // Use this to debug all of keyguard
65    public static boolean DEBUG;
66
67    // also referenced in SecuritySettings.java
68    static final int APPWIDGET_HOST_ID = 0x4B455947;
69    private static final String KEYGUARD_WIDGET_PREFS = "keyguard_widget_prefs";
70
71    private static final int TRANSPORT_GONE = 0;
72    private static final int TRANSPORT_INVISIBLE = 1;
73    private static final int TRANSPORT_VISIBLE = 2;
74
75    private AppWidgetHost mAppWidgetHost;
76    private KeyguardWidgetRegion mAppWidgetRegion;
77    private KeyguardWidgetPager mAppWidgetContainer;
78    private ViewFlipper mSecurityViewContainer;
79    private KeyguardSelectorView mKeyguardSelectorView;
80    private KeyguardTransportControlView mTransportControl;
81    private boolean mEnableMenuKey;
82    private boolean mIsVerifyUnlockOnly;
83    private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView
84    private SecurityMode mCurrentSecuritySelection = SecurityMode.None;
85
86    protected Runnable mLaunchRunnable;
87
88    protected int mFailedAttempts;
89    private LockPatternUtils mLockPatternUtils;
90
91    private KeyguardSecurityModel mSecurityModel;
92
93    private Rect mTempRect = new Rect();
94    private int mTransportState = TRANSPORT_GONE;
95
96    /*package*/ interface TransportCallback {
97        void onListenerDetached();
98        void onListenerAttached();
99        void onPlayStateChanged();
100    }
101
102    /*package*/ interface UserSwitcherCallback {
103        void hideSecurityView(int duration);
104        void showSecurityView();
105        void showUnlockHint();
106    }
107
108    public KeyguardHostView(Context context) {
109        this(context, null);
110    }
111
112    public KeyguardHostView(Context context, AttributeSet attrs) {
113        super(context, attrs);
114        mLockPatternUtils = new LockPatternUtils(context);
115        mAppWidgetHost = new AppWidgetHost(
116                context, APPWIDGET_HOST_ID, mOnClickHandler, Looper.myLooper());
117        mSecurityModel = new KeyguardSecurityModel(context);
118
119        // The following enables the MENU key to work for testing automation
120        mEnableMenuKey = shouldEnableMenuKey();
121        setFocusable(true);
122        setFocusableInTouchMode(true);
123    }
124
125    @Override
126    public boolean onTouchEvent(MotionEvent ev) {
127        boolean result = super.onTouchEvent(ev);
128        mTempRect.set(0, 0, 0, 0);
129        offsetRectIntoDescendantCoords(mSecurityViewContainer, mTempRect);
130        ev.offsetLocation(mTempRect.left, mTempRect.top);
131        result = mSecurityViewContainer.dispatchTouchEvent(ev) || result;
132        ev.offsetLocation(-mTempRect.left, -mTempRect.top);
133        return result;
134    }
135
136    @Override
137    protected void dispatchDraw(Canvas canvas) {
138        super.dispatchDraw(canvas);
139        if (mViewMediatorCallback != null) {
140            mViewMediatorCallback.keyguardDoneDrawing();
141        }
142    }
143
144    private int getWidgetPosition(int id) {
145        final int children = mAppWidgetContainer.getChildCount();
146        for (int i = 0; i < children; i++) {
147            if (mAppWidgetContainer.getChildAt(i).getId() == id) {
148                return i;
149            }
150        }
151        return -1;
152    }
153
154    @Override
155    protected void onFinishInflate() {
156        mAppWidgetRegion = (KeyguardWidgetRegion) findViewById(R.id.kg_widget_region);
157        mAppWidgetRegion.setVisibility(VISIBLE);
158        mAppWidgetRegion.setCallbacks(mWidgetCallbacks);
159
160        mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
161        mSecurityViewContainer = (ViewFlipper) findViewById(R.id.view_flipper);
162        mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view);
163
164        addDefaultWidgets();
165        updateSecurityViews();
166        setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
167    }
168
169    private void updateSecurityViews() {
170        int children = mSecurityViewContainer.getChildCount();
171        for (int i = 0; i < children; i++) {
172            updateSecurityView(mSecurityViewContainer.getChildAt(i));
173        }
174    }
175
176    private void updateSecurityView(View view) {
177        if (view instanceof KeyguardSecurityView) {
178            KeyguardSecurityView ksv = (KeyguardSecurityView) view;
179            ksv.setKeyguardCallback(mCallback);
180            ksv.setLockPatternUtils(mLockPatternUtils);
181        } else {
182            Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
183        }
184    }
185
186    void setLockPatternUtils(LockPatternUtils utils) {
187        mSecurityModel.setLockPatternUtils(utils);
188        mLockPatternUtils = utils;
189        updateSecurityViews();
190    }
191
192    @Override
193    protected void onAttachedToWindow() {
194        super.onAttachedToWindow();
195        mAppWidgetHost.startListening();
196        maybePopulateWidgets();
197        disableStatusViewInteraction();
198        post(mSwitchPageRunnable);
199    }
200
201    private void disableStatusViewInteraction() {
202        // Disable all user interaction on status view. This is done to prevent falsing in the
203        // pocket from triggering useractivity and prevents 3rd party replacement widgets
204        // from responding to user interaction while in this position.
205        View statusView = findViewById(R.id.keyguard_status_view);
206        if (statusView instanceof KeyguardWidgetFrame) {
207            ((KeyguardWidgetFrame) statusView).setDisableUserInteraction(true);
208        }
209    }
210
211    @Override
212    protected void onDetachedFromWindow() {
213        super.onDetachedFromWindow();
214        mAppWidgetHost.stopListening();
215    }
216
217    private AppWidgetHost getAppWidgetHost() {
218        return mAppWidgetHost;
219    }
220
221    void addWidget(AppWidgetHostView view) {
222        mAppWidgetContainer.addWidget(view);
223    }
224
225    private KeyguardWidgetRegion.Callbacks mWidgetCallbacks
226            = new KeyguardWidgetRegion.Callbacks() {
227        @Override
228        public void userActivity() {
229            if (mViewMediatorCallback != null) {
230                mViewMediatorCallback.userActivity();
231            }
232        }
233
234        @Override
235        public void onUserActivityTimeoutChanged() {
236            if (mViewMediatorCallback != null) {
237                mViewMediatorCallback.onUserActivityTimeoutChanged();
238            }
239        }
240    };
241
242    @Override
243    public long getUserActivityTimeout() {
244        // Currently only considering user activity timeouts needed by widgets.
245        // Could also take into account longer timeouts for certain security views.
246        if (mAppWidgetRegion != null) {
247            return mAppWidgetRegion.getUserActivityTimeout();
248        }
249        return -1;
250    }
251
252    private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
253
254        public void userActivity(long timeout) {
255            if (mViewMediatorCallback != null) {
256                mViewMediatorCallback.userActivity(timeout);
257            }
258        }
259
260        public void dismiss(boolean authenticated) {
261            showNextSecurityScreenOrFinish(authenticated);
262        }
263
264        public boolean isVerifyUnlockOnly() {
265            return mIsVerifyUnlockOnly;
266        }
267
268        public void reportSuccessfulUnlockAttempt() {
269            KeyguardUpdateMonitor.getInstance(mContext).clearFailedUnlockAttempts();
270            mLockPatternUtils.reportSuccessfulPasswordAttempt();
271        }
272
273        public void reportFailedUnlockAttempt() {
274            if (mCurrentSecuritySelection == SecurityMode.Biometric) {
275                KeyguardUpdateMonitor.getInstance(mContext).reportFailedBiometricUnlockAttempt();
276            } else {
277                KeyguardHostView.this.reportFailedUnlockAttempt();
278            }
279        }
280
281        public int getFailedAttempts() {
282            return KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts();
283        }
284
285        @Override
286        public void showBackupSecurity() {
287            KeyguardHostView.this.showBackupSecurity();
288        }
289
290        @Override
291        public void setOnDismissRunnable(Runnable runnable) {
292            KeyguardHostView.this.setOnDismissRunnable(runnable);
293        }
294
295    };
296
297    private void showDialog(String title, String message) {
298        final AlertDialog dialog = new AlertDialog.Builder(mContext)
299            .setTitle(title)
300            .setMessage(message)
301            .setNeutralButton(com.android.internal.R.string.ok, null)
302            .create();
303        if (!(mContext instanceof Activity)) {
304            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
305        }
306        dialog.show();
307    }
308
309    private void showTimeoutDialog() {
310        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
311        int messageId = 0;
312
313        switch (mSecurityModel.getSecurityMode()) {
314            case Pattern:
315                messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
316                break;
317
318            case Password: {
319                    final boolean isPin = mLockPatternUtils.getKeyguardStoredPasswordQuality() ==
320                        DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
321                    messageId = isPin ? R.string.kg_too_many_failed_pin_attempts_dialog_message
322                            : R.string.kg_too_many_failed_password_attempts_dialog_message;
323                }
324                break;
325        }
326
327        if (messageId != 0) {
328            final String message = mContext.getString(messageId,
329                    KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(),
330                    timeoutInSeconds);
331            showDialog(null, message);
332        }
333    }
334
335    private void showAlmostAtWipeDialog(int attempts, int remaining) {
336        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
337        String message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe,
338                attempts, remaining);
339        showDialog(null, message);
340    }
341
342    private void showWipeDialog(int attempts) {
343        String message = mContext.getString(R.string.kg_failed_attempts_now_wiping, attempts);
344        showDialog(null, message);
345    }
346
347    private void showAlmostAtAccountLoginDialog() {
348        final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
349        final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
350                - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
351        String message = mContext.getString(R.string.kg_failed_attempts_almost_at_login,
352                count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds);
353        showDialog(null, message);
354    }
355
356    private void reportFailedUnlockAttempt() {
357        final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
358        final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time
359
360        if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
361
362        SecurityMode mode = mSecurityModel.getSecurityMode();
363        final boolean usingPattern = mode == KeyguardSecurityModel.SecurityMode.Pattern;
364
365        final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager()
366                .getMaximumFailedPasswordsForWipe(null, mLockPatternUtils.getCurrentUser());
367
368        final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
369                - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
370
371        final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
372                (failedAttemptsBeforeWipe - failedAttempts)
373                : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
374
375        boolean showTimeout = false;
376        if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
377            // If we reach this code, it means the user has installed a DevicePolicyManager
378            // that requests device wipe after N attempts.  Once we get below the grace
379            // period, we'll post this dialog every time as a clear warning until the
380            // bombshell hits and the device is wiped.
381            if (remainingBeforeWipe > 0) {
382                showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe);
383            } else {
384                // Too many attempts. The device will be wiped shortly.
385                Slog.i(TAG, "Too many unlock attempts; device will be wiped!");
386                showWipeDialog(failedAttempts);
387            }
388        } else {
389            showTimeout =
390                (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
391            if (usingPattern && mEnableFallback) {
392                if (failedAttempts == failedAttemptWarning) {
393                    showAlmostAtAccountLoginDialog();
394                    showTimeout = false; // don't show both dialogs
395                } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
396                    mLockPatternUtils.setPermanentlyLocked(true);
397                    showSecurityScreen(SecurityMode.Account);
398                    // don't show timeout dialog because we show account unlock screen next
399                    showTimeout = false;
400                }
401            }
402        }
403        monitor.reportFailedUnlockAttempt();
404        mLockPatternUtils.reportFailedPasswordAttempt();
405        if (showTimeout) {
406            showTimeoutDialog();
407        }
408    }
409
410    /**
411     * Shows the backup security screen for the current security mode.  This could be used for
412     * password recovery screens but is currently only used for pattern unlock to show the
413     * account unlock screen and biometric unlock to show the user's normal unlock.
414     */
415    private void showBackupSecurity() {
416        showSecurityScreen(mSecurityModel.getBackupSecurityMode());
417    }
418
419    public boolean showNextSecurityScreenIfPresent() {
420        SecurityMode securityMode = mSecurityModel.getSecurityMode();
421        // Allow an alternate, such as biometric unlock
422        securityMode = mSecurityModel.getAlternateFor(securityMode);
423        if (SecurityMode.None == securityMode) {
424            return false;
425        } else {
426            showSecurityScreen(securityMode); // switch to the alternate security view
427            return true;
428        }
429    }
430
431    private void showNextSecurityScreenOrFinish(boolean authenticated) {
432        boolean finish = false;
433        if (SecurityMode.None == mCurrentSecuritySelection) {
434            SecurityMode securityMode = mSecurityModel.getSecurityMode();
435            // Allow an alternate, such as biometric unlock
436            securityMode = mSecurityModel.getAlternateFor(securityMode);
437            if (SecurityMode.None == securityMode) {
438                finish = true; // no security required
439            } else {
440                showSecurityScreen(securityMode); // switch to the alternate security view
441            }
442        } else if (authenticated) {
443            switch (mCurrentSecuritySelection) {
444                case Pattern:
445                case Password:
446                case Account:
447                case Biometric:
448                    finish = true;
449                    break;
450
451                case SimPin:
452                case SimPuk:
453                    // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
454                    SecurityMode securityMode = mSecurityModel.getSecurityMode();
455                    if (securityMode != SecurityMode.None) {
456                        showSecurityScreen(securityMode);
457                    } else {
458                        finish = true;
459                    }
460                    break;
461
462                default:
463                    showSecurityScreen(SecurityMode.None);
464                    break;
465            }
466        } else {
467            // Not authenticated but we were asked to dismiss so go back to selector screen.
468            showSecurityScreen(SecurityMode.None);
469        }
470        if (finish) {
471            // If there's a pending runnable because the user interacted with a widget
472            // and we're leaving keyguard, then run it.
473            if (mLaunchRunnable != null) {
474                mLaunchRunnable.run();
475                mLaunchRunnable = null;
476            }
477            if (mViewMediatorCallback != null) {
478                mViewMediatorCallback.keyguardDone(true);
479            }
480        }
481    }
482
483    private OnClickHandler mOnClickHandler = new OnClickHandler() {
484        @Override
485        public boolean onClickHandler(final View view,
486                final android.app.PendingIntent pendingIntent,
487                final Intent fillInIntent) {
488            if (pendingIntent.isActivity()) {
489                setOnDismissRunnable(new Runnable() {
490                    public void run() {
491                        try {
492                              // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
493                              Context context = view.getContext();
494                              ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view,
495                                      0, 0,
496                                      view.getMeasuredWidth(), view.getMeasuredHeight());
497                              context.startIntentSender(
498                                      pendingIntent.getIntentSender(), fillInIntent,
499                                      Intent.FLAG_ACTIVITY_NEW_TASK,
500                                      Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
501                          } catch (IntentSender.SendIntentException e) {
502                              android.util.Log.e(TAG, "Cannot send pending intent: ", e);
503                          } catch (Exception e) {
504                              android.util.Log.e(TAG, "Cannot send pending intent due to " +
505                                      "unknown exception: ", e);
506                          }
507                    }
508                });
509
510                mCallback.dismiss(false);
511                return true;
512            } else {
513                return super.onClickHandler(view, pendingIntent, fillInIntent);
514            }
515        };
516    };
517
518    @Override
519    public void reset() {
520        mIsVerifyUnlockOnly = false;
521        mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_status_view));
522        requestFocus();
523    }
524
525    /**
526     *  Sets a runnable to run when keyguard is dismissed
527     * @param runnable
528     */
529    protected void setOnDismissRunnable(Runnable runnable) {
530        mLaunchRunnable = runnable;
531    }
532
533    private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
534        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
535        KeyguardSecurityView view = null;
536        final int children = mSecurityViewContainer.getChildCount();
537        for (int child = 0; child < children; child++) {
538            if (mSecurityViewContainer.getChildAt(child).getId() == securityViewIdForMode) {
539                view = ((KeyguardSecurityView)mSecurityViewContainer.getChildAt(child));
540                break;
541            }
542        }
543        if (view == null) {
544            final LayoutInflater inflater = LayoutInflater.from(mContext);
545            View v = inflater.inflate(getLayoutIdFor(securityMode), this, false);
546            mSecurityViewContainer.addView(v);
547            updateSecurityView(v);
548            view = (KeyguardSecurityView) v;
549        }
550        return view;
551    }
552
553    /**
554     * Switches to the given security view unless it's already being shown, in which case
555     * this is a no-op.
556     *
557     * @param securityMode
558     */
559    private void showSecurityScreen(SecurityMode securityMode) {
560
561        if (securityMode == mCurrentSecuritySelection) return;
562
563        KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
564        KeyguardSecurityView newView = getSecurityView(securityMode);
565
566        // Emulate Activity life cycle
567        oldView.onPause();
568        newView.onResume();
569
570        final boolean needsInput = newView.needsInput();
571        if (mViewMediatorCallback != null) {
572            mViewMediatorCallback.setNeedsInput(needsInput);
573        }
574
575        // Find and show this child.
576        final int childCount = mSecurityViewContainer.getChildCount();
577
578        // If we're go to/from the selector view, do flip animation, otherwise use fade animation.
579        final boolean doFlip = mCurrentSecuritySelection == SecurityMode.None
580                || securityMode == SecurityMode.None;
581        final int inAnimation = doFlip ? R.anim.keyguard_security_animate_in
582                : R.anim.keyguard_security_fade_in;
583        final int outAnimation = doFlip ? R.anim.keyguard_security_animate_out
584                : R.anim.keyguard_security_fade_out;
585
586        mSecurityViewContainer.setInAnimation(AnimationUtils.loadAnimation(mContext, inAnimation));
587        mSecurityViewContainer.setOutAnimation(AnimationUtils.loadAnimation(mContext, outAnimation));
588        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
589        for (int i = 0; i < childCount; i++) {
590            if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) {
591                mSecurityViewContainer.setDisplayedChild(i);
592                break;
593            }
594        }
595
596
597        if (securityMode == SecurityMode.None) {
598            // Discard current runnable if we're switching back to the selector view
599            setOnDismissRunnable(null);
600            setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
601        } else {
602            setSystemUiVisibility(getSystemUiVisibility() & (~View.STATUS_BAR_DISABLE_BACK));
603        }
604
605        mCurrentSecuritySelection = securityMode;
606    }
607
608    @Override
609    public void onScreenTurnedOn() {
610        if (DEBUG) Log.d(TAG, "screen on");
611        showSecurityScreen(mCurrentSecuritySelection);
612        getSecurityView(mCurrentSecuritySelection).onResume();
613
614        // This is a an attempt to fix bug 7137389 where the device comes back on but the entire
615        // layout is blank but forcing a layout causes it to reappear (e.g. with with
616        // hierarchyviewer).
617        requestLayout();
618    }
619
620    @Override
621    public void onScreenTurnedOff() {
622        if (DEBUG) Log.d(TAG, "screen off");
623        showSecurityScreen(SecurityMode.None);
624        getSecurityView(mCurrentSecuritySelection).onPause();
625    }
626
627    @Override
628    public void show() {
629        onScreenTurnedOn();
630    }
631
632    private boolean isSecure() {
633        SecurityMode mode = mSecurityModel.getSecurityMode();
634        switch (mode) {
635            case Pattern:
636                return mLockPatternUtils.isLockPatternEnabled();
637            case Password:
638                return mLockPatternUtils.isLockPasswordEnabled();
639            case SimPin:
640            case SimPuk:
641            case Account:
642                return true;
643            case None:
644                return false;
645            default:
646                throw new IllegalStateException("Unknown security mode " + mode);
647        }
648    }
649
650    @Override
651    public void wakeWhenReadyTq(int keyCode) {
652        if (DEBUG) Log.d(TAG, "onWakeKey");
653        if (keyCode == KeyEvent.KEYCODE_MENU && isSecure()) {
654            if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU");
655            showSecurityScreen(SecurityMode.None);
656        } else {
657            if (DEBUG) Log.d(TAG, "poking wake lock immediately");
658        }
659        if (mViewMediatorCallback != null) {
660            mViewMediatorCallback.wakeUp();
661        }
662    }
663
664    @Override
665    public void verifyUnlock() {
666        SecurityMode securityMode = mSecurityModel.getSecurityMode();
667        if (securityMode == KeyguardSecurityModel.SecurityMode.None) {
668            if (mViewMediatorCallback != null) {
669                mViewMediatorCallback.keyguardDone(true);
670            }
671        } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
672                && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
673            // can only verify unlock when in pattern/password mode
674            if (mViewMediatorCallback != null) {
675                mViewMediatorCallback.keyguardDone(false);
676            }
677        } else {
678            // otherwise, go to the unlock screen, see if they can verify it
679            mIsVerifyUnlockOnly = true;
680            showSecurityScreen(securityMode);
681        }
682    }
683
684    private int getSecurityViewIdForMode(SecurityMode securityMode) {
685        switch (securityMode) {
686            case None: return R.id.keyguard_selector_view;
687            case Pattern: return R.id.keyguard_pattern_view;
688            case Password: return R.id.keyguard_password_view;
689            case Biometric: return R.id.keyguard_face_unlock_view;
690            case Account: return R.id.keyguard_account_view;
691            case SimPin: return R.id.keyguard_sim_pin_view;
692            case SimPuk: return R.id.keyguard_sim_puk_view;
693        }
694        return 0;
695    }
696
697    private int getLayoutIdFor(SecurityMode securityMode) {
698        switch (securityMode) {
699            case None: return R.layout.keyguard_selector_view;
700            case Pattern: return R.layout.keyguard_pattern_view;
701            case Password: return R.layout.keyguard_password_view;
702            case Biometric: return R.layout.keyguard_face_unlock_view;
703            case Account: return R.layout.keyguard_account_view;
704            case SimPin: return R.layout.keyguard_sim_pin_view;
705            case SimPuk: return R.layout.keyguard_sim_puk_view;
706            default:
707                throw new RuntimeException("No layout for securityMode " + securityMode);
708        }
709    }
710
711    private void addWidget(int appId) {
712        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
713        AppWidgetProviderInfo appWidgetInfo = appWidgetManager.getAppWidgetInfo(appId);
714        if (appWidgetInfo != null) {
715            AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo);
716            addWidget(view);
717        } else {
718            Log.w(TAG, "AppWidgetInfo was null; not adding widget id " + appId);
719        }
720    }
721
722    private void addDefaultWidgets() {
723        LayoutInflater inflater = LayoutInflater.from(mContext);
724        inflater.inflate(R.layout.keyguard_status_view, mAppWidgetContainer, true);
725        inflater.inflate(R.layout.keyguard_transport_control_view, this, true);
726
727        inflateAndAddUserSelectorWidgetIfNecessary();
728        initializeTransportControl();
729    }
730
731    private void initializeTransportControl() {
732        mTransportControl =
733            (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control);
734        mTransportControl.setVisibility(View.GONE);
735
736        // This code manages showing/hiding the transport control. We keep it around and only
737        // add it to the hierarchy if it needs to be present.
738        if (mTransportControl != null) {
739            mTransportControl.setKeyguardCallback(new TransportCallback() {
740                @Override
741                public void onListenerDetached() {
742                    int page = getWidgetPosition(R.id.keyguard_transport_control);
743                    if (page != -1) {
744                        if (page == mAppWidgetContainer.getCurrentPage()) {
745                            // Switch back to clock view if music was showing.
746                            mAppWidgetContainer
747                                .setCurrentPage(getWidgetPosition(R.id.keyguard_status_view));
748                        }
749                        mAppWidgetContainer.removeView(mTransportControl);
750                        // XXX keep view attached to hierarchy so we still get show/hide events
751                        // from AudioManager
752                        KeyguardHostView.this.addView(mTransportControl);
753                        mTransportControl.setVisibility(View.GONE);
754                        mTransportState = TRANSPORT_GONE;
755                    }
756                }
757
758                @Override
759                public void onListenerAttached() {
760                    if (getWidgetPosition(R.id.keyguard_transport_control) == -1) {
761                        KeyguardHostView.this.removeView(mTransportControl);
762                        mAppWidgetContainer.addView(mTransportControl, 0);
763                        mTransportControl.setVisibility(View.VISIBLE);
764                    }
765                }
766
767                @Override
768                public void onPlayStateChanged() {
769                    mTransportControl.post(mSwitchPageRunnable);
770                }
771            });
772        }
773    }
774
775    private void maybePopulateWidgets() {
776        DevicePolicyManager dpm =
777                (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
778        if (dpm != null) {
779            final int currentUser = mLockPatternUtils.getCurrentUser();
780            final int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUser);
781            if ((disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0) {
782                Log.v(TAG, "Keyguard widgets disabled because of device policy admin");
783                return;
784            }
785        }
786
787        // Replace status widget if selected by user in Settings
788        int statusWidgetId = mLockPatternUtils.getStatusWidget();
789        if (statusWidgetId != -1) {
790            addWidget(statusWidgetId);
791            View newStatusWidget = mAppWidgetContainer.getChildAt(
792                    mAppWidgetContainer.getChildCount() - 1);
793
794            int oldStatusWidgetPosition = getWidgetPosition(R.id.keyguard_status_view);
795            mAppWidgetContainer.removeViewAt(oldStatusWidgetPosition);
796
797            // Re-add new status widget at position of old one
798            mAppWidgetContainer.removeView(newStatusWidget);
799            newStatusWidget.setId(R.id.keyguard_status_view);
800            mAppWidgetContainer.addView(newStatusWidget, oldStatusWidgetPosition);
801        }
802
803        // Add user-selected widget
804        final int[] widgets = mLockPatternUtils.getUserDefinedWidgets();
805        for (int i = 0; i < widgets.length; i++) {
806            if (widgets[i] != -1) {
807                addWidget(widgets[i]);
808            }
809        }
810    }
811
812    Runnable mSwitchPageRunnable = new Runnable() {
813        @Override
814        public void run() {
815           showAppropriateWidgetPage();
816        }
817    };
818
819    static class SavedState extends BaseSavedState {
820        int transportState;
821
822        SavedState(Parcelable superState) {
823            super(superState);
824        }
825
826        private SavedState(Parcel in) {
827            super(in);
828            this.transportState = in.readInt();
829        }
830
831        @Override
832        public void writeToParcel(Parcel out, int flags) {
833            super.writeToParcel(out, flags);
834            out.writeInt(this.transportState);
835        }
836
837        public static final Parcelable.Creator<SavedState> CREATOR
838                = new Parcelable.Creator<SavedState>() {
839            public SavedState createFromParcel(Parcel in) {
840                return new SavedState(in);
841            }
842
843            public SavedState[] newArray(int size) {
844                return new SavedState[size];
845            }
846        };
847    }
848
849    @Override
850    public Parcelable onSaveInstanceState() {
851        Parcelable superState = super.onSaveInstanceState();
852        SavedState ss = new SavedState(superState);
853        ss.transportState = mTransportState;
854        return ss;
855    }
856
857    @Override
858    public void onRestoreInstanceState(Parcelable state) {
859        if (!(state instanceof SavedState)) {
860            super.onRestoreInstanceState(state);
861            return;
862        }
863        SavedState ss = (SavedState) state;
864        super.onRestoreInstanceState(ss.getSuperState());
865        mTransportState = ss.transportState;
866        post(mSwitchPageRunnable);
867    }
868
869    private void showAppropriateWidgetPage() {
870
871        // The following sets the priority for showing widgets. Transport should be shown if
872        // music is playing, followed by the multi-user widget if enabled, followed by the
873        // status widget.
874        final int pageToShow;
875        if (mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE) {
876            mTransportState = TRANSPORT_VISIBLE;
877            pageToShow = mAppWidgetContainer.indexOfChild(mTransportControl);
878        } else {
879            UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
880            final View multiUserView = findViewById(R.id.keyguard_multi_user_selector);
881            final int multiUserPosition = mAppWidgetContainer.indexOfChild(multiUserView);
882            if (multiUserPosition != -1 && mUm.getUsers(true).size() > 1) {
883                pageToShow = multiUserPosition;
884            } else {
885                final View statusView = findViewById(R.id.keyguard_status_view);
886                pageToShow = mAppWidgetContainer.indexOfChild(statusView);
887            }
888            if (mTransportState == TRANSPORT_VISIBLE) {
889                mTransportState = TRANSPORT_INVISIBLE;
890            }
891        }
892        mAppWidgetContainer.setCurrentPage(pageToShow);
893    }
894
895    private void inflateAndAddUserSelectorWidgetIfNecessary() {
896        // if there are multiple users, we need to add the multi-user switcher widget to the
897        // keyguard.
898        UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
899        List<UserInfo> users = mUm.getUsers(true);
900
901        if (users.size() > 1) {
902            KeyguardWidgetFrame userSwitcher = (KeyguardWidgetFrame)
903                LayoutInflater.from(mContext).inflate(R.layout.keyguard_multi_user_selector_widget,
904                        mAppWidgetContainer, false);
905
906            // add the switcher in the first position
907            mAppWidgetContainer.addView(userSwitcher, getWidgetPosition(R.id.keyguard_status_view));
908            KeyguardMultiUserSelectorView multiUser = (KeyguardMultiUserSelectorView)
909                    userSwitcher.getChildAt(0);
910
911            UserSwitcherCallback callback = new UserSwitcherCallback() {
912                @Override
913                public void hideSecurityView(int duration) {
914                    mSecurityViewContainer.animate().alpha(0).setDuration(duration);
915                }
916
917                @Override
918                public void showSecurityView() {
919                    mSecurityViewContainer.setAlpha(1.0f);
920                }
921
922                @Override
923                public void showUnlockHint() {
924                    mKeyguardSelectorView.ping();
925                }
926
927            };
928            multiUser.setCallback(callback);
929        }
930    }
931
932    @Override
933    public void cleanUp() {
934
935    }
936
937    /**
938     * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
939     * some cases where we wish to disable it, notably when the menu button placement or technology
940     * is prone to false positives.
941     *
942     * @return true if the menu key should be enabled
943     */
944    private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
945    private boolean shouldEnableMenuKey() {
946        final Resources res = getResources();
947        final boolean configDisabled = res.getBoolean(
948                com.android.internal.R.bool.config_disableMenuKeyInLockScreen);
949        final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
950        final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
951        return !configDisabled || isTestHarness || fileOverride;
952    }
953
954    @Override
955    public boolean onKeyDown(int keyCode, KeyEvent event) {
956        if (keyCode == KeyEvent.KEYCODE_MENU && mEnableMenuKey) {
957            showNextSecurityScreenOrFinish(false);
958            return true;
959        } else {
960            return super.onKeyDown(keyCode, event);
961        }
962    }
963
964    public void goToUserSwitcher() {
965        mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_multi_user_selector));
966    }
967
968    public boolean handleBackKey() {
969        if (mCurrentSecuritySelection != SecurityMode.None) {
970            mCallback.dismiss(false);
971            return true;
972        }
973        return false;
974    }
975
976}
977