KeyguardHostView.java revision dcc9681b0d4f52e1f441ef2abdda3eb949cc0c4b
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.Context;
29import android.content.Intent;
30import android.content.IntentSender;
31import android.content.pm.UserInfo;
32import android.content.res.Resources;
33import android.graphics.Canvas;
34import android.graphics.Rect;
35import android.os.Looper;
36import android.os.Parcel;
37import android.os.Parcelable;
38import android.os.UserHandle;
39import android.os.UserManager;
40import android.provider.Settings;
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;
51import android.widget.TextView;
52
53import com.android.internal.R;
54import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode;
55import com.android.internal.widget.LockPatternUtils;
56
57import java.io.File;
58import java.util.List;
59
60public class KeyguardHostView extends KeyguardViewBase {
61    private static final String TAG = "KeyguardViewHost";
62
63    // Use this to debug all of keyguard
64    public static boolean DEBUG;
65
66    // also referenced in SecuritySettings.java
67    static final int APPWIDGET_HOST_ID = 0x4B455947;
68
69    // transport control states
70    private static final int TRANSPORT_GONE = 0;
71    private static final int TRANSPORT_INVISIBLE = 1;
72    private static final int TRANSPORT_VISIBLE = 2;
73
74    private AppWidgetHost mAppWidgetHost;
75    private KeyguardWidgetPager mAppWidgetContainer;
76    private KeyguardSecurityViewFlipper mSecurityViewContainer;
77    private KeyguardSelectorView mKeyguardSelectorView;
78    private KeyguardTransportControlView mTransportControl;
79    private boolean mEnableMenuKey;
80    private boolean mIsVerifyUnlockOnly;
81    private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView
82    private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
83
84    protected Runnable mLaunchRunnable;
85
86    protected int mFailedAttempts;
87    private LockPatternUtils mLockPatternUtils;
88
89    private KeyguardSecurityModel mSecurityModel;
90    private KeyguardViewStateManager mViewStateManager;
91
92    private Rect mTempRect = new Rect();
93    private int mTransportState = TRANSPORT_GONE;
94
95    /*package*/ interface TransportCallback {
96        void onListenerDetached();
97        void onListenerAttached();
98        void onPlayStateChanged();
99    }
100
101    /*package*/ interface UserSwitcherCallback {
102        void hideSecurityView(int duration);
103        void showSecurityView();
104        void showUnlockHint();
105        void userActivity();
106    }
107
108    public KeyguardHostView(Context context) {
109        this(context, null);
110    }
111
112    public KeyguardHostView(Context context, AttributeSet attrs) {
113        super(context, attrs);
114        mLockPatternUtils = new LockPatternUtils(context);
115        mAppWidgetHost = new AppWidgetHost(
116                context, APPWIDGET_HOST_ID, mOnClickHandler, Looper.myLooper());
117        mSecurityModel = new KeyguardSecurityModel(context);
118
119        // The following enables the MENU key to work for testing automation
120        mEnableMenuKey = shouldEnableMenuKey();
121        setFocusable(true);
122        setFocusableInTouchMode(true);
123    }
124
125    @Override
126    public boolean onTouchEvent(MotionEvent ev) {
127        boolean result = super.onTouchEvent(ev);
128        mTempRect.set(0, 0, 0, 0);
129        offsetRectIntoDescendantCoords(mSecurityViewContainer, mTempRect);
130        ev.offsetLocation(mTempRect.left, mTempRect.top);
131        result = mSecurityViewContainer.dispatchTouchEvent(ev) || result;
132        ev.offsetLocation(-mTempRect.left, -mTempRect.top);
133        return result;
134    }
135
136    @Override
137    protected void dispatchDraw(Canvas canvas) {
138        super.dispatchDraw(canvas);
139        if (mViewMediatorCallback != null) {
140            mViewMediatorCallback.keyguardDoneDrawing();
141        }
142    }
143
144    private int getWidgetPosition(int id) {
145        final int children = mAppWidgetContainer.getChildCount();
146        for (int i = 0; i < children; i++) {
147            if (mAppWidgetContainer.getChildAt(i).getId() == id) {
148                return i;
149            }
150        }
151        return -1;
152    }
153
154    @Override
155    protected void onFinishInflate() {
156        // Grab instances of and make any necessary changes to the main layouts. Create
157        // view state manager and wire up necessary listeners / callbacks.
158        mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
159        mAppWidgetContainer.setVisibility(VISIBLE);
160        mAppWidgetContainer.setCallbacks(mWidgetCallbacks);
161        mAppWidgetContainer.setMinScale(0.5f);
162
163        addDefaultWidgets();
164        maybePopulateWidgets();
165
166        mViewStateManager = new KeyguardViewStateManager();
167        SlidingChallengeLayout slider =
168                (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
169        if (slider != null) {
170            slider.setOnChallengeScrolledListener(mViewStateManager);
171        }
172        mAppWidgetContainer.setViewStateManager(mViewStateManager);
173
174        mViewStateManager.setPagedView(mAppWidgetContainer);
175        mViewStateManager.setChallengeLayout(slider != null ? slider :
176                (ChallengeLayout) findViewById(R.id.multi_pane_challenge));
177        mSecurityViewContainer = (KeyguardSecurityViewFlipper) findViewById(R.id.view_flipper);
178        mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view);
179        mViewStateManager.setSecurityViewContainer(mSecurityViewContainer);
180
181        mViewStateManager.showUsabilityHints();
182
183        updateSecurityViews();
184        if (!(mContext instanceof Activity)) {
185            setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
186        }
187
188        if (KeyguardUpdateMonitor.getInstance(mContext).getIsFirstBoot()) {
189            showPrimarySecurityScreen(false);
190        }
191    }
192
193    private void updateSecurityViews() {
194        int children = mSecurityViewContainer.getChildCount();
195        for (int i = 0; i < children; i++) {
196            updateSecurityView(mSecurityViewContainer.getChildAt(i));
197        }
198    }
199
200    private void updateSecurityView(View view) {
201        if (view instanceof KeyguardSecurityView) {
202            KeyguardSecurityView ksv = (KeyguardSecurityView) view;
203            ksv.setKeyguardCallback(mCallback);
204            ksv.setLockPatternUtils(mLockPatternUtils);
205        } else {
206            Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
207        }
208    }
209
210    void setLockPatternUtils(LockPatternUtils utils) {
211        mSecurityModel.setLockPatternUtils(utils);
212        mLockPatternUtils = utils;
213        updateSecurityViews();
214    }
215
216    @Override
217    protected void onAttachedToWindow() {
218        super.onAttachedToWindow();
219        mAppWidgetHost.startListening();
220        post(mSwitchPageRunnable);
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 && KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()) {
429            // If we're not turning off, then allow biometric alternate.
430            // We'll reload it when the device comes back on.
431            securityMode = mSecurityModel.getAlternateFor(securityMode);
432        }
433        showSecurityScreen(securityMode);
434    }
435
436    /**
437     * Shows the backup security screen for the current security mode.  This could be used for
438     * password recovery screens but is currently only used for pattern unlock to show the
439     * account unlock screen and biometric unlock to show the user's normal unlock.
440     */
441    private void showBackupSecurityScreen() {
442        if (DEBUG) Log.d(TAG, "showBackupSecurity()");
443        SecurityMode backup = mSecurityModel.getBackupSecurityMode(mCurrentSecuritySelection);
444        showSecurityScreen(backup);
445    }
446
447    public boolean showNextSecurityScreenIfPresent() {
448        SecurityMode securityMode = mSecurityModel.getSecurityMode();
449        // Allow an alternate, such as biometric unlock
450        securityMode = mSecurityModel.getAlternateFor(securityMode);
451        if (SecurityMode.None == securityMode) {
452            return false;
453        } else {
454            showSecurityScreen(securityMode); // switch to the alternate security view
455            return true;
456        }
457    }
458
459    private void showNextSecurityScreenOrFinish(boolean authenticated) {
460        if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
461        boolean finish = false;
462        if (SecurityMode.None == mCurrentSecuritySelection) {
463            SecurityMode securityMode = mSecurityModel.getSecurityMode();
464            // Allow an alternate, such as biometric unlock
465            securityMode = mSecurityModel.getAlternateFor(securityMode);
466            if (SecurityMode.None == securityMode) {
467                finish = true; // no security required
468            } else {
469                showSecurityScreen(securityMode); // switch to the alternate security view
470            }
471        } else if (authenticated) {
472            switch (mCurrentSecuritySelection) {
473                case Pattern:
474                case Password:
475                case PIN:
476                case Account:
477                case Biometric:
478                    finish = true;
479                    break;
480
481                case SimPin:
482                case SimPuk:
483                    // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
484                    SecurityMode securityMode = mSecurityModel.getSecurityMode();
485                    if (securityMode != SecurityMode.None) {
486                        showSecurityScreen(securityMode);
487                    } else {
488                        finish = true;
489                    }
490                    break;
491
492                default:
493                    Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
494                    showPrimarySecurityScreen(false);
495                    break;
496            }
497        } else {
498            showPrimarySecurityScreen(false);
499        }
500        if (finish) {
501            // If the alternate unlock was suppressed, it can now be safely
502            // enabled because the user has left keyguard.
503            KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
504            KeyguardUpdateMonitor.getInstance(mContext).setIsFirstBoot(false);
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                mCallback.dismiss(false);
548                return true;
549            } else {
550                return super.onClickHandler(view, pendingIntent, fillInIntent);
551            }
552        };
553    };
554
555    private KeyguardStatusViewManager mKeyguardStatusViewManager;
556
557    // Used to ignore callbacks from methods that are no longer current (e.g. face unlock).
558    // This avoids unwanted asynchronous events from messing with the state.
559    private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
560
561        @Override
562        public void userActivity(long timeout) {
563        }
564
565        @Override
566        public void showBackupSecurity() {
567        }
568
569        @Override
570        public void setOnDismissRunnable(Runnable runnable) {
571        }
572
573        @Override
574        public void reportSuccessfulUnlockAttempt() {
575        }
576
577        @Override
578        public void reportFailedUnlockAttempt() {
579        }
580
581        @Override
582        public boolean isVerifyUnlockOnly() {
583            return false;
584        }
585
586        @Override
587        public int getFailedAttempts() {
588            return 0;
589        }
590
591        @Override
592        public void dismiss(boolean securityVerified) {
593        }
594    };
595
596    @Override
597    public void reset() {
598        mIsVerifyUnlockOnly = false;
599        mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_status_view));
600    }
601
602    /**
603     *  Sets a runnable to run when keyguard is dismissed
604     * @param runnable
605     */
606    protected void setOnDismissRunnable(Runnable runnable) {
607        mLaunchRunnable = runnable;
608    }
609
610    private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
611        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
612        KeyguardSecurityView view = null;
613        final int children = mSecurityViewContainer.getChildCount();
614        for (int child = 0; child < children; child++) {
615            if (mSecurityViewContainer.getChildAt(child).getId() == securityViewIdForMode) {
616                view = ((KeyguardSecurityView)mSecurityViewContainer.getChildAt(child));
617                break;
618            }
619        }
620        boolean simPukFullScreen = getResources().getBoolean(
621                com.android.internal.R.bool.kg_sim_puk_account_full_screen);
622        int layoutId = getLayoutIdFor(securityMode);
623        if (view == null && layoutId != 0) {
624            final LayoutInflater inflater = LayoutInflater.from(mContext);
625            if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
626            View v = inflater.inflate(layoutId, this, false);
627            mSecurityViewContainer.addView(v);
628            updateSecurityView(v);
629
630            view = (KeyguardSecurityView) v;
631            TextView navigationText = ((TextView) findViewById(R.id.keyguard_message_area));
632
633            // Some devices can fit a navigation area, others cannot. On devices that cannot,
634            // we display the security message in status area.
635            if (navigationText != null) {
636                view.setSecurityMessageDisplay(new KeyguardNavigationManager(navigationText));
637            } else {
638                view.setSecurityMessageDisplay(mKeyguardStatusViewManager);
639            }
640        }
641
642        if (securityMode == SecurityMode.SimPin || securityMode == SecurityMode.SimPuk ||
643            securityMode == SecurityMode.Account) {
644            if (simPukFullScreen) {
645                mAppWidgetContainer.setVisibility(View.GONE);
646            }
647        }
648
649        if (view instanceof KeyguardSelectorView) {
650            KeyguardSelectorView selectorView = (KeyguardSelectorView) view;
651            View carrierText = selectorView.findViewById(R.id.keyguard_selector_fade_container);
652            selectorView.setCarrierArea(carrierText);
653        }
654
655        return view;
656    }
657
658    /**
659     * Switches to the given security view unless it's already being shown, in which case
660     * this is a no-op.
661     *
662     * @param securityMode
663     */
664    private void showSecurityScreen(SecurityMode securityMode) {
665        if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
666
667        if (securityMode == mCurrentSecuritySelection) return;
668
669        KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
670        KeyguardSecurityView newView = getSecurityView(securityMode);
671
672        // Emulate Activity life cycle
673        if (oldView != null) {
674            oldView.onPause();
675            oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
676        }
677        newView.onResume();
678        newView.setKeyguardCallback(mCallback);
679
680        final boolean needsInput = newView.needsInput();
681        if (mViewMediatorCallback != null) {
682            mViewMediatorCallback.setNeedsInput(needsInput);
683        }
684
685        // Find and show this child.
686        final int childCount = mSecurityViewContainer.getChildCount();
687
688        // Do flip animation to the next screen
689        if (false) {
690            mSecurityViewContainer.setInAnimation(
691                    AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_animate_in));
692            mSecurityViewContainer.setOutAnimation(
693                    AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_animate_out));
694        }
695        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
696        for (int i = 0; i < childCount; i++) {
697            if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) {
698                mSecurityViewContainer.setDisplayedChild(i);
699                break;
700            }
701        }
702
703        if (securityMode == SecurityMode.None) {
704            // Discard current runnable if we're switching back to the selector view
705            setOnDismissRunnable(null);
706        }
707        mCurrentSecuritySelection = securityMode;
708    }
709
710    @Override
711    public void onScreenTurnedOn() {
712        if (DEBUG) Log.d(TAG, "screen on");
713        showPrimarySecurityScreen(false);
714        getSecurityView(mCurrentSecuritySelection).onResume();
715
716        // This is a an attempt to fix bug 7137389 where the device comes back on but the entire
717        // layout is blank but forcing a layout causes it to reappear (e.g. with with
718        // hierarchyviewer).
719        requestLayout();
720
721        if (mViewStateManager != null) {
722            mViewStateManager.showUsabilityHints();
723        }
724    }
725
726    @Override
727    public void onScreenTurnedOff() {
728        if (DEBUG) Log.d(TAG, "screen off");
729        showPrimarySecurityScreen(true);
730        getSecurityView(mCurrentSecuritySelection).onPause();
731    }
732
733    @Override
734    public void show() {
735        if (DEBUG) Log.d(TAG, "show()");
736        showPrimarySecurityScreen(false);
737    }
738
739    private boolean isSecure() {
740        SecurityMode mode = mSecurityModel.getSecurityMode();
741        switch (mode) {
742            case Pattern:
743                return mLockPatternUtils.isLockPatternEnabled();
744            case Password:
745            case PIN:
746                return mLockPatternUtils.isLockPasswordEnabled();
747            case SimPin:
748            case SimPuk:
749            case Account:
750                return true;
751            case None:
752                return false;
753            default:
754                throw new IllegalStateException("Unknown security mode " + mode);
755        }
756    }
757
758    @Override
759    public void wakeWhenReadyTq(int keyCode) {
760        if (DEBUG) Log.d(TAG, "onWakeKey");
761        if (keyCode == KeyEvent.KEYCODE_MENU && isSecure()) {
762            if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU");
763            showSecurityScreen(SecurityMode.None);
764        } else {
765            if (DEBUG) Log.d(TAG, "poking wake lock immediately");
766        }
767        if (mViewMediatorCallback != null) {
768            mViewMediatorCallback.wakeUp();
769        }
770    }
771
772    @Override
773    public void verifyUnlock() {
774        SecurityMode securityMode = mSecurityModel.getSecurityMode();
775        if (securityMode == KeyguardSecurityModel.SecurityMode.None) {
776            if (mViewMediatorCallback != null) {
777                mViewMediatorCallback.keyguardDone(true);
778            }
779        } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
780                && securityMode != KeyguardSecurityModel.SecurityMode.PIN
781                && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
782            // can only verify unlock when in pattern/password mode
783            if (mViewMediatorCallback != null) {
784                mViewMediatorCallback.keyguardDone(false);
785            }
786        } else {
787            // otherwise, go to the unlock screen, see if they can verify it
788            mIsVerifyUnlockOnly = true;
789            showSecurityScreen(securityMode);
790        }
791    }
792
793    private int getSecurityViewIdForMode(SecurityMode securityMode) {
794        switch (securityMode) {
795            case None: return R.id.keyguard_selector_view;
796            case Pattern: return R.id.keyguard_pattern_view;
797            case PIN: return R.id.keyguard_pin_view;
798            case Password: return R.id.keyguard_password_view;
799            case Biometric: return R.id.keyguard_face_unlock_view;
800            case Account: return R.id.keyguard_account_view;
801            case SimPin: return R.id.keyguard_sim_pin_view;
802            case SimPuk: return R.id.keyguard_sim_puk_view;
803        }
804        return 0;
805    }
806
807    private int getLayoutIdFor(SecurityMode securityMode) {
808        switch (securityMode) {
809            case None: return R.layout.keyguard_selector_view;
810            case Pattern: return R.layout.keyguard_pattern_view;
811            case PIN: return R.layout.keyguard_pin_view;
812            case Password: return R.layout.keyguard_password_view;
813            case Biometric: return R.layout.keyguard_face_unlock_view;
814            case Account: return R.layout.keyguard_account_view;
815            case SimPin: return R.layout.keyguard_sim_pin_view;
816            case SimPuk: return R.layout.keyguard_sim_puk_view;
817            default:
818                return 0;
819        }
820    }
821
822    private void addWidget(int appId, int pageIndex) {
823        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
824        AppWidgetProviderInfo appWidgetInfo = appWidgetManager.getAppWidgetInfo(appId);
825        if (appWidgetInfo != null) {
826            AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo);
827            addWidget(view, pageIndex);
828        } else {
829            Log.w(TAG, "AppWidgetInfo was null; not adding widget id " + appId);
830        }
831    }
832
833    private final CameraWidgetFrame.Callbacks mCameraWidgetCallbacks =
834        new CameraWidgetFrame.Callbacks() {
835            @Override
836            public void onLaunchingCamera() {
837                SlidingChallengeLayout slider = locateSlider();
838                if (slider != null) {
839                    slider.showHandle(false);
840                }
841            }
842
843            @Override
844            public void onCameraLaunched() {
845                SlidingChallengeLayout slider = locateSlider();
846                if (slider != null) {
847                    slider.showHandle(true);
848                }
849                mAppWidgetContainer.scrollLeft();
850            }
851
852            private SlidingChallengeLayout locateSlider() {
853                return (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
854            }
855        };
856
857    private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() {
858        @Override
859        Context getContext() {
860            return mContext;
861        }
862
863        @Override
864        KeyguardSecurityCallback getCallback() {
865            return mCallback;
866        }
867
868        @Override
869        LockPatternUtils getLockPatternUtils() {
870            return mLockPatternUtils;
871        }};
872
873    private void addDefaultWidgets() {
874        LayoutInflater inflater = LayoutInflater.from(mContext);
875        inflater.inflate(R.layout.keyguard_transport_control_view, this, true);
876
877        View addWidget = inflater.inflate(R.layout.keyguard_add_widget, null, true);
878        mAppWidgetContainer.addWidget(addWidget);
879        View statusWidget = inflater.inflate(R.layout.keyguard_status_view, null, true);
880        mAppWidgetContainer.addWidget(statusWidget);
881        if (mContext.getResources().getBoolean(R.bool.kg_enable_camera_default_widget)) {
882            View cameraWidget =
883                    CameraWidgetFrame.create(mContext, mCameraWidgetCallbacks, mActivityLauncher);
884            if (cameraWidget != null) {
885                mAppWidgetContainer.addWidget(cameraWidget);
886            }
887        }
888
889        View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view);
890        addWidgetButton.setOnClickListener(new OnClickListener() {
891            @Override
892            public void onClick(View v) {
893                mCallback.setOnDismissRunnable(new Runnable() {
894
895                    @Override
896                    public void run() {
897                        Intent intent = new Intent(Settings.ACTION_SECURITY_SETTINGS);
898                        intent.addFlags(
899                                Intent.FLAG_ACTIVITY_NEW_TASK
900                                | Intent.FLAG_ACTIVITY_SINGLE_TOP
901                                | Intent.FLAG_ACTIVITY_CLEAR_TOP
902                                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
903                        mContext.startActivityAsUser(intent,
904                                new UserHandle(UserHandle.USER_CURRENT));
905                    }
906                });
907                mCallback.dismiss(false);
908            }
909        });
910
911        enableUserSelectorIfNecessary();
912        initializeTransportControl();
913    }
914
915    private void initializeTransportControl() {
916        mTransportControl =
917            (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control);
918        mTransportControl.setVisibility(View.GONE);
919
920        // This code manages showing/hiding the transport control. We keep it around and only
921        // add it to the hierarchy if it needs to be present.
922        if (mTransportControl != null) {
923            mTransportControl.setKeyguardCallback(new TransportCallback() {
924                @Override
925                public void onListenerDetached() {
926                    int page = getWidgetPosition(R.id.keyguard_transport_control);
927                    if (page != -1) {
928                        mAppWidgetContainer.removeView(mTransportControl);
929                        // XXX keep view attached so we still get show/hide events from AudioManager
930                        KeyguardHostView.this.addView(mTransportControl);
931                        mTransportControl.setVisibility(View.GONE);
932                        mTransportState = TRANSPORT_GONE;
933                        mTransportControl.post(mSwitchPageRunnable);
934                    }
935                }
936
937                @Override
938                public void onListenerAttached() {
939                    if (getWidgetPosition(R.id.keyguard_transport_control) == -1) {
940                        KeyguardHostView.this.removeView(mTransportControl);
941                        mAppWidgetContainer.addView(mTransportControl, 0);
942                        mTransportControl.setVisibility(View.VISIBLE);
943                    }
944                }
945
946                @Override
947                public void onPlayStateChanged() {
948                    mTransportControl.post(mSwitchPageRunnable);
949                }
950            });
951        }
952
953        mKeyguardStatusViewManager = ((KeyguardStatusView)
954                findViewById(R.id.keyguard_status_view_face_palm)).getManager();
955
956    }
957
958    private void maybePopulateWidgets() {
959        DevicePolicyManager dpm =
960                (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
961        if (dpm != null) {
962            final int currentUser = mLockPatternUtils.getCurrentUser();
963            final int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUser);
964            if ((disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0) {
965                Log.v(TAG, "Keyguard widgets disabled because of device policy admin");
966                return;
967            }
968        }
969
970        View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget);
971        int addPageIndex = mAppWidgetContainer.indexOfChild(addWidget);
972        // This shouldn't happen, but just to be safe!
973        if (addPageIndex < 0) {
974            addPageIndex = 0;
975        }
976
977        // Add user-selected widget
978        final int[] widgets = mLockPatternUtils.getAppWidgets();
979        if (widgets == null) {
980            Log.d(TAG, "Problem reading widgets");
981        } else {
982            for (int i = widgets.length -1; i >= 0; i--) {
983                if (widgets[i] != -1) {
984                    // We add the widgets from left to right, starting after the first page after
985                    // the add page. We count down, since the order will be persisted from right
986                    // to left, starting after camera.
987                    addWidget(widgets[i], addPageIndex + 1);
988                }
989            }
990        }
991    }
992
993    Runnable mSwitchPageRunnable = new Runnable() {
994        @Override
995        public void run() {
996           showAppropriateWidgetPage();
997        }
998    };
999
1000    static class SavedState extends BaseSavedState {
1001        int transportState;
1002
1003        SavedState(Parcelable superState) {
1004            super(superState);
1005        }
1006
1007        private SavedState(Parcel in) {
1008            super(in);
1009            this.transportState = in.readInt();
1010        }
1011
1012        @Override
1013        public void writeToParcel(Parcel out, int flags) {
1014            super.writeToParcel(out, flags);
1015            out.writeInt(this.transportState);
1016        }
1017
1018        public static final Parcelable.Creator<SavedState> CREATOR
1019                = new Parcelable.Creator<SavedState>() {
1020            public SavedState createFromParcel(Parcel in) {
1021                return new SavedState(in);
1022            }
1023
1024            public SavedState[] newArray(int size) {
1025                return new SavedState[size];
1026            }
1027        };
1028    }
1029
1030    @Override
1031    public Parcelable onSaveInstanceState() {
1032        Parcelable superState = super.onSaveInstanceState();
1033        SavedState ss = new SavedState(superState);
1034        ss.transportState = mTransportState;
1035        return ss;
1036    }
1037
1038    @Override
1039    public void onRestoreInstanceState(Parcelable state) {
1040        if (!(state instanceof SavedState)) {
1041            super.onRestoreInstanceState(state);
1042            return;
1043        }
1044        SavedState ss = (SavedState) state;
1045        super.onRestoreInstanceState(ss.getSuperState());
1046        mTransportState = ss.transportState;
1047        post(mSwitchPageRunnable);
1048    }
1049
1050    private void showAppropriateWidgetPage() {
1051
1052        // The following sets the priority for showing widgets. Transport should be shown if
1053        // music is playing, followed by the multi-user widget if enabled, followed by the
1054        // status widget.
1055        final int pageToShow;
1056        if (mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE) {
1057            mTransportState = TRANSPORT_VISIBLE;
1058            pageToShow = mAppWidgetContainer.indexOfChild(mTransportControl);
1059        } else {
1060            UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
1061            final View multiUserView = findViewById(R.id.keyguard_multi_user_selector);
1062            final int multiUserPosition = mAppWidgetContainer.indexOfChild(multiUserView);
1063            if (multiUserPosition != -1 && mUm.getUsers(true).size() > 1) {
1064                pageToShow = multiUserPosition;
1065            } else {
1066                final View statusView = findViewById(R.id.keyguard_status_view);
1067                pageToShow = mAppWidgetContainer.indexOfChild(statusView);
1068            }
1069            if (mTransportState == TRANSPORT_VISIBLE) {
1070                mTransportState = TRANSPORT_INVISIBLE;
1071            }
1072        }
1073        mAppWidgetContainer.setCurrentPage(pageToShow);
1074    }
1075
1076    private void enableUserSelectorIfNecessary() {
1077        // if there are multiple users, we need to add the multi-user switcher widget to the
1078        // keyguard.
1079        UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
1080        List<UserInfo> users = mUm.getUsers(true);
1081
1082        if (users.size() > 1) {
1083            KeyguardMultiUserSelectorView multiUser =
1084                    (KeyguardMultiUserSelectorView) findViewById(R.id.keyguard_user_selector);
1085            multiUser.setVisibility(View.VISIBLE);
1086            multiUser.addUsers(mUm.getUsers(true));
1087            UserSwitcherCallback callback = new UserSwitcherCallback() {
1088                @Override
1089                public void hideSecurityView(int duration) {
1090                    mSecurityViewContainer.animate().alpha(0).setDuration(duration);
1091                }
1092
1093                @Override
1094                public void showSecurityView() {
1095                    mSecurityViewContainer.setAlpha(1.0f);
1096                }
1097
1098                @Override
1099                public void showUnlockHint() {
1100                    if (mKeyguardSelectorView != null) {
1101                        mKeyguardSelectorView.showUsabilityHint();
1102                    }
1103                }
1104
1105                @Override
1106                public void userActivity() {
1107                    if (mViewMediatorCallback != null) {
1108                        mViewMediatorCallback.userActivity();
1109                    }
1110                }
1111            };
1112            multiUser.setCallback(callback);
1113        }
1114    }
1115
1116    @Override
1117    public void cleanUp() {
1118
1119    }
1120
1121    /**
1122     * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
1123     * some cases where we wish to disable it, notably when the menu button placement or technology
1124     * is prone to false positives.
1125     *
1126     * @return true if the menu key should be enabled
1127     */
1128    private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
1129    private boolean shouldEnableMenuKey() {
1130        final Resources res = getResources();
1131        final boolean configDisabled = res.getBoolean(
1132                com.android.internal.R.bool.config_disableMenuKeyInLockScreen);
1133        final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
1134        final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
1135        return !configDisabled || isTestHarness || fileOverride;
1136    }
1137
1138    @Override
1139    public boolean onKeyDown(int keyCode, KeyEvent event) {
1140        if (keyCode == KeyEvent.KEYCODE_MENU && mEnableMenuKey) {
1141            showNextSecurityScreenOrFinish(false);
1142            return true;
1143        } else {
1144            return super.onKeyDown(keyCode, event);
1145        }
1146    }
1147
1148    public void goToUserSwitcher() {
1149        mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_multi_user_selector));
1150    }
1151
1152    public boolean handleBackKey() {
1153        if (mCurrentSecuritySelection != SecurityMode.None) {
1154            mCallback.dismiss(false);
1155            return true;
1156        }
1157        return false;
1158    }
1159
1160}
1161