KeyguardHostView.java revision 5f050e5c2c1616c7f0795c4f13e42a5d150b8015
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 = KeyguardViewMediator.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        addWidgetsFromSettings();
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        mAppWidgetContainer.setLockPatternUtils(mLockPatternUtils);
174
175        mViewStateManager.setPagedView(mAppWidgetContainer);
176        mViewStateManager.setChallengeLayout(slider != null ? slider :
177                (ChallengeLayout) findViewById(R.id.multi_pane_challenge));
178        mSecurityViewContainer = (KeyguardSecurityViewFlipper) findViewById(R.id.view_flipper);
179        mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view);
180        mViewStateManager.setSecurityViewContainer(mSecurityViewContainer);
181
182        mViewStateManager.showUsabilityHints();
183
184        if (!(mContext instanceof Activity)) {
185            setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
186        }
187
188        showPrimarySecurityScreen(false);
189
190        updateSecurityViews();
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                && !KeyguardUpdateMonitor.getInstance(mContext).getIsFirstBoot()) {
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            KeyguardUpdateMonitor.getInstance(mContext).setIsFirstBoot(false);
506
507            // If there's a pending runnable because the user interacted with a widget
508            // and we're leaving keyguard, then run it.
509            if (mLaunchRunnable != null) {
510                mLaunchRunnable.run();
511                mLaunchRunnable = null;
512            }
513            if (mViewMediatorCallback != null) {
514                mViewMediatorCallback.keyguardDone(true);
515            }
516        } else {
517            mViewStateManager.showBouncer(true);
518        }
519    }
520
521    private OnClickHandler mOnClickHandler = new OnClickHandler() {
522        @Override
523        public boolean onClickHandler(final View view,
524                final android.app.PendingIntent pendingIntent,
525                final Intent fillInIntent) {
526            if (pendingIntent.isActivity()) {
527                setOnDismissRunnable(new Runnable() {
528                    public void run() {
529                        try {
530                              // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
531                              Context context = view.getContext();
532                              ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view,
533                                      0, 0,
534                                      view.getMeasuredWidth(), view.getMeasuredHeight());
535                              context.startIntentSender(
536                                      pendingIntent.getIntentSender(), fillInIntent,
537                                      Intent.FLAG_ACTIVITY_NEW_TASK,
538                                      Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
539                          } catch (IntentSender.SendIntentException e) {
540                              android.util.Log.e(TAG, "Cannot send pending intent: ", e);
541                          } catch (Exception e) {
542                              android.util.Log.e(TAG, "Cannot send pending intent due to " +
543                                      "unknown exception: ", e);
544                          }
545                    }
546                });
547
548                mCallback.dismiss(false);
549                return true;
550            } else {
551                return super.onClickHandler(view, pendingIntent, fillInIntent);
552            }
553        };
554    };
555
556    private KeyguardStatusViewManager mKeyguardStatusViewManager;
557
558    // Used to ignore callbacks from methods that are no longer current (e.g. face unlock).
559    // This avoids unwanted asynchronous events from messing with the state.
560    private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
561
562        @Override
563        public void userActivity(long timeout) {
564        }
565
566        @Override
567        public void showBackupSecurity() {
568        }
569
570        @Override
571        public void setOnDismissRunnable(Runnable runnable) {
572        }
573
574        @Override
575        public void reportSuccessfulUnlockAttempt() {
576        }
577
578        @Override
579        public void reportFailedUnlockAttempt() {
580        }
581
582        @Override
583        public boolean isVerifyUnlockOnly() {
584            return false;
585        }
586
587        @Override
588        public int getFailedAttempts() {
589            return 0;
590        }
591
592        @Override
593        public void dismiss(boolean securityVerified) {
594        }
595    };
596
597    @Override
598    public void reset() {
599        mIsVerifyUnlockOnly = false;
600        mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_status_view));
601    }
602
603    /**
604     *  Sets a runnable to run when keyguard is dismissed
605     * @param runnable
606     */
607    protected void setOnDismissRunnable(Runnable runnable) {
608        mLaunchRunnable = runnable;
609    }
610
611    private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
612        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
613        KeyguardSecurityView view = null;
614        final int children = mSecurityViewContainer.getChildCount();
615        for (int child = 0; child < children; child++) {
616            if (mSecurityViewContainer.getChildAt(child).getId() == securityViewIdForMode) {
617                view = ((KeyguardSecurityView)mSecurityViewContainer.getChildAt(child));
618                break;
619            }
620        }
621        int layoutId = getLayoutIdFor(securityMode);
622        if (view == null && layoutId != 0) {
623            final LayoutInflater inflater = LayoutInflater.from(mContext);
624            if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
625            View v = inflater.inflate(layoutId, this, false);
626            mSecurityViewContainer.addView(v);
627            updateSecurityView(v);
628
629            view = (KeyguardSecurityView) v;
630            TextView navigationText = ((TextView) findViewById(R.id.keyguard_message_area));
631
632            // Some devices can fit a navigation area, others cannot. On devices that cannot,
633            // we display the security message in status area.
634            if (navigationText != null) {
635                view.setSecurityMessageDisplay(new KeyguardNavigationManager(navigationText));
636            } else {
637                if (mKeyguardStatusViewManager != null) {
638                    view.setSecurityMessageDisplay(mKeyguardStatusViewManager);
639                }
640            }
641        }
642
643        if (view instanceof KeyguardSelectorView) {
644            KeyguardSelectorView selectorView = (KeyguardSelectorView) view;
645            View carrierText = selectorView.findViewById(R.id.keyguard_selector_fade_container);
646            selectorView.setCarrierArea(carrierText);
647        }
648
649        return view;
650    }
651
652    /**
653     * Switches to the given security view unless it's already being shown, in which case
654     * this is a no-op.
655     *
656     * @param securityMode
657     */
658    private void showSecurityScreen(SecurityMode securityMode) {
659        if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
660
661        if (securityMode == mCurrentSecuritySelection) return;
662
663        KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
664        KeyguardSecurityView newView = getSecurityView(securityMode);
665
666        // Enter full screen mode if we're in SIM or Account screen
667        boolean fullScreenEnabled = getResources().getBoolean(
668                com.android.internal.R.bool.kg_sim_puk_account_full_screen);
669        boolean isSimOrAccount = securityMode == SecurityMode.SimPin
670                || securityMode == SecurityMode.SimPuk
671                || securityMode == SecurityMode.Account;
672        mAppWidgetContainer.setVisibility(
673                isSimOrAccount && fullScreenEnabled ? View.GONE : View.VISIBLE);
674
675        // Emulate Activity life cycle
676        if (oldView != null) {
677            oldView.onPause();
678            oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
679        }
680        newView.onResume();
681        newView.setKeyguardCallback(mCallback);
682
683        final boolean needsInput = newView.needsInput();
684        if (mViewMediatorCallback != null) {
685            mViewMediatorCallback.setNeedsInput(needsInput);
686        }
687
688        // Find and show this child.
689        final int childCount = mSecurityViewContainer.getChildCount();
690
691        // Do flip animation to the next screen
692        if (false) {
693            mSecurityViewContainer.setInAnimation(
694                    AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_animate_in));
695            mSecurityViewContainer.setOutAnimation(
696                    AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_animate_out));
697        }
698        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
699        for (int i = 0; i < childCount; i++) {
700            if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) {
701                mSecurityViewContainer.setDisplayedChild(i);
702                break;
703            }
704        }
705
706        if (securityMode == SecurityMode.None) {
707            // Discard current runnable if we're switching back to the selector view
708            setOnDismissRunnable(null);
709        }
710        mCurrentSecuritySelection = securityMode;
711    }
712
713    @Override
714    public void onScreenTurnedOn() {
715        if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
716        showPrimarySecurityScreen(false);
717        getSecurityView(mCurrentSecuritySelection).onResume();
718
719        // This is a an attempt to fix bug 7137389 where the device comes back on but the entire
720        // layout is blank but forcing a layout causes it to reappear (e.g. with with
721        // hierarchyviewer).
722        requestLayout();
723
724        if (mViewStateManager != null) {
725            mViewStateManager.showUsabilityHints();
726        }
727    }
728
729    @Override
730    public void onScreenTurnedOff() {
731        if (DEBUG) Log.d(TAG, "screen off, instance " + Integer.toHexString(hashCode()));
732        saveStickyWidgetIndex();
733        showPrimarySecurityScreen(true);
734        getSecurityView(mCurrentSecuritySelection).onPause();
735    }
736
737    @Override
738    public void show() {
739        if (DEBUG) Log.d(TAG, "show()");
740        showPrimarySecurityScreen(false);
741    }
742
743    private boolean isSecure() {
744        SecurityMode mode = mSecurityModel.getSecurityMode();
745        switch (mode) {
746            case Pattern:
747                return mLockPatternUtils.isLockPatternEnabled();
748            case Password:
749            case PIN:
750                return mLockPatternUtils.isLockPasswordEnabled();
751            case SimPin:
752            case SimPuk:
753            case Account:
754                return true;
755            case None:
756                return false;
757            default:
758                throw new IllegalStateException("Unknown security mode " + mode);
759        }
760    }
761
762    @Override
763    public void wakeWhenReadyTq(int keyCode) {
764        if (DEBUG) Log.d(TAG, "onWakeKey");
765        if (keyCode == KeyEvent.KEYCODE_MENU && isSecure()) {
766            if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU");
767            showSecurityScreen(SecurityMode.None);
768        } else {
769            if (DEBUG) Log.d(TAG, "poking wake lock immediately");
770        }
771        if (mViewMediatorCallback != null) {
772            mViewMediatorCallback.wakeUp();
773        }
774    }
775
776    @Override
777    public void verifyUnlock() {
778        SecurityMode securityMode = mSecurityModel.getSecurityMode();
779        if (securityMode == KeyguardSecurityModel.SecurityMode.None) {
780            if (mViewMediatorCallback != null) {
781                mViewMediatorCallback.keyguardDone(true);
782            }
783        } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
784                && securityMode != KeyguardSecurityModel.SecurityMode.PIN
785                && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
786            // can only verify unlock when in pattern/password mode
787            if (mViewMediatorCallback != null) {
788                mViewMediatorCallback.keyguardDone(false);
789            }
790        } else {
791            // otherwise, go to the unlock screen, see if they can verify it
792            mIsVerifyUnlockOnly = true;
793            showSecurityScreen(securityMode);
794        }
795    }
796
797    private int getSecurityViewIdForMode(SecurityMode securityMode) {
798        switch (securityMode) {
799            case None: return R.id.keyguard_selector_view;
800            case Pattern: return R.id.keyguard_pattern_view;
801            case PIN: return R.id.keyguard_pin_view;
802            case Password: return R.id.keyguard_password_view;
803            case Biometric: return R.id.keyguard_face_unlock_view;
804            case Account: return R.id.keyguard_account_view;
805            case SimPin: return R.id.keyguard_sim_pin_view;
806            case SimPuk: return R.id.keyguard_sim_puk_view;
807        }
808        return 0;
809    }
810
811    private int getLayoutIdFor(SecurityMode securityMode) {
812        switch (securityMode) {
813            case None: return R.layout.keyguard_selector_view;
814            case Pattern: return R.layout.keyguard_pattern_view;
815            case PIN: return R.layout.keyguard_pin_view;
816            case Password: return R.layout.keyguard_password_view;
817            case Biometric: return R.layout.keyguard_face_unlock_view;
818            case Account: return R.layout.keyguard_account_view;
819            case SimPin: return R.layout.keyguard_sim_pin_view;
820            case SimPuk: return R.layout.keyguard_sim_puk_view;
821            default:
822                return 0;
823        }
824    }
825
826    private void addWidget(int appId, int pageIndex) {
827        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
828        AppWidgetProviderInfo appWidgetInfo = appWidgetManager.getAppWidgetInfo(appId);
829        if (appWidgetInfo != null) {
830            AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo);
831            addWidget(view, pageIndex);
832        } else {
833            Log.w(TAG, "AppWidgetInfo for app widget id " + appId + " was null, deleting");
834            mLockPatternUtils.removeAppWidget(appId);
835        }
836    }
837
838    private final CameraWidgetFrame.Callbacks mCameraWidgetCallbacks =
839        new CameraWidgetFrame.Callbacks() {
840            @Override
841            public void onLaunchingCamera() {
842                SlidingChallengeLayout slider = locateSlider();
843                if (slider != null) {
844                    slider.showHandle(false);
845                }
846            }
847
848            @Override
849            public void onCameraLaunched() {
850                SlidingChallengeLayout slider = locateSlider();
851                if (slider != null) {
852                    slider.showHandle(true);
853                    slider.showChallenge(true);
854                }
855                if (isCameraPage(mAppWidgetContainer.getCurrentPage())) {
856                    mAppWidgetContainer.scrollLeft();
857                }
858            }
859
860            private SlidingChallengeLayout locateSlider() {
861                return (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
862            }
863        };
864
865    private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() {
866        @Override
867        Context getContext() {
868            return mContext;
869        }
870
871        @Override
872        KeyguardSecurityCallback getCallback() {
873            return mCallback;
874        }
875
876        @Override
877        LockPatternUtils getLockPatternUtils() {
878            return mLockPatternUtils;
879        }};
880
881    private void addDefaultWidgets() {
882        LayoutInflater inflater = LayoutInflater.from(mContext);
883        inflater.inflate(R.layout.keyguard_transport_control_view, this, true);
884
885        View addWidget = inflater.inflate(R.layout.keyguard_add_widget, null, true);
886        mAppWidgetContainer.addWidget(addWidget);
887        if (mContext.getResources().getBoolean(R.bool.kg_enable_camera_default_widget)) {
888            View cameraWidget =
889                    CameraWidgetFrame.create(mContext, mCameraWidgetCallbacks, mActivityLauncher);
890            if (cameraWidget != null) {
891                mAppWidgetContainer.addWidget(cameraWidget);
892            }
893        }
894
895        View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view);
896        addWidgetButton.setOnClickListener(new OnClickListener() {
897            @Override
898            public void onClick(View v) {
899                mCallback.setOnDismissRunnable(new Runnable() {
900
901                    @Override
902                    public void run() {
903                        Intent intent = new Intent(Settings.ACTION_SECURITY_SETTINGS);
904                        intent.addFlags(
905                                Intent.FLAG_ACTIVITY_NEW_TASK
906                                | Intent.FLAG_ACTIVITY_SINGLE_TOP
907                                | Intent.FLAG_ACTIVITY_CLEAR_TOP
908                                | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
909                        mContext.startActivityAsUser(intent,
910                                new UserHandle(UserHandle.USER_CURRENT));
911                    }
912                });
913                mCallback.dismiss(false);
914            }
915        });
916
917        enableUserSelectorIfNecessary();
918        initializeTransportControl();
919    }
920
921    private void initializeTransportControl() {
922        mTransportControl =
923            (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control);
924        mTransportControl.setVisibility(View.GONE);
925
926        // This code manages showing/hiding the transport control. We keep it around and only
927        // add it to the hierarchy if it needs to be present.
928        if (mTransportControl != null) {
929            mTransportControl.setKeyguardCallback(new TransportCallback() {
930                @Override
931                public void onListenerDetached() {
932                    int page = getWidgetPosition(R.id.keyguard_transport_control);
933                    if (page != -1) {
934                        mAppWidgetContainer.removeView(mTransportControl);
935                        // XXX keep view attached so we still get show/hide events from AudioManager
936                        KeyguardHostView.this.addView(mTransportControl);
937                        mTransportControl.setVisibility(View.GONE);
938                        mTransportState = TRANSPORT_GONE;
939                        mTransportControl.post(mSwitchPageRunnable);
940                    }
941                }
942
943                @Override
944                public void onListenerAttached() {
945                    if (getWidgetPosition(R.id.keyguard_transport_control) == -1) {
946                        KeyguardHostView.this.removeView(mTransportControl);
947                        mAppWidgetContainer.addView(mTransportControl, 0);
948                        mTransportControl.setVisibility(View.VISIBLE);
949                    }
950                }
951
952                @Override
953                public void onPlayStateChanged() {
954                    mTransportControl.post(mSwitchPageRunnable);
955                }
956            });
957        }
958
959        KeyguardStatusView ksv = (KeyguardStatusView)
960                findViewById(R.id.keyguard_status_view_face_palm);
961        if (ksv != null) {
962            mKeyguardStatusViewManager = ksv.getManager();
963        }
964
965    }
966
967    private void addWidgetsFromSettings() {
968        DevicePolicyManager dpm =
969                (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
970        if (dpm != null) {
971            final int currentUser = mLockPatternUtils.getCurrentUser();
972            final int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUser);
973            if ((disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0) {
974                Log.v(TAG, "Keyguard widgets disabled because of device policy admin");
975                return;
976            }
977        }
978
979        View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget);
980        int addPageIndex = mAppWidgetContainer.indexOfChild(addWidget);
981        // This shouldn't happen, but just to be safe!
982        if (addPageIndex < 0) {
983            addPageIndex = 0;
984        }
985
986        // Add user-selected widget
987        final int[] widgets = mLockPatternUtils.getAppWidgets();
988        if (widgets == null) {
989            Log.d(TAG, "Problem reading widgets");
990        } else {
991            for (int i = widgets.length -1; i >= 0; i--) {
992                if (widgets[i] == LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) {
993                    LayoutInflater inflater = LayoutInflater.from(mContext);
994                    View statusWidget = inflater.inflate(R.layout.keyguard_status_view, null, true);
995                    mAppWidgetContainer.addWidget(statusWidget, addPageIndex + 1);
996                } else {
997                    // We add the widgets from left to right, starting after the first page after
998                    // the add page. We count down, since the order will be persisted from right
999                    // to left, starting after camera.
1000                    addWidget(widgets[i], addPageIndex + 1);
1001                }
1002            }
1003        }
1004    }
1005
1006    Runnable mSwitchPageRunnable = new Runnable() {
1007        @Override
1008        public void run() {
1009           showAppropriateWidgetPage();
1010        }
1011    };
1012
1013    static class SavedState extends BaseSavedState {
1014        int transportState;
1015
1016        SavedState(Parcelable superState) {
1017            super(superState);
1018        }
1019
1020        private SavedState(Parcel in) {
1021            super(in);
1022            this.transportState = in.readInt();
1023        }
1024
1025        @Override
1026        public void writeToParcel(Parcel out, int flags) {
1027            super.writeToParcel(out, flags);
1028            out.writeInt(this.transportState);
1029        }
1030
1031        public static final Parcelable.Creator<SavedState> CREATOR
1032                = new Parcelable.Creator<SavedState>() {
1033            public SavedState createFromParcel(Parcel in) {
1034                return new SavedState(in);
1035            }
1036
1037            public SavedState[] newArray(int size) {
1038                return new SavedState[size];
1039            }
1040        };
1041    }
1042
1043    @Override
1044    public Parcelable onSaveInstanceState() {
1045        if (DEBUG) Log.d(TAG, "onSaveInstanceState");
1046        saveStickyWidgetIndex();
1047        Parcelable superState = super.onSaveInstanceState();
1048        SavedState ss = new SavedState(superState);
1049        ss.transportState = mTransportState;
1050        return ss;
1051    }
1052
1053    @Override
1054    public void onRestoreInstanceState(Parcelable state) {
1055        if (DEBUG) Log.d(TAG, "onRestoreInstanceState");
1056        if (!(state instanceof SavedState)) {
1057            super.onRestoreInstanceState(state);
1058            return;
1059        }
1060        SavedState ss = (SavedState) state;
1061        super.onRestoreInstanceState(ss.getSuperState());
1062        mTransportState = ss.transportState;
1063        post(mSwitchPageRunnable);
1064    }
1065
1066    @Override
1067    public void onWindowFocusChanged(boolean hasWindowFocus) {
1068        super.onWindowFocusChanged(hasWindowFocus);
1069        if (DEBUG) Log.d(TAG, "Window is " + (hasWindowFocus ? "focused" : "unfocused"));
1070        if (!hasWindowFocus) {
1071            saveStickyWidgetIndex();
1072        }
1073    }
1074
1075    private void showAppropriateWidgetPage() {
1076        boolean isMusicPlaying =
1077                mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE;
1078        if (isMusicPlaying) {
1079            mTransportState = TRANSPORT_VISIBLE;
1080        } else if (mTransportState == TRANSPORT_VISIBLE) {
1081            mTransportState = TRANSPORT_INVISIBLE;
1082        }
1083        int pageToShow = getAppropriateWidgetPage(isMusicPlaying);
1084        mAppWidgetContainer.setCurrentPage(pageToShow);
1085    }
1086
1087    private boolean isCameraPage(int pageIndex) {
1088        View v = mAppWidgetContainer.getChildAt(pageIndex);
1089        return v != null && v instanceof CameraWidgetFrame;
1090    }
1091
1092    private boolean isAddPage(int pageIndex) {
1093        View v = mAppWidgetContainer.getChildAt(pageIndex);
1094        return v != null && v.getId() == R.id.keyguard_add_widget;
1095    }
1096
1097    private int getAppropriateWidgetPage(boolean isMusicPlaying) {
1098        // assumes at least one widget (besides camera + add)
1099
1100        // if music playing, show transport
1101        if (isMusicPlaying) {
1102            if (DEBUG) Log.d(TAG, "Music playing, show transport");
1103            return mAppWidgetContainer.indexOfChild(mTransportControl);
1104        }
1105
1106        // if we have a valid sticky widget, show it
1107        int stickyWidgetIndex = mLockPatternUtils.getStickyAppWidgetIndex();
1108        if (stickyWidgetIndex > -1
1109                && stickyWidgetIndex < mAppWidgetContainer.getChildCount()
1110                && !isAddPage(stickyWidgetIndex)
1111                && !isCameraPage(stickyWidgetIndex)) {
1112            if (DEBUG) Log.d(TAG, "Valid sticky widget found, show page " + stickyWidgetIndex);
1113            return stickyWidgetIndex;
1114        }
1115
1116        // else show the right-most widget (except for camera)
1117        int rightMost = mAppWidgetContainer.getChildCount() - 1;
1118        if (isCameraPage(rightMost)) {
1119            rightMost--;
1120        }
1121        if (DEBUG) Log.d(TAG, "Show right-most page " + rightMost);
1122        return rightMost;
1123    }
1124
1125    private void saveStickyWidgetIndex() {
1126        int stickyWidgetIndex = mAppWidgetContainer.getCurrentPage();
1127        if (isAddPage(stickyWidgetIndex)) {
1128            stickyWidgetIndex++;
1129        }
1130        if (isCameraPage(stickyWidgetIndex)) {
1131            stickyWidgetIndex--;
1132        }
1133        if (stickyWidgetIndex < 0 || stickyWidgetIndex >= mAppWidgetContainer.getChildCount()) {
1134            stickyWidgetIndex = -1;
1135        }
1136        if (DEBUG) Log.d(TAG, "saveStickyWidgetIndex: " + stickyWidgetIndex);
1137        mLockPatternUtils.setStickyAppWidgetIndex(stickyWidgetIndex);
1138    }
1139
1140    private void enableUserSelectorIfNecessary() {
1141        // if there are multiple users, we need to enable to multi-user switcher
1142        UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
1143        List<UserInfo> users = mUm.getUsers(true);
1144
1145        if (users.size() > 1) {
1146            KeyguardMultiUserSelectorView multiUser =
1147                    (KeyguardMultiUserSelectorView) findViewById(R.id.keyguard_user_selector);
1148            multiUser.setVisibility(View.VISIBLE);
1149            multiUser.addUsers(mUm.getUsers(true));
1150            UserSwitcherCallback callback = new UserSwitcherCallback() {
1151                @Override
1152                public void hideSecurityView(int duration) {
1153                    mSecurityViewContainer.animate().alpha(0).setDuration(duration);
1154                }
1155
1156                @Override
1157                public void showSecurityView() {
1158                    mSecurityViewContainer.setAlpha(1.0f);
1159                }
1160
1161                @Override
1162                public void showUnlockHint() {
1163                    if (mKeyguardSelectorView != null) {
1164                        mKeyguardSelectorView.showUsabilityHint();
1165                    }
1166                }
1167
1168                @Override
1169                public void userActivity() {
1170                    if (mViewMediatorCallback != null) {
1171                        mViewMediatorCallback.userActivity();
1172                    }
1173                }
1174            };
1175            multiUser.setCallback(callback);
1176        }
1177    }
1178
1179    @Override
1180    public void cleanUp() {
1181
1182    }
1183
1184    /**
1185     * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
1186     * some cases where we wish to disable it, notably when the menu button placement or technology
1187     * is prone to false positives.
1188     *
1189     * @return true if the menu key should be enabled
1190     */
1191    private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
1192    private boolean shouldEnableMenuKey() {
1193        final Resources res = getResources();
1194        final boolean configDisabled = res.getBoolean(
1195                com.android.internal.R.bool.config_disableMenuKeyInLockScreen);
1196        final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
1197        final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
1198        return !configDisabled || isTestHarness || fileOverride;
1199    }
1200
1201    @Override
1202    public boolean onKeyDown(int keyCode, KeyEvent event) {
1203        if (keyCode == KeyEvent.KEYCODE_MENU && mEnableMenuKey) {
1204            showNextSecurityScreenOrFinish(false);
1205            return true;
1206        } else {
1207            return super.onKeyDown(keyCode, event);
1208        }
1209    }
1210
1211    public void goToUserSwitcher() {
1212        mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_multi_user_selector));
1213    }
1214
1215    public boolean handleBackKey() {
1216        if (mCurrentSecuritySelection != SecurityMode.None) {
1217            mCallback.dismiss(false);
1218            return true;
1219        }
1220        return false;
1221    }
1222
1223}
1224