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