KeyguardHostView.java revision d6523da1ad7bae805a3dbea8d7b398d86cd3c181
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.UserManager;
39import android.util.AttributeSet;
40import android.util.Log;
41import android.util.Slog;
42import android.view.KeyEvent;
43import android.view.LayoutInflater;
44import android.view.MotionEvent;
45import android.view.View;
46import android.view.WindowManager;
47import android.view.animation.AnimationUtils;
48import android.widget.RemoteViews.OnClickHandler;
49import android.widget.TextView;
50import android.widget.ViewFlipper;
51
52import com.android.internal.R;
53import com.android.internal.policy.impl.keyguard.KeyguardSecurityModel.SecurityMode;
54import com.android.internal.widget.LockPatternUtils;
55
56import java.io.File;
57import java.util.List;
58
59public class KeyguardHostView extends KeyguardViewBase {
60    private static final String TAG = "KeyguardViewHost";
61
62    // Use this to debug all of keyguard
63    public static boolean DEBUG;
64
65    // also referenced in SecuritySettings.java
66    static final int APPWIDGET_HOST_ID = 0x4B455947;
67
68    // transport control states
69    private static final int TRANSPORT_GONE = 0;
70    private static final int TRANSPORT_INVISIBLE = 1;
71    private static final int TRANSPORT_VISIBLE = 2;
72
73    private AppWidgetHost mAppWidgetHost;
74    private KeyguardWidgetPager mAppWidgetContainer;
75    private ViewFlipper mSecurityViewContainer;
76    private KeyguardSelectorView mKeyguardSelectorView;
77    private KeyguardTransportControlView mTransportControl;
78    private boolean mEnableMenuKey;
79    private boolean mIsVerifyUnlockOnly;
80    private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView
81    private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
82
83    protected Runnable mLaunchRunnable;
84
85    protected int mFailedAttempts;
86    private LockPatternUtils mLockPatternUtils;
87
88    private KeyguardSecurityModel mSecurityModel;
89    private KeyguardViewStateManager mViewStateManager;
90
91    private Rect mTempRect = new Rect();
92    private int mTransportState = TRANSPORT_GONE;
93
94    /*package*/ interface TransportCallback {
95        void onListenerDetached();
96        void onListenerAttached();
97        void onPlayStateChanged();
98    }
99
100    /*package*/ interface UserSwitcherCallback {
101        void hideSecurityView(int duration);
102        void showSecurityView();
103        void showUnlockHint();
104    }
105
106    public KeyguardHostView(Context context) {
107        this(context, null);
108    }
109
110    public KeyguardHostView(Context context, AttributeSet attrs) {
111        super(context, attrs);
112        mLockPatternUtils = new LockPatternUtils(context);
113        mAppWidgetHost = new AppWidgetHost(
114                context, APPWIDGET_HOST_ID, mOnClickHandler, Looper.myLooper());
115        mSecurityModel = new KeyguardSecurityModel(context);
116
117        // The following enables the MENU key to work for testing automation
118        mEnableMenuKey = shouldEnableMenuKey();
119        setFocusable(true);
120        setFocusableInTouchMode(true);
121    }
122
123    @Override
124    public boolean onTouchEvent(MotionEvent ev) {
125        boolean result = super.onTouchEvent(ev);
126        mTempRect.set(0, 0, 0, 0);
127        offsetRectIntoDescendantCoords(mSecurityViewContainer, mTempRect);
128        ev.offsetLocation(mTempRect.left, mTempRect.top);
129        result = mSecurityViewContainer.dispatchTouchEvent(ev) || result;
130        ev.offsetLocation(-mTempRect.left, -mTempRect.top);
131        return result;
132    }
133
134    @Override
135    protected void dispatchDraw(Canvas canvas) {
136        super.dispatchDraw(canvas);
137        if (mViewMediatorCallback != null) {
138            mViewMediatorCallback.keyguardDoneDrawing();
139        }
140    }
141
142    private int getWidgetPosition(int id) {
143        final int children = mAppWidgetContainer.getChildCount();
144        for (int i = 0; i < children; i++) {
145            if (mAppWidgetContainer.getChildAt(i).getId() == id) {
146                return i;
147            }
148        }
149        return -1;
150    }
151
152    @Override
153    protected void onFinishInflate() {
154        // Grab instances of and make any necessary changes to the main layouts. Create
155        // view state manager and wire up necessary listeners / callbacks.
156        mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
157        mAppWidgetContainer.setVisibility(VISIBLE);
158        mAppWidgetContainer.setCallbacks(mWidgetCallbacks);
159        mAppWidgetContainer.setMinScale(0.5f);
160
161        addDefaultWidgets();
162        maybePopulateWidgets();
163
164        mViewStateManager = new KeyguardViewStateManager();
165        SlidingChallengeLayout challengeLayout =
166                (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
167        challengeLayout.setOnChallengeScrolledListener(mViewStateManager);
168        mAppWidgetContainer.setViewStateManager(mViewStateManager);
169
170        mViewStateManager.setPagedView(mAppWidgetContainer);
171        mViewStateManager.setSlidingChallenge(challengeLayout);
172
173        mSecurityViewContainer = (ViewFlipper) findViewById(R.id.view_flipper);
174        mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view);
175
176
177        updateSecurityViews();
178        if (!(mContext instanceof Activity)) {
179            setSystemUiVisibility(getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
180        }
181
182        if (KeyguardUpdateMonitor.getInstance(mContext).getIsFirstBoot()) {
183            showPrimarySecurityScreen(false);
184        }
185    }
186
187    private void updateSecurityViews() {
188        int children = mSecurityViewContainer.getChildCount();
189        for (int i = 0; i < children; i++) {
190            updateSecurityView(mSecurityViewContainer.getChildAt(i));
191        }
192    }
193
194    private void updateSecurityView(View view) {
195        if (view instanceof KeyguardSecurityView) {
196            KeyguardSecurityView ksv = (KeyguardSecurityView) view;
197            ksv.setKeyguardCallback(mCallback);
198            ksv.setLockPatternUtils(mLockPatternUtils);
199        } else {
200            Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
201        }
202    }
203
204    void setLockPatternUtils(LockPatternUtils utils) {
205        mSecurityModel.setLockPatternUtils(utils);
206        mLockPatternUtils = utils;
207        updateSecurityViews();
208    }
209
210    @Override
211    protected void onAttachedToWindow() {
212        super.onAttachedToWindow();
213        mAppWidgetHost.startListening();
214        disableStatusViewInteraction();
215        post(mSwitchPageRunnable);
216    }
217
218    private void disableStatusViewInteraction() {
219        // Disable all user interaction on status view. This is done to prevent falsing in the
220        // pocket from triggering useractivity and prevents 3rd party replacement widgets
221        // from responding to user interaction while in this position.
222        View statusView = findViewById(R.id.keyguard_status_view);
223        if (statusView instanceof KeyguardWidgetFrame) {
224            ((KeyguardWidgetFrame) statusView).setDisableUserInteraction(true);
225        }
226    }
227
228    @Override
229    protected void onDetachedFromWindow() {
230        super.onDetachedFromWindow();
231        mAppWidgetHost.stopListening();
232    }
233
234    private AppWidgetHost getAppWidgetHost() {
235        return mAppWidgetHost;
236    }
237
238    void addWidget(AppWidgetHostView view, int pageIndex) {
239        mAppWidgetContainer.addWidget(view, pageIndex);
240    }
241
242    private KeyguardWidgetPager.Callbacks mWidgetCallbacks
243            = new KeyguardWidgetPager.Callbacks() {
244        @Override
245        public void userActivity() {
246            if (mViewMediatorCallback != null) {
247                mViewMediatorCallback.userActivity();
248            }
249        }
250
251        @Override
252        public void onUserActivityTimeoutChanged() {
253            if (mViewMediatorCallback != null) {
254                mViewMediatorCallback.onUserActivityTimeoutChanged();
255            }
256        }
257    };
258
259    @Override
260    public long getUserActivityTimeout() {
261        // Currently only considering user activity timeouts needed by widgets.
262        // Could also take into account longer timeouts for certain security views.
263        if (mAppWidgetContainer != null) {
264            return mAppWidgetContainer.getUserActivityTimeout();
265        }
266        return -1;
267    }
268
269    private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
270
271        public void userActivity(long timeout) {
272            if (mViewMediatorCallback != null) {
273                mViewMediatorCallback.userActivity(timeout);
274            }
275        }
276
277        public void dismiss(boolean authenticated) {
278            showNextSecurityScreenOrFinish(authenticated);
279        }
280
281        public boolean isVerifyUnlockOnly() {
282            return mIsVerifyUnlockOnly;
283        }
284
285        public void reportSuccessfulUnlockAttempt() {
286            KeyguardUpdateMonitor.getInstance(mContext).clearFailedUnlockAttempts();
287            mLockPatternUtils.reportSuccessfulPasswordAttempt();
288        }
289
290        public void reportFailedUnlockAttempt() {
291            if (mCurrentSecuritySelection == SecurityMode.Biometric) {
292                KeyguardUpdateMonitor.getInstance(mContext).reportFailedBiometricUnlockAttempt();
293            } else {
294                KeyguardHostView.this.reportFailedUnlockAttempt();
295            }
296        }
297
298        public int getFailedAttempts() {
299            return KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts();
300        }
301
302        @Override
303        public void showBackupSecurity() {
304            KeyguardHostView.this.showBackupSecurityScreen();
305        }
306
307        @Override
308        public void setOnDismissRunnable(Runnable runnable) {
309            KeyguardHostView.this.setOnDismissRunnable(runnable);
310        }
311
312    };
313
314    private void showDialog(String title, String message) {
315        final AlertDialog dialog = new AlertDialog.Builder(mContext)
316            .setTitle(title)
317            .setMessage(message)
318            .setNeutralButton(com.android.internal.R.string.ok, null)
319            .create();
320        if (!(mContext instanceof Activity)) {
321            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
322        }
323        dialog.show();
324    }
325
326    private void showTimeoutDialog() {
327        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
328        int messageId = 0;
329
330        switch (mSecurityModel.getSecurityMode()) {
331            case Pattern:
332                messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
333                break;
334
335            case Password: {
336                    final boolean isPin = mLockPatternUtils.getKeyguardStoredPasswordQuality() ==
337                        DevicePolicyManager.PASSWORD_QUALITY_NUMERIC;
338                    messageId = isPin ? R.string.kg_too_many_failed_pin_attempts_dialog_message
339                            : R.string.kg_too_many_failed_password_attempts_dialog_message;
340                }
341                break;
342        }
343
344        if (messageId != 0) {
345            final String message = mContext.getString(messageId,
346                    KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(),
347                    timeoutInSeconds);
348            showDialog(null, message);
349        }
350    }
351
352    private void showAlmostAtWipeDialog(int attempts, int remaining) {
353        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
354        String message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe,
355                attempts, remaining);
356        showDialog(null, message);
357    }
358
359    private void showWipeDialog(int attempts) {
360        String message = mContext.getString(R.string.kg_failed_attempts_now_wiping, attempts);
361        showDialog(null, message);
362    }
363
364    private void showAlmostAtAccountLoginDialog() {
365        final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
366        final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
367                - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
368        String message = mContext.getString(R.string.kg_failed_attempts_almost_at_login,
369                count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds);
370        showDialog(null, message);
371    }
372
373    private void reportFailedUnlockAttempt() {
374        final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
375        final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time
376
377        if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
378
379        SecurityMode mode = mSecurityModel.getSecurityMode();
380        final boolean usingPattern = mode == KeyguardSecurityModel.SecurityMode.Pattern;
381
382        final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager()
383                .getMaximumFailedPasswordsForWipe(null, mLockPatternUtils.getCurrentUser());
384
385        final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
386                - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
387
388        final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
389                (failedAttemptsBeforeWipe - failedAttempts)
390                : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
391
392        boolean showTimeout = false;
393        if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
394            // If we reach this code, it means the user has installed a DevicePolicyManager
395            // that requests device wipe after N attempts.  Once we get below the grace
396            // period, we'll post this dialog every time as a clear warning until the
397            // bombshell hits and the device is wiped.
398            if (remainingBeforeWipe > 0) {
399                showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe);
400            } else {
401                // Too many attempts. The device will be wiped shortly.
402                Slog.i(TAG, "Too many unlock attempts; device will be wiped!");
403                showWipeDialog(failedAttempts);
404            }
405        } else {
406            showTimeout =
407                (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
408            if (usingPattern && mEnableFallback) {
409                if (failedAttempts == failedAttemptWarning) {
410                    showAlmostAtAccountLoginDialog();
411                    showTimeout = false; // don't show both dialogs
412                } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
413                    mLockPatternUtils.setPermanentlyLocked(true);
414                    showSecurityScreen(SecurityMode.Account);
415                    // don't show timeout dialog because we show account unlock screen next
416                    showTimeout = false;
417                }
418            }
419        }
420        monitor.reportFailedUnlockAttempt();
421        mLockPatternUtils.reportFailedPasswordAttempt();
422        if (showTimeout) {
423            showTimeoutDialog();
424        }
425    }
426
427    /**
428     * Shows the primary security screen for the user. This will be either the multi-selector
429     * or the user's security method.
430     * @param turningOff true if the device is being turned off
431     */
432    void showPrimarySecurityScreen(boolean turningOff) {
433        SecurityMode securityMode = mSecurityModel.getSecurityMode();
434        if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
435        if (!turningOff && KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()) {
436            // If we're not turning off, then allow biometric alternate.
437            // We'll reload it when the device comes back on.
438            securityMode = mSecurityModel.getAlternateFor(securityMode);
439        }
440        showSecurityScreen(securityMode);
441    }
442
443    /**
444     * Shows the backup security screen for the current security mode.  This could be used for
445     * password recovery screens but is currently only used for pattern unlock to show the
446     * account unlock screen and biometric unlock to show the user's normal unlock.
447     */
448    private void showBackupSecurityScreen() {
449        if (DEBUG) Log.d(TAG, "showBackupSecurity()");
450        SecurityMode backup = mSecurityModel.getBackupSecurityMode(mCurrentSecuritySelection);
451        showSecurityScreen(backup);
452    }
453
454    public boolean showNextSecurityScreenIfPresent() {
455        SecurityMode securityMode = mSecurityModel.getSecurityMode();
456        // Allow an alternate, such as biometric unlock
457        securityMode = mSecurityModel.getAlternateFor(securityMode);
458        if (SecurityMode.None == securityMode) {
459            return false;
460        } else {
461            showSecurityScreen(securityMode); // switch to the alternate security view
462            return true;
463        }
464    }
465
466    private void showNextSecurityScreenOrFinish(boolean authenticated) {
467        if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
468        boolean finish = false;
469        if (SecurityMode.None == mCurrentSecuritySelection) {
470            SecurityMode securityMode = mSecurityModel.getSecurityMode();
471            // Allow an alternate, such as biometric unlock
472            securityMode = mSecurityModel.getAlternateFor(securityMode);
473            if (SecurityMode.None == securityMode) {
474                finish = true; // no security required
475            } else {
476                showSecurityScreen(securityMode); // switch to the alternate security view
477            }
478        } else if (authenticated) {
479            switch (mCurrentSecuritySelection) {
480                case Pattern:
481                case Password:
482                case Account:
483                case Biometric:
484                    finish = true;
485                    break;
486
487                case SimPin:
488                case SimPuk:
489                    // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
490                    SecurityMode securityMode = mSecurityModel.getSecurityMode();
491                    if (securityMode != SecurityMode.None) {
492                        showSecurityScreen(securityMode);
493                    } else {
494                        finish = true;
495                    }
496                    break;
497
498                default:
499                    Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
500                    showPrimarySecurityScreen(false);
501                    break;
502            }
503        } else {
504            showPrimarySecurityScreen(false);
505        }
506        if (finish) {
507            // If the alternate unlock was suppressed, it can now be safely
508            // enabled because the user has left keyguard.
509            KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
510            KeyguardUpdateMonitor.getInstance(mContext).setIsFirstBoot(false);
511
512            // If there's a pending runnable because the user interacted with a widget
513            // and we're leaving keyguard, then run it.
514            if (mLaunchRunnable != null) {
515                mLaunchRunnable.run();
516                mLaunchRunnable = null;
517            }
518            if (mViewMediatorCallback != null) {
519                mViewMediatorCallback.keyguardDone(true);
520            }
521        }
522    }
523
524    private OnClickHandler mOnClickHandler = new OnClickHandler() {
525        @Override
526        public boolean onClickHandler(final View view,
527                final android.app.PendingIntent pendingIntent,
528                final Intent fillInIntent) {
529            if (pendingIntent.isActivity()) {
530                setOnDismissRunnable(new Runnable() {
531                    public void run() {
532                        try {
533                              // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
534                              Context context = view.getContext();
535                              ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view,
536                                      0, 0,
537                                      view.getMeasuredWidth(), view.getMeasuredHeight());
538                              context.startIntentSender(
539                                      pendingIntent.getIntentSender(), fillInIntent,
540                                      Intent.FLAG_ACTIVITY_NEW_TASK,
541                                      Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
542                          } catch (IntentSender.SendIntentException e) {
543                              android.util.Log.e(TAG, "Cannot send pending intent: ", e);
544                          } catch (Exception e) {
545                              android.util.Log.e(TAG, "Cannot send pending intent due to " +
546                                      "unknown exception: ", e);
547                          }
548                    }
549                });
550
551                mCallback.dismiss(false);
552                return true;
553            } else {
554                return super.onClickHandler(view, pendingIntent, fillInIntent);
555            }
556        };
557    };
558
559    private KeyguardStatusViewManager mKeyguardStatusViewManager;
560
561    // Used to ignore callbacks from methods that are no longer current (e.g. face unlock).
562    // This avoids unwanted asynchronous events from messing with the state.
563    private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
564
565        @Override
566        public void userActivity(long timeout) {
567        }
568
569        @Override
570        public void showBackupSecurity() {
571        }
572
573        @Override
574        public void setOnDismissRunnable(Runnable runnable) {
575        }
576
577        @Override
578        public void reportSuccessfulUnlockAttempt() {
579        }
580
581        @Override
582        public void reportFailedUnlockAttempt() {
583        }
584
585        @Override
586        public boolean isVerifyUnlockOnly() {
587            return false;
588        }
589
590        @Override
591        public int getFailedAttempts() {
592            return 0;
593        }
594
595        @Override
596        public void dismiss(boolean securityVerified) {
597        }
598    };
599
600    @Override
601    public void reset() {
602        mIsVerifyUnlockOnly = false;
603        mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_status_view));
604    }
605
606    /**
607     *  Sets a runnable to run when keyguard is dismissed
608     * @param runnable
609     */
610    protected void setOnDismissRunnable(Runnable runnable) {
611        mLaunchRunnable = runnable;
612    }
613
614    private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
615        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
616        KeyguardSecurityView view = null;
617        final int children = mSecurityViewContainer.getChildCount();
618        for (int child = 0; child < children; child++) {
619            if (mSecurityViewContainer.getChildAt(child).getId() == securityViewIdForMode) {
620                view = ((KeyguardSecurityView)mSecurityViewContainer.getChildAt(child));
621                break;
622            }
623        }
624        boolean simPukFullScreen = getResources().getBoolean(
625                com.android.internal.R.bool.kg_sim_puk_account_full_screen);
626        int layoutId = getLayoutIdFor(securityMode);
627        if (view == null && layoutId != 0) {
628            final LayoutInflater inflater = LayoutInflater.from(mContext);
629            if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
630            View v = inflater.inflate(layoutId, this, false);
631            mSecurityViewContainer.addView(v);
632            updateSecurityView(v);
633
634            view = (KeyguardSecurityView) v;
635            TextView navigationText = ((TextView) findViewById(R.id.keyguard_message_area));
636
637            // Some devices can fit a navigation area, others cannot. On devices that cannot,
638            // we display the security message in status area.
639            if (navigationText != null) {
640                view.setSecurityMessageDisplay(new KeyguardNavigationManager(navigationText));
641            } else {
642                view.setSecurityMessageDisplay(mKeyguardStatusViewManager);
643            }
644        }
645
646        if (securityMode == SecurityMode.SimPin || securityMode == SecurityMode.SimPuk ||
647            securityMode == SecurityMode.Account) {
648            if (simPukFullScreen) {
649                mAppWidgetContainer.setVisibility(View.GONE);
650            }
651        }
652
653        if (view instanceof KeyguardSelectorView) {
654            KeyguardSelectorView selectorView = (KeyguardSelectorView) view;
655            View carrierText = selectorView.findViewById(R.id.keyguard_selector_fade_container);
656            selectorView.setCarrierArea(carrierText);
657        }
658
659        return view;
660    }
661
662    /**
663     * Switches to the given security view unless it's already being shown, in which case
664     * this is a no-op.
665     *
666     * @param securityMode
667     */
668    private void showSecurityScreen(SecurityMode securityMode) {
669        if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
670
671        if (securityMode == mCurrentSecuritySelection) return;
672
673        KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
674        KeyguardSecurityView newView = getSecurityView(securityMode);
675
676        // Emulate Activity life cycle
677        if (oldView != null) {
678            oldView.onPause();
679            oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
680        }
681        newView.onResume();
682        newView.setKeyguardCallback(mCallback);
683
684        final boolean needsInput = newView.needsInput();
685        if (mViewMediatorCallback != null) {
686            mViewMediatorCallback.setNeedsInput(needsInput);
687        }
688
689        // Find and show this child.
690        final int childCount = mSecurityViewContainer.getChildCount();
691
692        // Do flip animation to the next screen
693        if (false) {
694            mSecurityViewContainer.setInAnimation(
695                    AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_animate_in));
696            mSecurityViewContainer.setOutAnimation(
697                    AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_animate_out));
698        }
699        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
700        for (int i = 0; i < childCount; i++) {
701            if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) {
702                mSecurityViewContainer.setDisplayedChild(i);
703                break;
704            }
705        }
706
707        if (securityMode == SecurityMode.None) {
708            // Discard current runnable if we're switching back to the selector view
709            setOnDismissRunnable(null);
710        }
711        mCurrentSecuritySelection = securityMode;
712    }
713
714    @Override
715    public void onScreenTurnedOn() {
716        if (DEBUG) Log.d(TAG, "screen on");
717        showPrimarySecurityScreen(false);
718        getSecurityView(mCurrentSecuritySelection).onResume();
719
720        // This is a an attempt to fix bug 7137389 where the device comes back on but the entire
721        // layout is blank but forcing a layout causes it to reappear (e.g. with with
722        // hierarchyviewer).
723        requestLayout();
724    }
725
726    @Override
727    public void onScreenTurnedOff() {
728        if (DEBUG) Log.d(TAG, "screen off");
729        showPrimarySecurityScreen(true);
730        getSecurityView(mCurrentSecuritySelection).onPause();
731    }
732
733    @Override
734    public void show() {
735        onScreenTurnedOn();
736    }
737
738    private boolean isSecure() {
739        SecurityMode mode = mSecurityModel.getSecurityMode();
740        switch (mode) {
741            case Pattern:
742                return mLockPatternUtils.isLockPatternEnabled();
743            case Password:
744                return mLockPatternUtils.isLockPasswordEnabled();
745            case SimPin:
746            case SimPuk:
747            case Account:
748                return true;
749            case None:
750                return false;
751            default:
752                throw new IllegalStateException("Unknown security mode " + mode);
753        }
754    }
755
756    @Override
757    public void wakeWhenReadyTq(int keyCode) {
758        if (DEBUG) Log.d(TAG, "onWakeKey");
759        if (keyCode == KeyEvent.KEYCODE_MENU && isSecure()) {
760            if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU");
761            showSecurityScreen(SecurityMode.None);
762        } else {
763            if (DEBUG) Log.d(TAG, "poking wake lock immediately");
764        }
765        if (mViewMediatorCallback != null) {
766            mViewMediatorCallback.wakeUp();
767        }
768    }
769
770    @Override
771    public void verifyUnlock() {
772        SecurityMode securityMode = mSecurityModel.getSecurityMode();
773        if (securityMode == KeyguardSecurityModel.SecurityMode.None) {
774            if (mViewMediatorCallback != null) {
775                mViewMediatorCallback.keyguardDone(true);
776            }
777        } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
778                && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
779            // can only verify unlock when in pattern/password mode
780            if (mViewMediatorCallback != null) {
781                mViewMediatorCallback.keyguardDone(false);
782            }
783        } else {
784            // otherwise, go to the unlock screen, see if they can verify it
785            mIsVerifyUnlockOnly = true;
786            showSecurityScreen(securityMode);
787        }
788    }
789
790    private int getSecurityViewIdForMode(SecurityMode securityMode) {
791        switch (securityMode) {
792            case None: return R.id.keyguard_selector_view;
793            case Pattern: return R.id.keyguard_pattern_view;
794            case Password: return R.id.keyguard_password_view;
795            case Biometric: return R.id.keyguard_face_unlock_view;
796            case Account: return R.id.keyguard_account_view;
797            case SimPin: return R.id.keyguard_sim_pin_view;
798            case SimPuk: return R.id.keyguard_sim_puk_view;
799        }
800        return 0;
801    }
802
803    private int getLayoutIdFor(SecurityMode securityMode) {
804        switch (securityMode) {
805            case None: return R.layout.keyguard_selector_view;
806            case Pattern: return R.layout.keyguard_pattern_view;
807            case Password: return R.layout.keyguard_password_view;
808            case Biometric: return R.layout.keyguard_face_unlock_view;
809            case Account: return R.layout.keyguard_account_view;
810            case SimPin: return R.layout.keyguard_sim_pin_view;
811            case SimPuk: return R.layout.keyguard_sim_puk_view;
812            default:
813                return 0;
814        }
815    }
816
817    private void addWidget(int appId, int pageIndex) {
818        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(mContext);
819        AppWidgetProviderInfo appWidgetInfo = appWidgetManager.getAppWidgetInfo(appId);
820        if (appWidgetInfo != null) {
821            AppWidgetHostView view = getAppWidgetHost().createView(mContext, appId, appWidgetInfo);
822            addWidget(view, pageIndex);
823        } else {
824            Log.w(TAG, "AppWidgetInfo was null; not adding widget id " + appId);
825        }
826    }
827
828    private void addDefaultWidgets() {
829        LayoutInflater inflater = LayoutInflater.from(mContext);
830        inflater.inflate(R.layout.keyguard_transport_control_view, this, true);
831
832        View addWidget = inflater.inflate(R.layout.keyguard_add_widget, null, true);
833        mAppWidgetContainer.addWidget(addWidget);
834        View statusWidget = inflater.inflate(R.layout.keyguard_status_view, null, true);
835        mAppWidgetContainer.addWidget(statusWidget);
836        View cameraWidget = inflater.inflate(R.layout.keyguard_camera_widget, null, true);
837        mAppWidgetContainer.addWidget(cameraWidget);
838
839        inflateAndAddUserSelectorWidgetIfNecessary();
840        initializeTransportControl();
841    }
842
843    private void initializeTransportControl() {
844        mTransportControl =
845            (KeyguardTransportControlView) findViewById(R.id.keyguard_transport_control);
846        mTransportControl.setVisibility(View.GONE);
847
848        // This code manages showing/hiding the transport control. We keep it around and only
849        // add it to the hierarchy if it needs to be present.
850        if (mTransportControl != null) {
851            mTransportControl.setKeyguardCallback(new TransportCallback() {
852                @Override
853                public void onListenerDetached() {
854                    int page = getWidgetPosition(R.id.keyguard_transport_control);
855                    if (page != -1) {
856                        mAppWidgetContainer.removeView(mTransportControl);
857                        // XXX keep view attached so we still get show/hide events from AudioManager
858                        KeyguardHostView.this.addView(mTransportControl);
859                        mTransportControl.setVisibility(View.GONE);
860                        mTransportState = TRANSPORT_GONE;
861                        mTransportControl.post(mSwitchPageRunnable);
862                    }
863                }
864
865                @Override
866                public void onListenerAttached() {
867                    if (getWidgetPosition(R.id.keyguard_transport_control) == -1) {
868                        KeyguardHostView.this.removeView(mTransportControl);
869                        mAppWidgetContainer.addView(mTransportControl, 0);
870                        mTransportControl.setVisibility(View.VISIBLE);
871                    }
872                }
873
874                @Override
875                public void onPlayStateChanged() {
876                    mTransportControl.post(mSwitchPageRunnable);
877                }
878            });
879        }
880
881        mKeyguardStatusViewManager = ((KeyguardStatusView)
882                findViewById(R.id.keyguard_status_view_face_palm)).getManager();
883
884    }
885
886    private void maybePopulateWidgets() {
887        DevicePolicyManager dpm =
888                (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
889        if (dpm != null) {
890            final int currentUser = mLockPatternUtils.getCurrentUser();
891            final int disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUser);
892            if ((disabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0) {
893                Log.v(TAG, "Keyguard widgets disabled because of device policy admin");
894                return;
895            }
896        }
897
898        View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget);
899        int addPageIndex = mAppWidgetContainer.indexOfChild(addWidget);
900        // This shouldn't happen, but just to be safe!
901        if (addPageIndex < 0) {
902            addPageIndex = 0;
903        }
904
905        // Add user-selected widget
906        final int[] widgets = mLockPatternUtils.getUserDefinedWidgets();
907        for (int i = widgets.length -1; i >= 0; i--) {
908            if (widgets[i] != -1) {
909                // We add the widgets from left to right, starting after the first page after
910                // the add page. We count down, since the order will be persisted from right
911                // to left, starting after camera.
912                addWidget(widgets[i], addPageIndex + 1);
913            }
914        }
915    }
916
917    Runnable mSwitchPageRunnable = new Runnable() {
918        @Override
919        public void run() {
920           showAppropriateWidgetPage();
921        }
922    };
923
924    static class SavedState extends BaseSavedState {
925        int transportState;
926
927        SavedState(Parcelable superState) {
928            super(superState);
929        }
930
931        private SavedState(Parcel in) {
932            super(in);
933            this.transportState = in.readInt();
934        }
935
936        @Override
937        public void writeToParcel(Parcel out, int flags) {
938            super.writeToParcel(out, flags);
939            out.writeInt(this.transportState);
940        }
941
942        public static final Parcelable.Creator<SavedState> CREATOR
943                = new Parcelable.Creator<SavedState>() {
944            public SavedState createFromParcel(Parcel in) {
945                return new SavedState(in);
946            }
947
948            public SavedState[] newArray(int size) {
949                return new SavedState[size];
950            }
951        };
952    }
953
954    @Override
955    public Parcelable onSaveInstanceState() {
956        Parcelable superState = super.onSaveInstanceState();
957        SavedState ss = new SavedState(superState);
958        ss.transportState = mTransportState;
959        return ss;
960    }
961
962    @Override
963    public void onRestoreInstanceState(Parcelable state) {
964        if (!(state instanceof SavedState)) {
965            super.onRestoreInstanceState(state);
966            return;
967        }
968        SavedState ss = (SavedState) state;
969        super.onRestoreInstanceState(ss.getSuperState());
970        mTransportState = ss.transportState;
971        post(mSwitchPageRunnable);
972    }
973
974    private void showAppropriateWidgetPage() {
975
976        // The following sets the priority for showing widgets. Transport should be shown if
977        // music is playing, followed by the multi-user widget if enabled, followed by the
978        // status widget.
979        final int pageToShow;
980        if (mTransportControl.isMusicPlaying() || mTransportState == TRANSPORT_VISIBLE) {
981            mTransportState = TRANSPORT_VISIBLE;
982            pageToShow = mAppWidgetContainer.indexOfChild(mTransportControl);
983        } else {
984            UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
985            final View multiUserView = findViewById(R.id.keyguard_multi_user_selector);
986            final int multiUserPosition = mAppWidgetContainer.indexOfChild(multiUserView);
987            if (multiUserPosition != -1 && mUm.getUsers(true).size() > 1) {
988                pageToShow = multiUserPosition;
989            } else {
990                final View statusView = findViewById(R.id.keyguard_status_view);
991                pageToShow = mAppWidgetContainer.indexOfChild(statusView);
992            }
993            if (mTransportState == TRANSPORT_VISIBLE) {
994                mTransportState = TRANSPORT_INVISIBLE;
995            }
996        }
997        mAppWidgetContainer.setCurrentPage(pageToShow);
998    }
999
1000    private void inflateAndAddUserSelectorWidgetIfNecessary() {
1001        // if there are multiple users, we need to add the multi-user switcher widget to the
1002        // keyguard.
1003        UserManager mUm = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
1004        List<UserInfo> users = mUm.getUsers(true);
1005
1006        if (users.size() > 1) {
1007            KeyguardWidgetFrame userSwitcher = (KeyguardWidgetFrame)
1008                LayoutInflater.from(mContext).inflate(R.layout.keyguard_multi_user_selector_widget,
1009                        mAppWidgetContainer, false);
1010
1011            // add the switcher in the first position
1012            mAppWidgetContainer.addView(userSwitcher, getWidgetPosition(R.id.keyguard_status_view));
1013            KeyguardMultiUserSelectorView multiUser = (KeyguardMultiUserSelectorView)
1014                    userSwitcher.getChildAt(0);
1015
1016            UserSwitcherCallback callback = new UserSwitcherCallback() {
1017                @Override
1018                public void hideSecurityView(int duration) {
1019                    mSecurityViewContainer.animate().alpha(0).setDuration(duration);
1020                }
1021
1022                @Override
1023                public void showSecurityView() {
1024                    mSecurityViewContainer.setAlpha(1.0f);
1025                }
1026
1027                @Override
1028                public void showUnlockHint() {
1029                    if (mKeyguardSelectorView != null) {
1030                        mKeyguardSelectorView.ping();
1031                    }
1032                }
1033
1034            };
1035            multiUser.setCallback(callback);
1036        }
1037    }
1038
1039    @Override
1040    public void cleanUp() {
1041
1042    }
1043
1044    /**
1045     * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
1046     * some cases where we wish to disable it, notably when the menu button placement or technology
1047     * is prone to false positives.
1048     *
1049     * @return true if the menu key should be enabled
1050     */
1051    private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
1052    private boolean shouldEnableMenuKey() {
1053        final Resources res = getResources();
1054        final boolean configDisabled = res.getBoolean(
1055                com.android.internal.R.bool.config_disableMenuKeyInLockScreen);
1056        final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
1057        final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
1058        return !configDisabled || isTestHarness || fileOverride;
1059    }
1060
1061    @Override
1062    public boolean onKeyDown(int keyCode, KeyEvent event) {
1063        if (keyCode == KeyEvent.KEYCODE_MENU && mEnableMenuKey) {
1064            showNextSecurityScreenOrFinish(false);
1065            return true;
1066        } else {
1067            return super.onKeyDown(keyCode, event);
1068        }
1069    }
1070
1071    public void goToUserSwitcher() {
1072        mAppWidgetContainer.setCurrentPage(getWidgetPosition(R.id.keyguard_multi_user_selector));
1073    }
1074
1075    public boolean handleBackKey() {
1076        if (mCurrentSecuritySelection != SecurityMode.None) {
1077            mCallback.dismiss(false);
1078            return true;
1079        }
1080        return false;
1081    }
1082
1083}
1084