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