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