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.SearchManager;
24import android.app.admin.DevicePolicyManager;
25import android.appwidget.AppWidgetHost;
26import android.appwidget.AppWidgetHostView;
27import android.appwidget.AppWidgetManager;
28import android.appwidget.AppWidgetProviderInfo;
29import android.content.ActivityNotFoundException;
30import android.content.ComponentName;
31import android.content.Context;
32import android.content.Intent;
33import android.content.IntentSender;
34import android.content.pm.UserInfo;
35import android.content.res.Resources;
36import android.graphics.Canvas;
37import android.graphics.Rect;
38import android.os.Looper;
39import android.os.Parcel;
40import android.os.Parcelable;
41import android.os.SystemClock;
42import android.os.UserHandle;
43import android.os.UserManager;
44import android.util.AttributeSet;
45import android.util.Log;
46import android.util.Slog;
47import android.view.KeyEvent;
48import android.view.LayoutInflater;
49import android.view.MotionEvent;
50import android.view.View;
51import android.view.WindowManager;
52import android.view.animation.AnimationUtils;
53import android.widget.RemoteViews.OnClickHandler;
54
55import com.android.internal.R;
56import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode;
57import com.android.internal.widget.LockPatternUtils;
58
59import java.io.File;
60import java.util.List;
61
62public class KeyguardHostView extends KeyguardViewBase {
63    private static final String TAG = "KeyguardHostView";
64
65    // Use this to debug all of keyguard
66    public static boolean DEBUG = KeyguardViewMediator.DEBUG;
67
68    // Found in KeyguardAppWidgetPickActivity.java
69    static final int APPWIDGET_HOST_ID = 0x4B455947;
70
71    private final int MAX_WIDGETS = 5;
72
73    private AppWidgetHost mAppWidgetHost;
74    private AppWidgetManager mAppWidgetManager;
75    private KeyguardWidgetPager mAppWidgetContainer;
76    private KeyguardSecurityViewFlipper mSecurityViewContainer;
77    private KeyguardSelectorView mKeyguardSelectorView;
78    private KeyguardTransportControlView mTransportControl;
79    private boolean mIsVerifyUnlockOnly;
80    private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView
81    private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
82    private int mAppWidgetToShow;
83
84    private boolean mCheckAppWidgetConsistencyOnBootCompleted = false;
85
86    protected OnDismissAction mDismissAction;
87
88    protected int mFailedAttempts;
89    private LockPatternUtils mLockPatternUtils;
90
91    private KeyguardSecurityModel mSecurityModel;
92    private KeyguardViewStateManager mViewStateManager;
93
94    boolean mPersitentStickyWidgetLoaded = false;
95
96    private Rect mTempRect = new Rect();
97
98    private int mDisabledFeatures;
99
100    private boolean mCameraDisabled;
101
102    private boolean mSafeModeEnabled;
103
104    /*package*/ interface TransportCallback {
105        void onListenerDetached();
106        void onListenerAttached();
107        void onPlayStateChanged();
108    }
109
110    /*package*/ interface UserSwitcherCallback {
111        void hideSecurityView(int duration);
112        void showSecurityView();
113        void showUnlockHint();
114        void userActivity();
115    }
116
117    /*package*/ interface OnDismissAction {
118        /* returns true if the dismiss should be deferred */
119        boolean onDismiss();
120    }
121
122    public KeyguardHostView(Context context) {
123        this(context, null);
124    }
125
126    public KeyguardHostView(Context context, AttributeSet attrs) {
127        super(context, attrs);
128        mLockPatternUtils = new LockPatternUtils(context);
129        mAppWidgetHost = new AppWidgetHost(
130                context, APPWIDGET_HOST_ID, mOnClickHandler, Looper.myLooper());
131        mAppWidgetManager = AppWidgetManager.getInstance(mContext);
132        mSecurityModel = new KeyguardSecurityModel(context);
133
134        mViewStateManager = new KeyguardViewStateManager(this);
135
136        DevicePolicyManager dpm =
137            (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
138        if (dpm != null) {
139            mDisabledFeatures = getDisabledFeatures(dpm);
140            mCameraDisabled = dpm.getCameraDisabled(null);
141        }
142
143        mSafeModeEnabled = LockPatternUtils.isSafeModeEnabled();
144
145        if (mSafeModeEnabled) {
146            Log.v(TAG, "Keyguard widgets disabled by safe mode");
147        }
148        if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0) {
149            Log.v(TAG, "Keyguard widgets disabled by DPM");
150        }
151        if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0) {
152            Log.v(TAG, "Keyguard secure camera disabled by DPM");
153        }
154    }
155
156    private KeyguardUpdateMonitorCallback mUpdateMonitorCallbacks =
157            new KeyguardUpdateMonitorCallback() {
158        @Override
159        public void onBootCompleted() {
160            if (mCheckAppWidgetConsistencyOnBootCompleted) {
161                checkAppWidgetConsistency();
162                mSwitchPageRunnable.run();
163                mCheckAppWidgetConsistencyOnBootCompleted = false;
164            }
165        }
166    };
167
168    private SlidingChallengeLayout mSlidingChallengeLayout;
169
170    @Override
171    public boolean onTouchEvent(MotionEvent ev) {
172        boolean result = super.onTouchEvent(ev);
173        mTempRect.set(0, 0, 0, 0);
174        offsetRectIntoDescendantCoords(mSecurityViewContainer, mTempRect);
175        ev.offsetLocation(mTempRect.left, mTempRect.top);
176        result = mSecurityViewContainer.dispatchTouchEvent(ev) || result;
177        ev.offsetLocation(-mTempRect.left, -mTempRect.top);
178        return result;
179    }
180
181    @Override
182    protected void dispatchDraw(Canvas canvas) {
183        super.dispatchDraw(canvas);
184        if (mViewMediatorCallback != null) {
185            mViewMediatorCallback.keyguardDoneDrawing();
186        }
187    }
188
189    private int getWidgetPosition(int id) {
190        final int children = mAppWidgetContainer.getChildCount();
191        for (int i = 0; i < children; i++) {
192            if (mAppWidgetContainer.getWidgetPageAt(i).getContent().getId() == id) {
193                return i;
194            }
195        }
196        return -1;
197    }
198
199    @Override
200    protected void onFinishInflate() {
201        // Grab instances of and make any necessary changes to the main layouts. Create
202        // view state manager and wire up necessary listeners / callbacks.
203        View deleteDropTarget = findViewById(R.id.keyguard_widget_pager_delete_target);
204        mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
205        mAppWidgetContainer.setVisibility(VISIBLE);
206        mAppWidgetContainer.setCallbacks(mWidgetCallbacks);
207        mAppWidgetContainer.setDeleteDropTarget(deleteDropTarget);
208        mAppWidgetContainer.setMinScale(0.5f);
209
210        mSlidingChallengeLayout = (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
211        if (mSlidingChallengeLayout != null) {
212            mSlidingChallengeLayout.setOnChallengeScrolledListener(mViewStateManager);
213        }
214        mAppWidgetContainer.setViewStateManager(mViewStateManager);
215        mAppWidgetContainer.setLockPatternUtils(mLockPatternUtils);
216
217        ChallengeLayout challenge = mSlidingChallengeLayout != null ? mSlidingChallengeLayout :
218            (ChallengeLayout) findViewById(R.id.multi_pane_challenge);
219        challenge.setOnBouncerStateChangedListener(mViewStateManager);
220        mAppWidgetContainer.setBouncerAnimationDuration(challenge.getBouncerAnimationDuration());
221        mViewStateManager.setPagedView(mAppWidgetContainer);
222        mViewStateManager.setChallengeLayout(challenge);
223        mSecurityViewContainer = (KeyguardSecurityViewFlipper) findViewById(R.id.view_flipper);
224        mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view);
225        mViewStateManager.setSecurityViewContainer(mSecurityViewContainer);
226
227        if (!(mContext instanceof Activity)) {
228            setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
229        }
230
231        addDefaultWidgets();
232
233        addWidgetsFromSettings();
234        if (numWidgets() >= MAX_WIDGETS) {
235            setAddWidgetEnabled(false);
236        }
237        checkAppWidgetConsistency();
238        mSwitchPageRunnable.run();
239        // This needs to be called after the pages are all added.
240        mViewStateManager.showUsabilityHints();
241
242        showPrimarySecurityScreen(false);
243        updateSecurityViews();
244    }
245
246    private int getDisabledFeatures(DevicePolicyManager dpm) {
247        int disabledFeatures = DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
248        if (dpm != null) {
249            final int currentUser = mLockPatternUtils.getCurrentUser();
250            disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUser);
251        }
252        return disabledFeatures;
253    }
254
255    private boolean widgetsDisabledByDpm() {
256        return (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0;
257    }
258
259    private boolean cameraDisabledByDpm() {
260        return mCameraDisabled
261                || (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0;
262    }
263
264    private void updateSecurityViews() {
265        int children = mSecurityViewContainer.getChildCount();
266        for (int i = 0; i < children; i++) {
267            updateSecurityView(mSecurityViewContainer.getChildAt(i));
268        }
269    }
270
271    private void updateSecurityView(View view) {
272        if (view instanceof KeyguardSecurityView) {
273            KeyguardSecurityView ksv = (KeyguardSecurityView) view;
274            ksv.setKeyguardCallback(mCallback);
275            ksv.setLockPatternUtils(mLockPatternUtils);
276            if (mViewStateManager.isBouncing()) {
277                ksv.showBouncer(0);
278            } else {
279                ksv.hideBouncer(0);
280            }
281        } else {
282            Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
283        }
284    }
285
286    void setLockPatternUtils(LockPatternUtils utils) {
287        mSecurityModel.setLockPatternUtils(utils);
288        mLockPatternUtils = utils;
289        updateSecurityViews();
290    }
291
292    @Override
293    protected void onAttachedToWindow() {
294        super.onAttachedToWindow();
295        mAppWidgetHost.startListening();
296        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallbacks);
297    }
298
299    @Override
300    protected void onDetachedFromWindow() {
301        super.onDetachedFromWindow();
302        mAppWidgetHost.stopListening();
303        KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallbacks);
304    }
305
306    private AppWidgetHost getAppWidgetHost() {
307        return mAppWidgetHost;
308    }
309
310    void addWidget(AppWidgetHostView view, int pageIndex) {
311        mAppWidgetContainer.addWidget(view, pageIndex);
312    }
313
314    private KeyguardWidgetPager.Callbacks mWidgetCallbacks
315            = new KeyguardWidgetPager.Callbacks() {
316        @Override
317        public void userActivity() {
318            KeyguardHostView.this.userActivity();
319        }
320
321        @Override
322        public void onUserActivityTimeoutChanged() {
323            KeyguardHostView.this.onUserActivityTimeoutChanged();
324        }
325
326        @Override
327        public void onAddView(View v) {
328            if (numWidgets() >= MAX_WIDGETS) {
329                setAddWidgetEnabled(false);
330            }
331        };
332
333        @Override
334        public void onRemoveView(View v) {
335            if (numWidgets() < MAX_WIDGETS) {
336                setAddWidgetEnabled(true);
337            }
338        }
339    };
340
341    public void userActivity() {
342        if (mViewMediatorCallback != null) {
343            mViewMediatorCallback.userActivity();
344        }
345    }
346
347    public void onUserActivityTimeoutChanged() {
348        if (mViewMediatorCallback != null) {
349            mViewMediatorCallback.onUserActivityTimeoutChanged();
350        }
351    }
352
353    @Override
354    public long getUserActivityTimeout() {
355        // Currently only considering user activity timeouts needed by widgets.
356        // Could also take into account longer timeouts for certain security views.
357        if (mAppWidgetContainer != null) {
358            return mAppWidgetContainer.getUserActivityTimeout();
359        }
360        return -1;
361    }
362
363    private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
364
365        public void userActivity(long timeout) {
366            if (mViewMediatorCallback != null) {
367                mViewMediatorCallback.userActivity(timeout);
368            }
369        }
370
371        public void dismiss(boolean authenticated) {
372            showNextSecurityScreenOrFinish(authenticated);
373        }
374
375        public boolean isVerifyUnlockOnly() {
376            return mIsVerifyUnlockOnly;
377        }
378
379        public void reportSuccessfulUnlockAttempt() {
380            KeyguardUpdateMonitor.getInstance(mContext).clearFailedUnlockAttempts();
381            mLockPatternUtils.reportSuccessfulPasswordAttempt();
382        }
383
384        public void reportFailedUnlockAttempt() {
385            if (mCurrentSecuritySelection == SecurityMode.Biometric) {
386                KeyguardUpdateMonitor.getInstance(mContext).reportFailedBiometricUnlockAttempt();
387            } else {
388                KeyguardHostView.this.reportFailedUnlockAttempt();
389            }
390        }
391
392        public int getFailedAttempts() {
393            return KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts();
394        }
395
396        @Override
397        public void showBackupSecurity() {
398            KeyguardHostView.this.showBackupSecurityScreen();
399        }
400
401        @Override
402        public void setOnDismissAction(OnDismissAction action) {
403            KeyguardHostView.this.setOnDismissAction(action);
404        }
405
406    };
407
408    private void showDialog(String title, String message) {
409        final AlertDialog dialog = new AlertDialog.Builder(mContext)
410            .setTitle(title)
411            .setMessage(message)
412            .setNeutralButton(com.android.internal.R.string.ok, null)
413            .create();
414        if (!(mContext instanceof Activity)) {
415            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
416        }
417        dialog.show();
418    }
419
420    private void showTimeoutDialog() {
421        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
422        int messageId = 0;
423
424        switch (mSecurityModel.getSecurityMode()) {
425            case Pattern:
426                messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
427                break;
428            case PIN:
429                messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message;
430                break;
431            case Password:
432                messageId = R.string.kg_too_many_failed_password_attempts_dialog_message;
433                break;
434        }
435
436        if (messageId != 0) {
437            final String message = mContext.getString(messageId,
438                    KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(),
439                    timeoutInSeconds);
440            showDialog(null, message);
441        }
442    }
443
444    private void showAlmostAtWipeDialog(int attempts, int remaining) {
445        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
446        String message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe,
447                attempts, remaining);
448        showDialog(null, message);
449    }
450
451    private void showWipeDialog(int attempts) {
452        String message = mContext.getString(R.string.kg_failed_attempts_now_wiping, attempts);
453        showDialog(null, message);
454    }
455
456    private void showAlmostAtAccountLoginDialog() {
457        final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
458        final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
459                - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
460        String message = mContext.getString(R.string.kg_failed_attempts_almost_at_login,
461                count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds);
462        showDialog(null, message);
463    }
464
465    private void reportFailedUnlockAttempt() {
466        final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
467        final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time
468
469        if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
470
471        SecurityMode mode = mSecurityModel.getSecurityMode();
472        final boolean usingPattern = mode == KeyguardSecurityModel.SecurityMode.Pattern;
473
474        final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager()
475                .getMaximumFailedPasswordsForWipe(null, mLockPatternUtils.getCurrentUser());
476
477        final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
478                - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
479
480        final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
481                (failedAttemptsBeforeWipe - failedAttempts)
482                : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
483
484        boolean showTimeout = false;
485        if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
486            // If we reach this code, it means the user has installed a DevicePolicyManager
487            // that requests device wipe after N attempts.  Once we get below the grace
488            // period, we'll post this dialog every time as a clear warning until the
489            // bombshell hits and the device is wiped.
490            if (remainingBeforeWipe > 0) {
491                showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe);
492            } else {
493                // Too many attempts. The device will be wiped shortly.
494                Slog.i(TAG, "Too many unlock attempts; device will be wiped!");
495                showWipeDialog(failedAttempts);
496            }
497        } else {
498            showTimeout =
499                (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
500            if (usingPattern && mEnableFallback) {
501                if (failedAttempts == failedAttemptWarning) {
502                    showAlmostAtAccountLoginDialog();
503                    showTimeout = false; // don't show both dialogs
504                } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
505                    mLockPatternUtils.setPermanentlyLocked(true);
506                    showSecurityScreen(SecurityMode.Account);
507                    // don't show timeout dialog because we show account unlock screen next
508                    showTimeout = false;
509                }
510            }
511        }
512        monitor.reportFailedUnlockAttempt();
513        mLockPatternUtils.reportFailedPasswordAttempt();
514        if (showTimeout) {
515            showTimeoutDialog();
516        }
517    }
518
519    /**
520     * Shows the primary security screen for the user. This will be either the multi-selector
521     * or the user's security method.
522     * @param turningOff true if the device is being turned off
523     */
524    void showPrimarySecurityScreen(boolean turningOff) {
525        SecurityMode securityMode = mSecurityModel.getSecurityMode();
526        if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
527        if (!turningOff &&
528                KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()) {
529            // If we're not turning off, then allow biometric alternate.
530            // We'll reload it when the device comes back on.
531            securityMode = mSecurityModel.getAlternateFor(securityMode);
532        }
533        showSecurityScreen(securityMode);
534    }
535
536    /**
537     * Shows the backup security screen for the current security mode.  This could be used for
538     * password recovery screens but is currently only used for pattern unlock to show the
539     * account unlock screen and biometric unlock to show the user's normal unlock.
540     */
541    private void showBackupSecurityScreen() {
542        if (DEBUG) Log.d(TAG, "showBackupSecurity()");
543        SecurityMode backup = mSecurityModel.getBackupSecurityMode(mCurrentSecuritySelection);
544        showSecurityScreen(backup);
545    }
546
547    public boolean showNextSecurityScreenIfPresent() {
548        SecurityMode securityMode = mSecurityModel.getSecurityMode();
549        // Allow an alternate, such as biometric unlock
550        securityMode = mSecurityModel.getAlternateFor(securityMode);
551        if (SecurityMode.None == securityMode) {
552            return false;
553        } else {
554            showSecurityScreen(securityMode); // switch to the alternate security view
555            return true;
556        }
557    }
558
559    private void showNextSecurityScreenOrFinish(boolean authenticated) {
560        if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
561        boolean finish = false;
562        if (SecurityMode.None == mCurrentSecuritySelection) {
563            SecurityMode securityMode = mSecurityModel.getSecurityMode();
564            // Allow an alternate, such as biometric unlock
565            securityMode = mSecurityModel.getAlternateFor(securityMode);
566            if (SecurityMode.None == securityMode) {
567                finish = true; // no security required
568            } else {
569                showSecurityScreen(securityMode); // switch to the alternate security view
570            }
571        } else if (authenticated) {
572            switch (mCurrentSecuritySelection) {
573                case Pattern:
574                case Password:
575                case PIN:
576                case Account:
577                case Biometric:
578                    finish = true;
579                    break;
580
581                case SimPin:
582                case SimPuk:
583                    // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
584                    SecurityMode securityMode = mSecurityModel.getSecurityMode();
585                    if (securityMode != SecurityMode.None) {
586                        showSecurityScreen(securityMode);
587                    } else {
588                        finish = true;
589                    }
590                    break;
591
592                default:
593                    Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
594                    showPrimarySecurityScreen(false);
595                    break;
596            }
597        } else {
598            showPrimarySecurityScreen(false);
599        }
600        if (finish) {
601            // If the alternate unlock was suppressed, it can now be safely
602            // enabled because the user has left keyguard.
603            KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
604
605            // If there's a pending runnable because the user interacted with a widget
606            // and we're leaving keyguard, then run it.
607            boolean deferKeyguardDone = false;
608            if (mDismissAction != null) {
609                deferKeyguardDone = mDismissAction.onDismiss();
610                mDismissAction = null;
611            }
612            if (mViewMediatorCallback != null) {
613                if (deferKeyguardDone) {
614                    mViewMediatorCallback.keyguardDonePending();
615                } else {
616                    mViewMediatorCallback.keyguardDone(true);
617                }
618            }
619        } else {
620            mViewStateManager.showBouncer(true);
621        }
622    }
623
624    private OnClickHandler mOnClickHandler = new OnClickHandler() {
625        @Override
626        public boolean onClickHandler(final View view,
627                final android.app.PendingIntent pendingIntent,
628                final Intent fillInIntent) {
629            if (pendingIntent.isActivity()) {
630                setOnDismissAction(new OnDismissAction() {
631                    public boolean onDismiss() {
632                        try {
633                              // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
634                              Context context = view.getContext();
635                              ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view,
636                                      0, 0,
637                                      view.getMeasuredWidth(), view.getMeasuredHeight());
638                              context.startIntentSender(
639                                      pendingIntent.getIntentSender(), fillInIntent,
640                                      Intent.FLAG_ACTIVITY_NEW_TASK,
641                                      Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
642                        } catch (IntentSender.SendIntentException e) {
643                            android.util.Log.e(TAG, "Cannot send pending intent: ", e);
644                        } catch (Exception e) {
645                            android.util.Log.e(TAG, "Cannot send pending intent due to " +
646                                    "unknown exception: ", e);
647                        }
648                        return false;
649                    }
650                });
651
652                if (mViewStateManager.isChallengeShowing()) {
653                    mViewStateManager.showBouncer(true);
654                } else {
655                    mCallback.dismiss(false);
656                }
657                return true;
658            } else {
659                return super.onClickHandler(view, pendingIntent, fillInIntent);
660            }
661        };
662    };
663
664    // Used to ignore callbacks from methods that are no longer current (e.g. face unlock).
665    // This avoids unwanted asynchronous events from messing with the state.
666    private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
667
668        @Override
669        public void userActivity(long timeout) {
670        }
671
672        @Override
673        public void showBackupSecurity() {
674        }
675
676        @Override
677        public void setOnDismissAction(OnDismissAction action) {
678        }
679
680        @Override
681        public void reportSuccessfulUnlockAttempt() {
682        }
683
684        @Override
685        public void reportFailedUnlockAttempt() {
686        }
687
688        @Override
689        public boolean isVerifyUnlockOnly() {
690            return false;
691        }
692
693        @Override
694        public int getFailedAttempts() {
695            return 0;
696        }
697
698        @Override
699        public void dismiss(boolean securityVerified) {
700        }
701    };
702
703    protected boolean mShowSecurityWhenReturn;
704
705    @Override
706    public void reset() {
707        mIsVerifyUnlockOnly = false;
708        mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_status_view));
709    }
710
711    /**
712     * Sets an action to perform when keyguard is dismissed.
713     * @param action
714     */
715    protected void setOnDismissAction(OnDismissAction action) {
716        mDismissAction = action;
717    }
718
719    private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
720        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
721        KeyguardSecurityView view = null;
722        final int children = mSecurityViewContainer.getChildCount();
723        for (int child = 0; child < children; child++) {
724            if (mSecurityViewContainer.getChildAt(child).getId() == securityViewIdForMode) {
725                view = ((KeyguardSecurityView)mSecurityViewContainer.getChildAt(child));
726                break;
727            }
728        }
729        int layoutId = getLayoutIdFor(securityMode);
730        if (view == null && layoutId != 0) {
731            final LayoutInflater inflater = LayoutInflater.from(mContext);
732            if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
733            View v = inflater.inflate(layoutId, mSecurityViewContainer, false);
734            mSecurityViewContainer.addView(v);
735            updateSecurityView(v);
736            view = (KeyguardSecurityView)v;
737        }
738
739        if (view instanceof KeyguardSelectorView) {
740            KeyguardSelectorView selectorView = (KeyguardSelectorView) view;
741            View carrierText = selectorView.findViewById(R.id.keyguard_selector_fade_container);
742            selectorView.setCarrierArea(carrierText);
743        }
744
745        return view;
746    }
747
748    /**
749     * Switches to the given security view unless it's already being shown, in which case
750     * this is a no-op.
751     *
752     * @param securityMode
753     */
754    private void showSecurityScreen(SecurityMode securityMode) {
755        if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
756
757        if (securityMode == mCurrentSecuritySelection) return;
758
759        KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
760        KeyguardSecurityView newView = getSecurityView(securityMode);
761
762        // Enter full screen mode if we're in SIM or Account screen
763        boolean fullScreenEnabled = getResources().getBoolean(
764                com.android.internal.R.bool.kg_sim_puk_account_full_screen);
765        boolean isSimOrAccount = securityMode == SecurityMode.SimPin
766                || securityMode == SecurityMode.SimPuk
767                || securityMode == SecurityMode.Account;
768        mAppWidgetContainer.setVisibility(
769                isSimOrAccount && fullScreenEnabled ? View.GONE : View.VISIBLE);
770
771        if (mSlidingChallengeLayout != null) {
772            mSlidingChallengeLayout.setChallengeInteractive(!fullScreenEnabled);
773        }
774
775        // Emulate Activity life cycle
776        if (oldView != null) {
777            oldView.onPause();
778            oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
779        }
780        newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
781        newView.setKeyguardCallback(mCallback);
782
783        final boolean needsInput = newView.needsInput();
784        if (mViewMediatorCallback != null) {
785            mViewMediatorCallback.setNeedsInput(needsInput);
786        }
787
788        // Find and show this child.
789        final int childCount = mSecurityViewContainer.getChildCount();
790
791        mSecurityViewContainer.setInAnimation(
792                AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_fade_in));
793        mSecurityViewContainer.setOutAnimation(
794                AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_fade_out));
795        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
796        for (int i = 0; i < childCount; i++) {
797            if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) {
798                mSecurityViewContainer.setDisplayedChild(i);
799                break;
800            }
801        }
802
803        if (securityMode == SecurityMode.None) {
804            // Discard current runnable if we're switching back to the selector view
805            setOnDismissAction(null);
806        }
807        mCurrentSecuritySelection = securityMode;
808    }
809
810    @Override
811    public void onScreenTurnedOn() {
812        if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
813        showPrimarySecurityScreen(false);
814        getSecurityView(mCurrentSecuritySelection).onResume(KeyguardSecurityView.SCREEN_ON);
815
816        // This is a an attempt to fix bug 7137389 where the device comes back on but the entire
817        // layout is blank but forcing a layout causes it to reappear (e.g. with with
818        // hierarchyviewer).
819        requestLayout();
820
821        if (mViewStateManager != null) {
822            mViewStateManager.showUsabilityHints();
823        }
824    }
825
826    @Override
827    public void onScreenTurnedOff() {
828        if (DEBUG) Log.d(TAG, String.format("screen off, instance %s at %s",
829                Integer.toHexString(hashCode()), SystemClock.uptimeMillis()));
830        // Once the screen turns off, we no longer consider this to be first boot and we want the
831        // biometric unlock to start next time keyguard is shown.
832        KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
833        // We use mAppWidgetToShow to show a particular widget after you add it-- once the screen
834        // turns off we reset that behavior
835        clearAppWidgetToShow();
836        checkAppWidgetConsistency();
837        showPrimarySecurityScreen(true);
838        getSecurityView(mCurrentSecuritySelection).onPause();
839        CameraWidgetFrame cameraPage = findCameraPage();
840        if (cameraPage != null) {
841            cameraPage.onScreenTurnedOff();
842        }
843    }
844
845    public void clearAppWidgetToShow() {
846        mAppWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
847    }
848
849    @Override
850    public void show() {
851        if (DEBUG) Log.d(TAG, "show()");
852        showPrimarySecurityScreen(false);
853    }
854
855    private boolean isSecure() {
856        SecurityMode mode = mSecurityModel.getSecurityMode();
857        switch (mode) {
858            case Pattern:
859                return mLockPatternUtils.isLockPatternEnabled();
860            case Password:
861            case PIN:
862                return mLockPatternUtils.isLockPasswordEnabled();
863            case SimPin:
864            case SimPuk:
865            case Account:
866                return true;
867            case None:
868                return false;
869            default:
870                throw new IllegalStateException("Unknown security mode " + mode);
871        }
872    }
873
874    @Override
875    public void wakeWhenReadyTq(int keyCode) {
876        if (DEBUG) Log.d(TAG, "onWakeKey");
877        if (keyCode == KeyEvent.KEYCODE_MENU && isSecure()) {
878            if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU");
879            showSecurityScreen(SecurityMode.None);
880        } else {
881            if (DEBUG) Log.d(TAG, "poking wake lock immediately");
882        }
883        if (mViewMediatorCallback != null) {
884            mViewMediatorCallback.wakeUp();
885        }
886    }
887
888    @Override
889    public void verifyUnlock() {
890        SecurityMode securityMode = mSecurityModel.getSecurityMode();
891        if (securityMode == KeyguardSecurityModel.SecurityMode.None) {
892            if (mViewMediatorCallback != null) {
893                mViewMediatorCallback.keyguardDone(true);
894            }
895        } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
896                && securityMode != KeyguardSecurityModel.SecurityMode.PIN
897                && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
898            // can only verify unlock when in pattern/password mode
899            if (mViewMediatorCallback != null) {
900                mViewMediatorCallback.keyguardDone(false);
901            }
902        } else {
903            // otherwise, go to the unlock screen, see if they can verify it
904            mIsVerifyUnlockOnly = true;
905            showSecurityScreen(securityMode);
906        }
907    }
908
909    private int getSecurityViewIdForMode(SecurityMode securityMode) {
910        switch (securityMode) {
911            case None: return R.id.keyguard_selector_view;
912            case Pattern: return R.id.keyguard_pattern_view;
913            case PIN: return R.id.keyguard_pin_view;
914            case Password: return R.id.keyguard_password_view;
915            case Biometric: return R.id.keyguard_face_unlock_view;
916            case Account: return R.id.keyguard_account_view;
917            case SimPin: return R.id.keyguard_sim_pin_view;
918            case SimPuk: return R.id.keyguard_sim_puk_view;
919        }
920        return 0;
921    }
922
923    private int getLayoutIdFor(SecurityMode securityMode) {
924        switch (securityMode) {
925            case None: return R.layout.keyguard_selector_view;
926            case Pattern: return R.layout.keyguard_pattern_view;
927            case PIN: return R.layout.keyguard_pin_view;
928            case Password: return R.layout.keyguard_password_view;
929            case Biometric: return R.layout.keyguard_face_unlock_view;
930            case Account: return R.layout.keyguard_account_view;
931            case SimPin: return R.layout.keyguard_sim_pin_view;
932            case SimPuk: return R.layout.keyguard_sim_puk_view;
933            default:
934                return 0;
935        }
936    }
937
938    private boolean addWidget(int appId, int pageIndex, boolean updateDbIfFailed) {
939        AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appId);
940        if (appWidgetInfo != null) {
941            AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo);
942            addWidget(view, pageIndex);
943            return true;
944        } else {
945            if (updateDbIfFailed) {
946                Log.w(TAG, "AppWidgetInfo for app widget id " + appId + " was null, deleting");
947                mAppWidgetHost.deleteAppWidgetId(appId);
948                mLockPatternUtils.removeAppWidget(appId);
949            }
950            return false;
951        }
952    }
953
954    private final CameraWidgetFrame.Callbacks mCameraWidgetCallbacks =
955        new CameraWidgetFrame.Callbacks() {
956            @Override
957            public void onLaunchingCamera() {
958                setSliderHandleAlpha(0);
959            }
960
961            @Override
962            public void onCameraLaunchedSuccessfully() {
963                if (mAppWidgetContainer.isCameraPage(mAppWidgetContainer.getCurrentPage())) {
964                    mAppWidgetContainer.scrollLeft();
965                }
966                setSliderHandleAlpha(1);
967                mShowSecurityWhenReturn = true;
968            }
969
970            @Override
971            public void onCameraLaunchedUnsuccessfully() {
972                setSliderHandleAlpha(1);
973            }
974
975            private void setSliderHandleAlpha(float alpha) {
976                SlidingChallengeLayout slider =
977                        (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
978                if (slider != null) {
979                    slider.setHandleAlpha(alpha);
980                }
981            }
982        };
983
984    private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() {
985        @Override
986        Context getContext() {
987            return mContext;
988        }
989
990        @Override
991        KeyguardSecurityCallback getCallback() {
992            return mCallback;
993        }
994
995        @Override
996        LockPatternUtils getLockPatternUtils() {
997            return mLockPatternUtils;
998        }
999    };
1000
1001    private int numWidgets() {
1002        final int childCount = mAppWidgetContainer.getChildCount();
1003        int widgetCount = 0;
1004        for (int i = 0; i < childCount; i++) {
1005            if (mAppWidgetContainer.isWidgetPage(i)) {
1006                widgetCount++;
1007            }
1008        }
1009        return widgetCount;
1010    }
1011
1012
1013    private void setAddWidgetEnabled(boolean clickable) {
1014        View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget);
1015        if (addWidget != null) {
1016            View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view);
1017            addWidgetButton.setEnabled(clickable);
1018        }
1019    }
1020
1021    private void addDefaultWidgets() {
1022        LayoutInflater inflater = LayoutInflater.from(mContext);
1023        inflater.inflate(R.layout.keyguard_transport_control_view, this, true);
1024
1025        if (!mSafeModeEnabled && !widgetsDisabledByDpm()) {
1026            View addWidget = inflater.inflate(R.layout.keyguard_add_widget, this, false);
1027            mAppWidgetContainer.addWidget(addWidget, 0);
1028            View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view);
1029            addWidgetButton.setOnClickListener(new OnClickListener() {
1030                @Override
1031                public void onClick(View v) {
1032                    // Pass in an invalid widget id... the picker will allocate an ID for us
1033                    mActivityLauncher.launchWidgetPicker(AppWidgetManager.INVALID_APPWIDGET_ID);
1034                }
1035            });
1036        }
1037
1038        // We currently disable cameras in safe mode because we support loading 3rd party
1039        // cameras we can't trust.  TODO: plumb safe mode into camera creation code and only
1040        // inflate system-provided camera?
1041        if (!mSafeModeEnabled && !cameraDisabledByDpm()
1042                && mContext.getResources().getBoolean(R.bool.kg_enable_camera_default_widget)) {
1043            View cameraWidget =
1044                    CameraWidgetFrame.create(mContext, mCameraWidgetCallbacks, mActivityLauncher);
1045            if (cameraWidget != null) {
1046                mAppWidgetContainer.addWidget(cameraWidget);
1047            }
1048        }
1049
1050        enableUserSelectorIfNecessary();
1051        initializeTransportControl();
1052    }
1053
1054    private boolean removeTransportFromWidgetPager() {
1055        int page = getWidgetPosition(R.id.keyguard_transport_control);
1056        if (page != -1) {
1057            mAppWidgetContainer.removeWidget(mTransportControl);
1058
1059            // XXX keep view attached so we still get show/hide events from AudioManager
1060            KeyguardHostView.this.addView(mTransportControl);
1061            mTransportControl.setVisibility(View.GONE);
1062            mViewStateManager.setTransportState(KeyguardViewStateManager.TRANSPORT_GONE);
1063            return true;
1064        }
1065        return false;
1066    }
1067
1068    private void addTransportToWidgetPager() {
1069        if (getWidgetPosition(R.id.keyguard_transport_control) == -1) {
1070            KeyguardHostView.this.removeView(mTransportControl);
1071            // insert to left of camera if it exists, otherwise after right-most widget
1072            int lastWidget = mAppWidgetContainer.getChildCount() - 1;
1073            int position = 0; // handle no widget case
1074            if (lastWidget >= 0) {
1075                position = mAppWidgetContainer.isCameraPage(lastWidget) ?
1076                        lastWidget : lastWidget + 1;
1077            }
1078            mAppWidgetContainer.addWidget(mTransportControl, position);
1079            mTransportControl.setVisibility(View.VISIBLE);
1080        }
1081    }
1082
1083    private void initializeTransportControl() {
1084        mTransportControl =
1085            (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control);
1086        mTransportControl.setVisibility(View.GONE);
1087
1088        // This code manages showing/hiding the transport control. We keep it around and only
1089        // add it to the hierarchy if it needs to be present.
1090        if (mTransportControl != null) {
1091            mTransportControl.setKeyguardCallback(new TransportCallback() {
1092                @Override
1093                public void onListenerDetached() {
1094                    if (removeTransportFromWidgetPager()) {
1095                        mTransportControl.post(mSwitchPageRunnable);
1096                    }
1097                }
1098
1099                @Override
1100                public void onListenerAttached() {
1101                    // Transport will be added when playstate changes...
1102                    mTransportControl.post(mSwitchPageRunnable);
1103                }
1104
1105                @Override
1106                public void onPlayStateChanged() {
1107                    mTransportControl.post(mSwitchPageRunnable);
1108                }
1109            });
1110        }
1111    }
1112
1113    private int getInsertPageIndex() {
1114        View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget);
1115        int insertionIndex = mAppWidgetContainer.indexOfChild(addWidget);
1116        if (insertionIndex < 0) {
1117            insertionIndex = 0; // no add widget page found
1118        } else {
1119            insertionIndex++; // place after add widget
1120        }
1121        return insertionIndex;
1122    }
1123
1124    private void addDefaultStatusWidget(int index) {
1125        LayoutInflater inflater = LayoutInflater.from(mContext);
1126        View statusWidget = inflater.inflate(R.layout.keyguard_status_view, null, true);
1127        mAppWidgetContainer.addWidget(statusWidget, index);
1128    }
1129
1130    private void addWidgetsFromSettings() {
1131        if (mSafeModeEnabled || widgetsDisabledByDpm()) {
1132            return;
1133        }
1134
1135        int insertionIndex = getInsertPageIndex();
1136
1137        // Add user-selected widget
1138        final int[] widgets = mLockPatternUtils.getAppWidgets();
1139
1140        if (widgets == null) {
1141            Log.d(TAG, "Problem reading widgets");
1142        } else {
1143            for (int i = widgets.length -1; i >= 0; i--) {
1144                if (widgets[i] == LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) {
1145                    addDefaultStatusWidget(insertionIndex);
1146                } else {
1147                    // We add the widgets from left to right, starting after the first page after
1148                    // the add page. We count down, since the order will be persisted from right
1149                    // to left, starting after camera.
1150                    addWidget(widgets[i], insertionIndex, true);
1151                }
1152            }
1153        }
1154    }
1155
1156    private int allocateIdForDefaultAppWidget() {
1157        int appWidgetId;
1158        Resources res = getContext().getResources();
1159        ComponentName defaultAppWidget = new ComponentName(
1160                res.getString(R.string.widget_default_package_name),
1161                res.getString(R.string.widget_default_class_name));
1162
1163        // Note: we don't support configuring the widget
1164        appWidgetId = mAppWidgetHost.allocateAppWidgetId();
1165
1166        try {
1167            mAppWidgetManager.bindAppWidgetId(appWidgetId, defaultAppWidget);
1168
1169        } catch (IllegalArgumentException e) {
1170            Log.e(TAG, "Error when trying to bind default AppWidget: " + e);
1171            mAppWidgetHost.deleteAppWidgetId(appWidgetId);
1172            appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
1173        }
1174        return appWidgetId;
1175    }
1176    public void checkAppWidgetConsistency() {
1177        // Since this method may bind a widget (which we can't do until boot completed) we
1178        // may have to defer it until after boot complete.
1179        if (!KeyguardUpdateMonitor.getInstance(mContext).hasBootCompleted()) {
1180            mCheckAppWidgetConsistencyOnBootCompleted = true;
1181            return;
1182        }
1183        final int childCount = mAppWidgetContainer.getChildCount();
1184        boolean widgetPageExists = false;
1185        for (int i = 0; i < childCount; i++) {
1186            if (mAppWidgetContainer.isWidgetPage(i)) {
1187                widgetPageExists = true;
1188                break;
1189            }
1190        }
1191        if (!widgetPageExists) {
1192            final int insertPageIndex = getInsertPageIndex();
1193
1194            final boolean userAddedWidgetsEnabled = !widgetsDisabledByDpm();
1195            boolean addedDefaultAppWidget = false;
1196
1197            if (!mSafeModeEnabled) {
1198                if (userAddedWidgetsEnabled) {
1199                    int appWidgetId = allocateIdForDefaultAppWidget();
1200                    if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
1201                        addedDefaultAppWidget = addWidget(appWidgetId, insertPageIndex, true);
1202                    }
1203                } else {
1204                    // note: even if widgetsDisabledByDpm() returns true, we still bind/create
1205                    // the default appwidget if possible
1206                    int appWidgetId = mLockPatternUtils.getFallbackAppWidgetId();
1207                    if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
1208                        appWidgetId = allocateIdForDefaultAppWidget();
1209                        if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
1210                            mLockPatternUtils.writeFallbackAppWidgetId(appWidgetId);
1211                        }
1212                    }
1213                    if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
1214                        addedDefaultAppWidget = addWidget(appWidgetId, insertPageIndex, false);
1215                        if (!addedDefaultAppWidget) {
1216                            mAppWidgetHost.deleteAppWidgetId(appWidgetId);
1217                            mLockPatternUtils.writeFallbackAppWidgetId(
1218                                    AppWidgetManager.INVALID_APPWIDGET_ID);
1219                        }
1220                    }
1221                }
1222            }
1223
1224            // Use the built-in status/clock view if we can't inflate the default widget
1225            if (!addedDefaultAppWidget) {
1226                addDefaultStatusWidget(insertPageIndex);
1227            }
1228
1229            // trigger DB updates only if user-added widgets are enabled
1230            if (!mSafeModeEnabled && userAddedWidgetsEnabled) {
1231                mAppWidgetContainer.onAddView(
1232                        mAppWidgetContainer.getChildAt(insertPageIndex), insertPageIndex);
1233            }
1234        }
1235    }
1236
1237    Runnable mSwitchPageRunnable = new Runnable() {
1238        @Override
1239        public void run() {
1240           showAppropriateWidgetPage();
1241        }
1242    };
1243
1244    static class SavedState extends BaseSavedState {
1245        int transportState;
1246        int appWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
1247
1248        SavedState(Parcelable superState) {
1249            super(superState);
1250        }
1251
1252        private SavedState(Parcel in) {
1253            super(in);
1254            this.transportState = in.readInt();
1255            this.appWidgetToShow = in.readInt();
1256        }
1257
1258        @Override
1259        public void writeToParcel(Parcel out, int flags) {
1260            super.writeToParcel(out, flags);
1261            out.writeInt(this.transportState);
1262            out.writeInt(this.appWidgetToShow);
1263        }
1264
1265        public static final Parcelable.Creator<SavedState> CREATOR
1266                = new Parcelable.Creator<SavedState>() {
1267            public SavedState createFromParcel(Parcel in) {
1268                return new SavedState(in);
1269            }
1270
1271            public SavedState[] newArray(int size) {
1272                return new SavedState[size];
1273            }
1274        };
1275    }
1276
1277    @Override
1278    public Parcelable onSaveInstanceState() {
1279        if (DEBUG) Log.d(TAG, "onSaveInstanceState");
1280        Parcelable superState = super.onSaveInstanceState();
1281        SavedState ss = new SavedState(superState);
1282        ss.transportState = mViewStateManager.getTransportState();
1283        ss.appWidgetToShow = mAppWidgetToShow;
1284        return ss;
1285    }
1286
1287    @Override
1288    public void onRestoreInstanceState(Parcelable state) {
1289        if (DEBUG) Log.d(TAG, "onRestoreInstanceState");
1290        if (!(state instanceof SavedState)) {
1291            super.onRestoreInstanceState(state);
1292            return;
1293        }
1294        SavedState ss = (SavedState) state;
1295        super.onRestoreInstanceState(ss.getSuperState());
1296        mViewStateManager.setTransportState(ss.transportState);
1297        mAppWidgetToShow = ss.appWidgetToShow;
1298        post(mSwitchPageRunnable);
1299    }
1300
1301    @Override
1302    public void onWindowFocusChanged(boolean hasWindowFocus) {
1303        super.onWindowFocusChanged(hasWindowFocus);
1304        if (DEBUG) Log.d(TAG, "Window is " + (hasWindowFocus ? "focused" : "unfocused"));
1305        if (hasWindowFocus && mShowSecurityWhenReturn) {
1306            SlidingChallengeLayout slider =
1307                (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
1308            if (slider != null) {
1309                slider.setHandleAlpha(1);
1310                slider.showChallenge(true);
1311            }
1312            mShowSecurityWhenReturn = false;
1313        }
1314    }
1315
1316    private void showAppropriateWidgetPage() {
1317        int state = mViewStateManager.getTransportState();
1318        boolean isMusicPlaying = mTransportControl.isMusicPlaying()
1319                || state == KeyguardViewStateManager.TRANSPORT_VISIBLE;
1320        if (isMusicPlaying) {
1321            mViewStateManager.setTransportState(KeyguardViewStateManager.TRANSPORT_VISIBLE);
1322            addTransportToWidgetPager();
1323        } else if (state == KeyguardViewStateManager.TRANSPORT_VISIBLE) {
1324            mViewStateManager.setTransportState(KeyguardViewStateManager.TRANSPORT_INVISIBLE);
1325        }
1326        int pageToShow = getAppropriateWidgetPage(isMusicPlaying);
1327        mAppWidgetContainer.setCurrentPage(pageToShow);
1328    }
1329
1330    private CameraWidgetFrame findCameraPage() {
1331        for (int i = mAppWidgetContainer.getChildCount() - 1; i >= 0; i--) {
1332            if (mAppWidgetContainer.isCameraPage(i)) {
1333                return (CameraWidgetFrame) mAppWidgetContainer.getChildAt(i);
1334            }
1335        }
1336        return null;
1337    }
1338
1339    boolean isMusicPage(int pageIndex) {
1340        return pageIndex >= 0 && pageIndex == getWidgetPosition(R.id.keyguard_transport_control);
1341    }
1342
1343    private int getAppropriateWidgetPage(boolean isMusicPlaying) {
1344        // assumes at least one widget (besides camera + add)
1345        if (mAppWidgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) {
1346            final int childCount = mAppWidgetContainer.getChildCount();
1347            for (int i = 0; i < childCount; i++) {
1348                if (mAppWidgetContainer.getWidgetPageAt(i).getContentAppWidgetId()
1349                        == mAppWidgetToShow) {
1350                    return i;
1351                }
1352            }
1353            mAppWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
1354        }
1355        // if music playing, show transport
1356        if (isMusicPlaying) {
1357            if (DEBUG) Log.d(TAG, "Music playing, show transport");
1358            return mAppWidgetContainer.getWidgetPageIndex(mTransportControl);
1359        }
1360
1361        // else show the right-most widget (except for camera)
1362        int rightMost = mAppWidgetContainer.getChildCount() - 1;
1363        if (mAppWidgetContainer.isCameraPage(rightMost)) {
1364            rightMost--;
1365        }
1366        if (DEBUG) Log.d(TAG, "Show right-most page " + rightMost);
1367        return rightMost;
1368    }
1369
1370    private void enableUserSelectorIfNecessary() {
1371        if (!UserManager.supportsMultipleUsers()) {
1372            return; // device doesn't support multi-user mode
1373        }
1374        final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
1375        if (um == null) {
1376            Throwable t = new Throwable();
1377            t.fillInStackTrace();
1378            Log.e(TAG, "user service is null.", t);
1379            return;
1380        }
1381
1382        // if there are multiple users, we need to enable to multi-user switcher
1383        final List<UserInfo> users = um.getUsers(true);
1384        if (users == null) {
1385            Throwable t = new Throwable();
1386            t.fillInStackTrace();
1387            Log.e(TAG, "list of users is null.", t);
1388            return;
1389        }
1390
1391        final View multiUserView = findViewById(R.id.keyguard_user_selector);
1392        if (multiUserView == null) {
1393            Throwable t = new Throwable();
1394            t.fillInStackTrace();
1395            Log.e(TAG, "can't find user_selector in layout.", t);
1396            return;
1397        }
1398
1399        if (users.size() > 1) {
1400            if (multiUserView instanceof KeyguardMultiUserSelectorView) {
1401                KeyguardMultiUserSelectorView multiUser =
1402                        (KeyguardMultiUserSelectorView) multiUserView;
1403                multiUser.setVisibility(View.VISIBLE);
1404                multiUser.addUsers(users);
1405                UserSwitcherCallback callback = new UserSwitcherCallback() {
1406                    @Override
1407                    public void hideSecurityView(int duration) {
1408                        mSecurityViewContainer.animate().alpha(0).setDuration(duration);
1409                    }
1410
1411                    @Override
1412                    public void showSecurityView() {
1413                        mSecurityViewContainer.setAlpha(1.0f);
1414                    }
1415
1416                    @Override
1417                    public void showUnlockHint() {
1418                        if (mKeyguardSelectorView != null) {
1419                            mKeyguardSelectorView.showUsabilityHint();
1420                        }
1421                    }
1422
1423                    @Override
1424                    public void userActivity() {
1425                        if (mViewMediatorCallback != null) {
1426                            mViewMediatorCallback.userActivity();
1427                        }
1428                    }
1429                };
1430                multiUser.setCallback(callback);
1431            } else {
1432                Throwable t = new Throwable();
1433                t.fillInStackTrace();
1434                if (multiUserView == null) {
1435                    Log.e(TAG, "could not find the user_selector.", t);
1436                } else {
1437                    Log.e(TAG, "user_selector is the wrong type.", t);
1438                }
1439            }
1440        }
1441    }
1442
1443    @Override
1444    public void cleanUp() {
1445
1446    }
1447
1448    /**
1449     * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
1450     * some cases where we wish to disable it, notably when the menu button placement or technology
1451     * is prone to false positives.
1452     *
1453     * @return true if the menu key should be enabled
1454     */
1455    private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
1456    private boolean shouldEnableMenuKey() {
1457        final Resources res = getResources();
1458        final boolean configDisabled = res.getBoolean(
1459                com.android.internal.R.bool.config_disableMenuKeyInLockScreen);
1460        final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
1461        final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
1462        return !configDisabled || isTestHarness || fileOverride;
1463    }
1464
1465
1466
1467    public void goToUserSwitcher() {
1468        mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_multi_user_selector));
1469    }
1470
1471    public void goToWidget(int appWidgetId) {
1472        mAppWidgetToShow = appWidgetId;
1473        mSwitchPageRunnable.run();
1474    }
1475
1476    public boolean handleMenuKey() {
1477        // The following enables the MENU key to work for testing automation
1478        if (shouldEnableMenuKey()) {
1479            showNextSecurityScreenOrFinish(false);
1480            return true;
1481        }
1482        return false;
1483    }
1484
1485    public boolean handleBackKey() {
1486        if (mCurrentSecuritySelection != SecurityMode.None) {
1487            mCallback.dismiss(false);
1488            return true;
1489        }
1490        return false;
1491    }
1492
1493    /**
1494     *  Dismisses the keyguard by going to the next screen or making it gone.
1495     */
1496    public void dismiss() {
1497        showNextSecurityScreenOrFinish(false);
1498    }
1499
1500    public void showAssistant() {
1501        final Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
1502          .getAssistIntent(mContext, UserHandle.USER_CURRENT);
1503
1504        if (intent == null) return;
1505
1506        final ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
1507                R.anim.keyguard_action_assist_enter, R.anim.keyguard_action_assist_exit,
1508                getHandler(), null);
1509
1510        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1511
1512        mActivityLauncher.launchActivityWithAnimation(
1513                intent, false, opts.toBundle(), null, null);
1514    }
1515}
1516