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