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