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