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