KeyguardHostView.java revision f8895248e2ac4dbb46622f3e04c7256f03175b4f
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.keyguard;
18
19import com.android.internal.widget.LockPatternUtils;
20import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
21import com.android.keyguard.KeyguardUpdateMonitor.DisplayClientState;
22
23import android.app.Activity;
24import android.app.ActivityManager;
25import android.app.ActivityOptions;
26import android.app.AlertDialog;
27import android.app.SearchManager;
28import android.app.admin.DevicePolicyManager;
29import android.appwidget.AppWidgetHost;
30import android.appwidget.AppWidgetHostView;
31import android.appwidget.AppWidgetManager;
32import android.appwidget.AppWidgetProviderInfo;
33import android.content.ComponentName;
34import android.content.Context;
35import android.content.Intent;
36import android.content.IntentSender;
37import android.content.pm.PackageManager.NameNotFoundException;
38import android.content.pm.UserInfo;
39import android.content.res.Resources;
40import android.graphics.Canvas;
41import android.graphics.Rect;
42import android.media.RemoteControlClient;
43import android.os.Looper;
44import android.os.Parcel;
45import android.os.Parcelable;
46import android.os.SystemClock;
47import android.os.UserHandle;
48import android.os.UserManager;
49import android.provider.Settings;
50import android.util.AttributeSet;
51import android.util.Log;
52import android.util.Slog;
53import android.view.LayoutInflater;
54import android.view.MotionEvent;
55import android.view.View;
56import android.view.WindowManager;
57import android.widget.RemoteViews.OnClickHandler;
58
59import java.io.File;
60import java.lang.ref.WeakReference;
61import java.util.List;
62
63public class KeyguardHostView extends KeyguardViewBase {
64    private static final String TAG = "KeyguardHostView";
65
66    // Transport control states.
67    static final int TRANSPORT_GONE = 0;
68    static final int TRANSPORT_INVISIBLE = 1;
69    static final int TRANSPORT_VISIBLE = 2;
70
71    private int mTransportState = TRANSPORT_GONE;
72
73    // Use this to debug all of keyguard
74    public static boolean DEBUG = KeyguardViewMediator.DEBUG;
75    public static boolean DEBUGXPORT = true; // debug music transport control
76
77    // Found in KeyguardAppWidgetPickActivity.java
78    static final int APPWIDGET_HOST_ID = 0x4B455947;
79
80    private final int MAX_WIDGETS = 5;
81
82    private AppWidgetHost mAppWidgetHost;
83    private AppWidgetManager mAppWidgetManager;
84    private KeyguardWidgetPager mAppWidgetContainer;
85    private KeyguardSecurityViewFlipper mSecurityViewContainer;
86    private KeyguardSelectorView mKeyguardSelectorView;
87    private KeyguardTransportControlView mTransportControl;
88    private boolean mIsVerifyUnlockOnly;
89    private boolean mEnableFallback; // TODO: This should get the value from KeyguardPatternView
90    private SecurityMode mCurrentSecuritySelection = SecurityMode.Invalid;
91    private int mAppWidgetToShow;
92
93    private boolean mCheckAppWidgetConsistencyOnBootCompleted = false;
94    private boolean mCleanupAppWidgetsOnBootCompleted = false;
95
96    protected OnDismissAction mDismissAction;
97
98    protected int mFailedAttempts;
99    private LockPatternUtils mLockPatternUtils;
100
101    private KeyguardSecurityModel mSecurityModel;
102    private KeyguardViewStateManager mViewStateManager;
103
104    private Rect mTempRect = new Rect();
105
106    private int mDisabledFeatures;
107
108    private boolean mCameraDisabled;
109
110    private boolean mSafeModeEnabled;
111
112    private boolean mUserSetupCompleted;
113
114    // User for whom this host view was created.  Final because we should never change the
115    // id without reconstructing an instance of KeyguardHostView. See note below...
116    private final int mUserId;
117
118    private KeyguardMultiUserSelectorView mKeyguardMultiUserSelectorView;
119
120    private boolean mIsScreenOn;
121
122    protected int mClientGeneration;
123
124    protected boolean mShowSecurityWhenReturn;
125
126    private final Rect mInsets = new Rect();
127
128    private MyOnClickHandler mOnClickHandler = new MyOnClickHandler(this);
129
130    /*package*/ interface UserSwitcherCallback {
131        void hideSecurityView(int duration);
132        void showSecurityView();
133        void showUnlockHint();
134        void userActivity();
135    }
136
137    interface TransportControlCallback {
138        void userActivity();
139    }
140
141    /*package*/ interface OnDismissAction {
142        /* returns true if the dismiss should be deferred */
143        boolean onDismiss();
144    }
145
146    public KeyguardHostView(Context context) {
147        this(context, null);
148    }
149
150    public KeyguardHostView(Context context, AttributeSet attrs) {
151        super(context, attrs);
152
153        if (DEBUG) Log.e(TAG, "KeyguardHostView()");
154
155        mLockPatternUtils = new LockPatternUtils(context);
156
157        // Note: This depends on KeyguardHostView getting reconstructed every time the
158        // user switches, since mUserId will be used for the entire session.
159        // Once created, keyguard should *never* re-use this instance with another user.
160        // In other words, mUserId should never change - hence it's marked final.
161        mUserId = mLockPatternUtils.getCurrentUser();
162
163        DevicePolicyManager dpm =
164                (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
165        if (dpm != null) {
166            mDisabledFeatures = getDisabledFeatures(dpm);
167            mCameraDisabled = dpm.getCameraDisabled(null);
168        }
169
170        mSafeModeEnabled = LockPatternUtils.isSafeModeEnabled();
171
172        // These need to be created with the user context...
173        Context userContext = null;
174        try {
175            final String packageName = "system";
176            userContext = mContext.createPackageContextAsUser(packageName, 0,
177                    new UserHandle(mUserId));
178
179        } catch (NameNotFoundException e) {
180            e.printStackTrace();
181            // This should never happen, but it's better to have no widgets than to crash.
182            userContext = context;
183        }
184
185        mAppWidgetHost = new AppWidgetHost(userContext, APPWIDGET_HOST_ID, mOnClickHandler,
186                Looper.myLooper());
187
188        cleanupAppWidgetIds();
189
190        mAppWidgetManager = AppWidgetManager.getInstance(userContext);
191
192        mSecurityModel = new KeyguardSecurityModel(context);
193
194        mViewStateManager = new KeyguardViewStateManager(this);
195
196        mUserSetupCompleted = Settings.Secure.getIntForUser(mContext.getContentResolver(),
197                Settings.Secure.USER_SETUP_COMPLETE, 0, UserHandle.USER_CURRENT) != 0;
198
199        // Ensure we have the current state *before* we call showAppropriateWidgetPage()
200        getInitialTransportState();
201
202        if (mSafeModeEnabled) {
203            Log.v(TAG, "Keyguard widgets disabled by safe mode");
204        }
205        if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0) {
206            Log.v(TAG, "Keyguard widgets disabled by DPM");
207        }
208        if ((mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0) {
209            Log.v(TAG, "Keyguard secure camera disabled by DPM");
210        }
211    }
212
213    public void announceCurrentSecurityMethod() {
214        View v = (View) getSecurityView(mCurrentSecuritySelection);
215        if (v != null) {
216            v.announceForAccessibility(v.getContentDescription());
217        }
218    }
219
220    private void getInitialTransportState() {
221        DisplayClientState dcs = KeyguardUpdateMonitor.getInstance(mContext)
222                .getCachedDisplayClientState();
223        mTransportState = (dcs.clearing ? TRANSPORT_GONE :
224            (isMusicPlaying(dcs.playbackState) ? TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE));
225
226        if (DEBUG) Log.v(TAG, "Initial transport state: "
227                + mTransportState + ", pbstate=" + dcs.playbackState);
228    }
229
230    private void cleanupAppWidgetIds() {
231        // Since this method may delete a widget (which we can't do until boot completed) we
232        // may have to defer it until after boot complete.
233        if (!KeyguardUpdateMonitor.getInstance(mContext).hasBootCompleted()) {
234            mCleanupAppWidgetsOnBootCompleted = true;
235            return;
236        }
237        if (!mSafeModeEnabled && !widgetsDisabled()) {
238            // Clean up appWidgetIds that are bound to lockscreen, but not actually used
239            // This is only to clean up after another bug: we used to not call
240            // deleteAppWidgetId when a user manually deleted a widget in keyguard. This code
241            // shouldn't have to run more than once per user. AppWidgetProviders rely on callbacks
242            // that are triggered by deleteAppWidgetId, which is why we're doing this
243            int[] appWidgetIdsInKeyguardSettings = mLockPatternUtils.getAppWidgets();
244            int[] appWidgetIdsBoundToHost = mAppWidgetHost.getAppWidgetIds();
245            for (int i = 0; i < appWidgetIdsBoundToHost.length; i++) {
246                int appWidgetId = appWidgetIdsBoundToHost[i];
247                if (!contains(appWidgetIdsInKeyguardSettings, appWidgetId)) {
248                    Log.d(TAG, "Found a appWidgetId that's not being used by keyguard, deleting id "
249                            + appWidgetId);
250                    mAppWidgetHost.deleteAppWidgetId(appWidgetId);
251                }
252            }
253        }
254    }
255
256    private static boolean contains(int[] array, int target) {
257        for (int value : array) {
258            if (value == target) {
259                return true;
260            }
261        }
262        return false;
263    }
264
265    private KeyguardUpdateMonitorCallback mUpdateMonitorCallbacks =
266            new KeyguardUpdateMonitorCallback() {
267        @Override
268        public void onBootCompleted() {
269            if (mCheckAppWidgetConsistencyOnBootCompleted) {
270                checkAppWidgetConsistency();
271                mSwitchPageRunnable.run();
272                mCheckAppWidgetConsistencyOnBootCompleted = false;
273            }
274            if (mCleanupAppWidgetsOnBootCompleted) {
275                cleanupAppWidgetIds();
276                mCleanupAppWidgetsOnBootCompleted = false;
277            }
278        }
279        @Override
280        public void onUserSwitchComplete(int userId) {
281            if (mKeyguardMultiUserSelectorView != null) {
282                mKeyguardMultiUserSelectorView.finalizeActiveUserView(true);
283            }
284        }
285        @Override
286        void onMusicClientIdChanged(
287                int clientGeneration, boolean clearing, android.app.PendingIntent intent) {
288            // Set transport state to invisible until we know music is playing (below)
289            if (DEBUGXPORT && (mClientGeneration != clientGeneration || clearing)) {
290                Log.v(TAG, (clearing ? "hide" : "show") + " transport, gen:" + clientGeneration);
291            }
292            mClientGeneration = clientGeneration;
293            final int newState = (clearing ? TRANSPORT_GONE
294                    : (mTransportState == TRANSPORT_VISIBLE ?
295                    TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE));
296            if (newState != mTransportState) {
297                mTransportState = newState;
298                if (DEBUGXPORT) Log.v(TAG, "update widget: transport state changed");
299                KeyguardHostView.this.post(mSwitchPageRunnable);
300            }
301        }
302        @Override
303        public void onMusicPlaybackStateChanged(int playbackState, long eventTime) {
304            if (DEBUGXPORT) Log.v(TAG, "music state changed: " + playbackState);
305            if (mTransportState != TRANSPORT_GONE) {
306                final int newState = (isMusicPlaying(playbackState) ?
307                        TRANSPORT_VISIBLE : TRANSPORT_INVISIBLE);
308                if (newState != mTransportState) {
309                    mTransportState = newState;
310                    if (DEBUGXPORT) Log.v(TAG, "update widget: play state changed");
311                    KeyguardHostView.this.post(mSwitchPageRunnable);
312                }
313            }
314        }
315    };
316
317    private static final boolean isMusicPlaying(int playbackState) {
318        // This should agree with the list in AudioService.isPlaystateActive()
319        switch (playbackState) {
320            case RemoteControlClient.PLAYSTATE_PLAYING:
321            case RemoteControlClient.PLAYSTATE_BUFFERING:
322            case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
323            case RemoteControlClient.PLAYSTATE_REWINDING:
324            case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
325            case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
326                return true;
327            default:
328                return false;
329        }
330    }
331
332    private SlidingChallengeLayout mSlidingChallengeLayout;
333    private MultiPaneChallengeLayout mMultiPaneChallengeLayout;
334
335    @Override
336    public boolean onTouchEvent(MotionEvent ev) {
337        boolean result = super.onTouchEvent(ev);
338        mTempRect.set(0, 0, 0, 0);
339        offsetRectIntoDescendantCoords(mSecurityViewContainer, mTempRect);
340        ev.offsetLocation(mTempRect.left, mTempRect.top);
341        result = mSecurityViewContainer.dispatchTouchEvent(ev) || result;
342        ev.offsetLocation(-mTempRect.left, -mTempRect.top);
343        return result;
344    }
345
346    @Override
347    protected void dispatchDraw(Canvas canvas) {
348        super.dispatchDraw(canvas);
349        if (mViewMediatorCallback != null) {
350            mViewMediatorCallback.keyguardDoneDrawing();
351        }
352    }
353
354    private int getWidgetPosition(int id) {
355        final KeyguardWidgetPager appWidgetContainer = mAppWidgetContainer;
356        final int children = appWidgetContainer.getChildCount();
357        for (int i = 0; i < children; i++) {
358            final View content = appWidgetContainer.getWidgetPageAt(i).getContent();
359            if (content != null && content.getId() == id) {
360                return i;
361            } else if (content == null) {
362                // Attempt to track down bug #8886916
363                Log.w(TAG, "*** Null content at " + "i=" + i + ",id=" + id + ",N=" + children);
364            }
365        }
366        return -1;
367    }
368
369    @Override
370    protected void onFinishInflate() {
371        // Grab instances of and make any necessary changes to the main layouts. Create
372        // view state manager and wire up necessary listeners / callbacks.
373        View deleteDropTarget = findViewById(R.id.keyguard_widget_pager_delete_target);
374        mAppWidgetContainer = (KeyguardWidgetPager) findViewById(R.id.app_widget_container);
375        mAppWidgetContainer.setVisibility(VISIBLE);
376        mAppWidgetContainer.setCallbacks(mWidgetCallbacks);
377        mAppWidgetContainer.setDeleteDropTarget(deleteDropTarget);
378        mAppWidgetContainer.setMinScale(0.5f);
379
380        mSlidingChallengeLayout = (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
381        if (mSlidingChallengeLayout != null) {
382            mSlidingChallengeLayout.setOnChallengeScrolledListener(mViewStateManager);
383        }
384        mAppWidgetContainer.setViewStateManager(mViewStateManager);
385        mAppWidgetContainer.setLockPatternUtils(mLockPatternUtils);
386
387        mMultiPaneChallengeLayout =
388                (MultiPaneChallengeLayout) findViewById(R.id.multi_pane_challenge);
389        ChallengeLayout challenge = mSlidingChallengeLayout != null ? mSlidingChallengeLayout :
390                mMultiPaneChallengeLayout;
391        challenge.setOnBouncerStateChangedListener(mViewStateManager);
392        mAppWidgetContainer.setBouncerAnimationDuration(challenge.getBouncerAnimationDuration());
393        mViewStateManager.setPagedView(mAppWidgetContainer);
394        mViewStateManager.setChallengeLayout(challenge);
395        mSecurityViewContainer = (KeyguardSecurityViewFlipper) findViewById(R.id.view_flipper);
396        mKeyguardSelectorView = (KeyguardSelectorView) findViewById(R.id.keyguard_selector_view);
397        mViewStateManager.setSecurityViewContainer(mSecurityViewContainer);
398
399        setBackButtonEnabled(false);
400
401        addDefaultWidgets();
402
403        addWidgetsFromSettings();
404        if (!shouldEnableAddWidget()) {
405            mAppWidgetContainer.setAddWidgetEnabled(false);
406        }
407        checkAppWidgetConsistency();
408        mSwitchPageRunnable.run();
409        // This needs to be called after the pages are all added.
410        mViewStateManager.showUsabilityHints();
411
412        showPrimarySecurityScreen(false);
413        updateSecurityViews();
414    }
415
416    private void setBackButtonEnabled(boolean enabled) {
417        if (mContext instanceof Activity) return;  // always enabled in activity mode
418        setSystemUiVisibility(enabled ?
419                getSystemUiVisibility() & ~View.STATUS_BAR_DISABLE_BACK :
420                getSystemUiVisibility() | View.STATUS_BAR_DISABLE_BACK);
421    }
422
423    private boolean shouldEnableAddWidget() {
424        return numWidgets() < MAX_WIDGETS && mUserSetupCompleted;
425    }
426
427    private int getDisabledFeatures(DevicePolicyManager dpm) {
428        int disabledFeatures = DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
429        if (dpm != null) {
430            final int currentUser = mLockPatternUtils.getCurrentUser();
431            disabledFeatures = dpm.getKeyguardDisabledFeatures(null, currentUser);
432        }
433        return disabledFeatures;
434    }
435
436    private boolean widgetsDisabled() {
437        boolean disabledByLowRamDevice = ActivityManager.isLowRamDeviceStatic();
438        boolean disabledByDpm =
439                (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_WIDGETS_ALL) != 0;
440        boolean disabledByUser = !mLockPatternUtils.getWidgetsEnabled();
441        return disabledByLowRamDevice || disabledByDpm || disabledByUser;
442    }
443
444    private boolean cameraDisabledByDpm() {
445        return mCameraDisabled
446                || (mDisabledFeatures & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) != 0;
447    }
448
449    private void updateSecurityViews() {
450        int children = mSecurityViewContainer.getChildCount();
451        for (int i = 0; i < children; i++) {
452            updateSecurityView(mSecurityViewContainer.getChildAt(i));
453        }
454    }
455
456    private void updateSecurityView(View view) {
457        if (view instanceof KeyguardSecurityView) {
458            KeyguardSecurityView ksv = (KeyguardSecurityView) view;
459            ksv.setKeyguardCallback(mCallback);
460            ksv.setLockPatternUtils(mLockPatternUtils);
461            if (mViewStateManager.isBouncing()) {
462                ksv.showBouncer(0);
463            } else {
464                ksv.hideBouncer(0);
465            }
466        } else {
467            Log.w(TAG, "View " + view + " is not a KeyguardSecurityView");
468        }
469    }
470
471    void setLockPatternUtils(LockPatternUtils utils) {
472        mSecurityModel.setLockPatternUtils(utils);
473        mLockPatternUtils = utils;
474        updateSecurityViews();
475    }
476
477    @Override
478    protected void onAttachedToWindow() {
479        super.onAttachedToWindow();
480        mAppWidgetHost.startListening();
481        KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateMonitorCallbacks);
482    }
483
484    @Override
485    protected void onDetachedFromWindow() {
486        super.onDetachedFromWindow();
487        mAppWidgetHost.stopListening();
488        KeyguardUpdateMonitor.getInstance(mContext).removeCallback(mUpdateMonitorCallbacks);
489    }
490
491    void addWidget(AppWidgetHostView view, int pageIndex) {
492        mAppWidgetContainer.addWidget(view, pageIndex);
493    }
494
495    private KeyguardWidgetPager.Callbacks mWidgetCallbacks
496            = new KeyguardWidgetPager.Callbacks() {
497        @Override
498        public void userActivity() {
499            KeyguardHostView.this.userActivity();
500        }
501
502        @Override
503        public void onUserActivityTimeoutChanged() {
504            KeyguardHostView.this.onUserActivityTimeoutChanged();
505        }
506
507        @Override
508        public void onAddView(View v) {
509            if (!shouldEnableAddWidget()) {
510                mAppWidgetContainer.setAddWidgetEnabled(false);
511            }
512        }
513
514        @Override
515        public void onRemoveView(View v, boolean deletePermanently) {
516            if (deletePermanently) {
517                final int appWidgetId = ((KeyguardWidgetFrame) v).getContentAppWidgetId();
518                if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID &&
519                        appWidgetId != LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) {
520                    mAppWidgetHost.deleteAppWidgetId(appWidgetId);
521                }
522            }
523        }
524
525        @Override
526        public void onRemoveViewAnimationCompleted() {
527            if (shouldEnableAddWidget()) {
528                mAppWidgetContainer.setAddWidgetEnabled(true);
529            }
530        }
531    };
532
533    public void initializeSwitchingUserState(boolean switching) {
534        if (!switching && mKeyguardMultiUserSelectorView != null) {
535            mKeyguardMultiUserSelectorView.finalizeActiveUserView(false);
536        }
537    }
538
539    public void userActivity() {
540        if (mViewMediatorCallback != null) {
541            mViewMediatorCallback.userActivity();
542        }
543    }
544
545    public void onUserActivityTimeoutChanged() {
546        if (mViewMediatorCallback != null) {
547            mViewMediatorCallback.onUserActivityTimeoutChanged();
548        }
549    }
550
551    @Override
552    public long getUserActivityTimeout() {
553        // Currently only considering user activity timeouts needed by widgets.
554        // Could also take into account longer timeouts for certain security views.
555        if (mAppWidgetContainer != null) {
556            return mAppWidgetContainer.getUserActivityTimeout();
557        }
558        return -1;
559    }
560
561    private KeyguardSecurityCallback mCallback = new KeyguardSecurityCallback() {
562
563        public void userActivity(long timeout) {
564            if (mViewMediatorCallback != null) {
565                mViewMediatorCallback.userActivity(timeout);
566            }
567        }
568
569        public void dismiss(boolean authenticated) {
570            showNextSecurityScreenOrFinish(authenticated);
571        }
572
573        public boolean isVerifyUnlockOnly() {
574            return mIsVerifyUnlockOnly;
575        }
576
577        public void reportSuccessfulUnlockAttempt() {
578            KeyguardUpdateMonitor.getInstance(mContext).clearFailedUnlockAttempts();
579            mLockPatternUtils.reportSuccessfulPasswordAttempt();
580        }
581
582        public void reportFailedUnlockAttempt() {
583            if (mCurrentSecuritySelection == SecurityMode.Biometric) {
584                KeyguardUpdateMonitor.getInstance(mContext).reportFailedBiometricUnlockAttempt();
585            } else {
586                KeyguardHostView.this.reportFailedUnlockAttempt();
587            }
588        }
589
590        public int getFailedAttempts() {
591            return KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts();
592        }
593
594        @Override
595        public void showBackupSecurity() {
596            KeyguardHostView.this.showBackupSecurityScreen();
597        }
598
599        @Override
600        public void setOnDismissAction(OnDismissAction action) {
601            KeyguardHostView.this.setOnDismissAction(action);
602        }
603
604    };
605
606    private void showDialog(String title, String message) {
607        final AlertDialog dialog = new AlertDialog.Builder(mContext)
608            .setTitle(title)
609            .setMessage(message)
610            .setNeutralButton(R.string.ok, null)
611            .create();
612        if (!(mContext instanceof Activity)) {
613            dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
614        }
615        dialog.show();
616    }
617
618    private void showTimeoutDialog() {
619        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
620        int messageId = 0;
621
622        switch (mSecurityModel.getSecurityMode()) {
623            case Pattern:
624                messageId = R.string.kg_too_many_failed_pattern_attempts_dialog_message;
625                break;
626            case PIN:
627                messageId = R.string.kg_too_many_failed_pin_attempts_dialog_message;
628                break;
629            case Password:
630                messageId = R.string.kg_too_many_failed_password_attempts_dialog_message;
631                break;
632        }
633
634        if (messageId != 0) {
635            final String message = mContext.getString(messageId,
636                    KeyguardUpdateMonitor.getInstance(mContext).getFailedUnlockAttempts(),
637                    timeoutInSeconds);
638            showDialog(null, message);
639        }
640    }
641
642    private void showAlmostAtWipeDialog(int attempts, int remaining) {
643        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
644        String message = mContext.getString(R.string.kg_failed_attempts_almost_at_wipe,
645                attempts, remaining);
646        showDialog(null, message);
647    }
648
649    private void showWipeDialog(int attempts) {
650        String message = mContext.getString(R.string.kg_failed_attempts_now_wiping, attempts);
651        showDialog(null, message);
652    }
653
654    private void showAlmostAtAccountLoginDialog() {
655        final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
656        final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
657                - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
658        String message = mContext.getString(R.string.kg_failed_attempts_almost_at_login,
659                count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds);
660        showDialog(null, message);
661    }
662
663    private void reportFailedUnlockAttempt() {
664        final KeyguardUpdateMonitor monitor = KeyguardUpdateMonitor.getInstance(mContext);
665        final int failedAttempts = monitor.getFailedUnlockAttempts() + 1; // +1 for this time
666
667        if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts);
668
669        SecurityMode mode = mSecurityModel.getSecurityMode();
670        final boolean usingPattern = mode == KeyguardSecurityModel.SecurityMode.Pattern;
671
672        final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager()
673                .getMaximumFailedPasswordsForWipe(null, mLockPatternUtils.getCurrentUser());
674
675        final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
676                - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
677
678        final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
679                (failedAttemptsBeforeWipe - failedAttempts)
680                : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
681
682        boolean showTimeout = false;
683        if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
684            // If we reach this code, it means the user has installed a DevicePolicyManager
685            // that requests device wipe after N attempts.  Once we get below the grace
686            // period, we'll post this dialog every time as a clear warning until the
687            // bombshell hits and the device is wiped.
688            if (remainingBeforeWipe > 0) {
689                showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe);
690            } else {
691                // Too many attempts. The device will be wiped shortly.
692                Slog.i(TAG, "Too many unlock attempts; device will be wiped!");
693                showWipeDialog(failedAttempts);
694            }
695        } else {
696            showTimeout =
697                (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
698            if (usingPattern && mEnableFallback) {
699                if (failedAttempts == failedAttemptWarning) {
700                    showAlmostAtAccountLoginDialog();
701                    showTimeout = false; // don't show both dialogs
702                } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
703                    mLockPatternUtils.setPermanentlyLocked(true);
704                    showSecurityScreen(SecurityMode.Account);
705                    // don't show timeout dialog because we show account unlock screen next
706                    showTimeout = false;
707                }
708            }
709        }
710        monitor.reportFailedUnlockAttempt();
711        mLockPatternUtils.reportFailedPasswordAttempt();
712        if (showTimeout) {
713            showTimeoutDialog();
714        }
715    }
716
717    /**
718     * Shows the primary security screen for the user. This will be either the multi-selector
719     * or the user's security method.
720     * @param turningOff true if the device is being turned off
721     */
722    void showPrimarySecurityScreen(boolean turningOff) {
723        SecurityMode securityMode = mSecurityModel.getSecurityMode();
724        if (DEBUG) Log.v(TAG, "showPrimarySecurityScreen(turningOff=" + turningOff + ")");
725        if (!turningOff &&
726                KeyguardUpdateMonitor.getInstance(mContext).isAlternateUnlockEnabled()) {
727            // If we're not turning off, then allow biometric alternate.
728            // We'll reload it when the device comes back on.
729            securityMode = mSecurityModel.getAlternateFor(securityMode);
730        }
731        showSecurityScreen(securityMode);
732    }
733
734    /**
735     * Shows the backup security screen for the current security mode.  This could be used for
736     * password recovery screens but is currently only used for pattern unlock to show the
737     * account unlock screen and biometric unlock to show the user's normal unlock.
738     */
739    private void showBackupSecurityScreen() {
740        if (DEBUG) Log.d(TAG, "showBackupSecurity()");
741        SecurityMode backup = mSecurityModel.getBackupSecurityMode(mCurrentSecuritySelection);
742        showSecurityScreen(backup);
743    }
744
745    public boolean showNextSecurityScreenIfPresent() {
746        SecurityMode securityMode = mSecurityModel.getSecurityMode();
747        // Allow an alternate, such as biometric unlock
748        securityMode = mSecurityModel.getAlternateFor(securityMode);
749        if (SecurityMode.None == securityMode) {
750            return false;
751        } else {
752            showSecurityScreen(securityMode); // switch to the alternate security view
753            return true;
754        }
755    }
756
757    private void showNextSecurityScreenOrFinish(boolean authenticated) {
758        if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
759        boolean finish = false;
760        if (SecurityMode.None == mCurrentSecuritySelection) {
761            SecurityMode securityMode = mSecurityModel.getSecurityMode();
762            // Allow an alternate, such as biometric unlock
763            securityMode = mSecurityModel.getAlternateFor(securityMode);
764            if (SecurityMode.None == securityMode) {
765                finish = true; // no security required
766            } else {
767                showSecurityScreen(securityMode); // switch to the alternate security view
768            }
769        } else if (authenticated) {
770            switch (mCurrentSecuritySelection) {
771                case Pattern:
772                case Password:
773                case PIN:
774                case Account:
775                case Biometric:
776                    finish = true;
777                    break;
778
779                case SimPin:
780                case SimPuk:
781                    // Shortcut for SIM PIN/PUK to go to directly to user's security screen or home
782                    SecurityMode securityMode = mSecurityModel.getSecurityMode();
783                    if (securityMode != SecurityMode.None) {
784                        showSecurityScreen(securityMode);
785                    } else {
786                        finish = true;
787                    }
788                    break;
789
790                default:
791                    Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
792                    showPrimarySecurityScreen(false);
793                    break;
794            }
795        } else {
796            showPrimarySecurityScreen(false);
797        }
798        if (finish) {
799            // If the alternate unlock was suppressed, it can now be safely
800            // enabled because the user has left keyguard.
801            KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
802
803            // If there's a pending runnable because the user interacted with a widget
804            // and we're leaving keyguard, then run it.
805            boolean deferKeyguardDone = false;
806            if (mDismissAction != null) {
807                deferKeyguardDone = mDismissAction.onDismiss();
808                mDismissAction = null;
809            }
810            if (mViewMediatorCallback != null) {
811                if (deferKeyguardDone) {
812                    mViewMediatorCallback.keyguardDonePending();
813                } else {
814                    mViewMediatorCallback.keyguardDone(true);
815                }
816            }
817        } else {
818            mViewStateManager.showBouncer(true);
819        }
820    }
821
822    private static class MyOnClickHandler extends OnClickHandler {
823
824        // weak reference to the hostView to avoid keeping a live reference
825        // due to Binder GC linkages to AppWidgetHost. By the same token,
826        // this click handler should not keep references to any large
827        // objects.
828        WeakReference<KeyguardHostView> mThis;
829
830        MyOnClickHandler(KeyguardHostView hostView) {
831            mThis = new WeakReference<KeyguardHostView>(hostView);
832        }
833
834        @Override
835        public boolean onClickHandler(final View view,
836                final android.app.PendingIntent pendingIntent,
837                final Intent fillInIntent) {
838            KeyguardHostView hostView = mThis.get();
839            if (hostView == null) {
840                return false;
841            }
842            if (pendingIntent.isActivity()) {
843                hostView.setOnDismissAction(new OnDismissAction() {
844                    public boolean onDismiss() {
845                        try {
846                            // TODO: Unregister this handler if PendingIntent.FLAG_ONE_SHOT?
847                            Context context = view.getContext();
848                            ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(view,
849                                    0, 0,
850                                    view.getMeasuredWidth(), view.getMeasuredHeight());
851                            context.startIntentSender(
852                                    pendingIntent.getIntentSender(), fillInIntent,
853                                    Intent.FLAG_ACTIVITY_NEW_TASK,
854                                    Intent.FLAG_ACTIVITY_NEW_TASK, 0, opts.toBundle());
855                        } catch (IntentSender.SendIntentException e) {
856                            android.util.Log.e(TAG, "Cannot send pending intent: ", e);
857                        } catch (Exception e) {
858                            android.util.Log.e(TAG, "Cannot send pending intent due to " +
859                                    "unknown exception: ", e);
860                        }
861                        return false;
862                    }
863                });
864
865                if (hostView.mViewStateManager.isChallengeShowing()) {
866                    hostView.mViewStateManager.showBouncer(true);
867                } else {
868                    hostView.mCallback.dismiss(false);
869                }
870                return true;
871            } else {
872                return super.onClickHandler(view, pendingIntent, fillInIntent);
873            }
874        };
875    };
876
877    // Used to ignore callbacks from methods that are no longer current (e.g. face unlock).
878    // This avoids unwanted asynchronous events from messing with the state.
879    private KeyguardSecurityCallback mNullCallback = new KeyguardSecurityCallback() {
880
881        @Override
882        public void userActivity(long timeout) {
883        }
884
885        @Override
886        public void showBackupSecurity() {
887        }
888
889        @Override
890        public void setOnDismissAction(OnDismissAction action) {
891        }
892
893        @Override
894        public void reportSuccessfulUnlockAttempt() {
895        }
896
897        @Override
898        public void reportFailedUnlockAttempt() {
899        }
900
901        @Override
902        public boolean isVerifyUnlockOnly() {
903            return false;
904        }
905
906        @Override
907        public int getFailedAttempts() {
908            return 0;
909        }
910
911        @Override
912        public void dismiss(boolean securityVerified) {
913        }
914    };
915
916    /**
917     * Sets an action to perform when keyguard is dismissed.
918     * @param action
919     */
920    protected void setOnDismissAction(OnDismissAction action) {
921        mDismissAction = action;
922    }
923
924    private KeyguardSecurityView getSecurityView(SecurityMode securityMode) {
925        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
926        KeyguardSecurityView view = null;
927        final int children = mSecurityViewContainer.getChildCount();
928        for (int child = 0; child < children; child++) {
929            if (mSecurityViewContainer.getChildAt(child).getId() == securityViewIdForMode) {
930                view = ((KeyguardSecurityView)mSecurityViewContainer.getChildAt(child));
931                break;
932            }
933        }
934        int layoutId = getLayoutIdFor(securityMode);
935        if (view == null && layoutId != 0) {
936            final LayoutInflater inflater = LayoutInflater.from(mContext);
937            if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
938            View v = inflater.inflate(layoutId, mSecurityViewContainer, false);
939            mSecurityViewContainer.addView(v);
940            updateSecurityView(v);
941            view = (KeyguardSecurityView)v;
942        }
943
944        if (view instanceof KeyguardSelectorView) {
945            KeyguardSelectorView selectorView = (KeyguardSelectorView) view;
946            View carrierText = selectorView.findViewById(R.id.keyguard_selector_fade_container);
947            selectorView.setCarrierArea(carrierText);
948        }
949
950        return view;
951    }
952
953    /**
954     * Switches to the given security view unless it's already being shown, in which case
955     * this is a no-op.
956     *
957     * @param securityMode
958     */
959    private void showSecurityScreen(SecurityMode securityMode) {
960        if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
961
962        if (securityMode == mCurrentSecuritySelection) return;
963
964        KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
965        KeyguardSecurityView newView = getSecurityView(securityMode);
966
967        // Enter full screen mode if we're in SIM or Account screen
968        boolean fullScreenEnabled = getResources().getBoolean(R.bool.kg_sim_puk_account_full_screen);
969        boolean isSimOrAccount = securityMode == SecurityMode.SimPin
970                || securityMode == SecurityMode.SimPuk
971                || securityMode == SecurityMode.Account;
972        mAppWidgetContainer.setVisibility(
973                isSimOrAccount && fullScreenEnabled ? View.GONE : View.VISIBLE);
974
975        if (mSlidingChallengeLayout != null) {
976            mSlidingChallengeLayout.setChallengeInteractive(!fullScreenEnabled);
977        }
978
979        // Emulate Activity life cycle
980        if (oldView != null) {
981            oldView.onPause();
982            oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view
983        }
984        newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
985        newView.setKeyguardCallback(mCallback);
986
987        final boolean needsInput = newView.needsInput();
988        if (mViewMediatorCallback != null) {
989            mViewMediatorCallback.setNeedsInput(needsInput);
990        }
991
992        // Find and show this child.
993        final int childCount = mSecurityViewContainer.getChildCount();
994
995        final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);
996        for (int i = 0; i < childCount; i++) {
997            if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) {
998                mSecurityViewContainer.setDisplayedChild(i);
999                break;
1000            }
1001        }
1002
1003        if (securityMode == SecurityMode.None) {
1004            // Discard current runnable if we're switching back to the selector view
1005            setOnDismissAction(null);
1006        }
1007        if (securityMode == SecurityMode.Account && !mLockPatternUtils.isPermanentlyLocked()) {
1008            // we're showing account as a backup, provide a way to get back to primary
1009            setBackButtonEnabled(true);
1010        }
1011        mCurrentSecuritySelection = securityMode;
1012    }
1013
1014    @Override
1015    public void onScreenTurnedOn() {
1016        if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
1017        mIsScreenOn = true;
1018        showPrimarySecurityScreen(false);
1019        getSecurityView(mCurrentSecuritySelection).onResume(KeyguardSecurityView.SCREEN_ON);
1020
1021        // This is a an attempt to fix bug 7137389 where the device comes back on but the entire
1022        // layout is blank but forcing a layout causes it to reappear (e.g. with with
1023        // hierarchyviewer).
1024        requestLayout();
1025
1026        if (mViewStateManager != null) {
1027            mViewStateManager.showUsabilityHints();
1028        }
1029
1030        requestFocus();
1031    }
1032
1033    @Override
1034    public void onScreenTurnedOff() {
1035        if (DEBUG) Log.d(TAG, String.format("screen off, instance %s at %s",
1036                Integer.toHexString(hashCode()), SystemClock.uptimeMillis()));
1037        mIsScreenOn = false;
1038        // Once the screen turns off, we no longer consider this to be first boot and we want the
1039        // biometric unlock to start next time keyguard is shown.
1040        KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(true);
1041        // We use mAppWidgetToShow to show a particular widget after you add it-- once the screen
1042        // turns off we reset that behavior
1043        clearAppWidgetToShow();
1044        checkAppWidgetConsistency();
1045        showPrimarySecurityScreen(true);
1046        getSecurityView(mCurrentSecuritySelection).onPause();
1047        CameraWidgetFrame cameraPage = findCameraPage();
1048        if (cameraPage != null) {
1049            cameraPage.onScreenTurnedOff();
1050        }
1051
1052        clearFocus();
1053    }
1054
1055    public void clearAppWidgetToShow() {
1056        mAppWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
1057    }
1058
1059    @Override
1060    public void show() {
1061        if (DEBUG) Log.d(TAG, "show()");
1062        showPrimarySecurityScreen(false);
1063    }
1064
1065    @Override
1066    public void verifyUnlock() {
1067        SecurityMode securityMode = mSecurityModel.getSecurityMode();
1068        if (securityMode == KeyguardSecurityModel.SecurityMode.None) {
1069            if (mViewMediatorCallback != null) {
1070                mViewMediatorCallback.keyguardDone(true);
1071            }
1072        } else if (securityMode != KeyguardSecurityModel.SecurityMode.Pattern
1073                && securityMode != KeyguardSecurityModel.SecurityMode.PIN
1074                && securityMode != KeyguardSecurityModel.SecurityMode.Password) {
1075            // can only verify unlock when in pattern/password mode
1076            if (mViewMediatorCallback != null) {
1077                mViewMediatorCallback.keyguardDone(false);
1078            }
1079        } else {
1080            // otherwise, go to the unlock screen, see if they can verify it
1081            mIsVerifyUnlockOnly = true;
1082            showSecurityScreen(securityMode);
1083        }
1084    }
1085
1086    private int getSecurityViewIdForMode(SecurityMode securityMode) {
1087        switch (securityMode) {
1088            case None: return R.id.keyguard_selector_view;
1089            case Pattern: return R.id.keyguard_pattern_view;
1090            case PIN: return R.id.keyguard_pin_view;
1091            case Password: return R.id.keyguard_password_view;
1092            case Biometric: return R.id.keyguard_face_unlock_view;
1093            case Account: return R.id.keyguard_account_view;
1094            case SimPin: return R.id.keyguard_sim_pin_view;
1095            case SimPuk: return R.id.keyguard_sim_puk_view;
1096        }
1097        return 0;
1098    }
1099
1100    private int getLayoutIdFor(SecurityMode securityMode) {
1101        switch (securityMode) {
1102            case None: return R.layout.keyguard_selector_view;
1103            case Pattern: return R.layout.keyguard_pattern_view;
1104            case PIN: return R.layout.keyguard_pin_view;
1105            case Password: return R.layout.keyguard_password_view;
1106            case Biometric: return R.layout.keyguard_face_unlock_view;
1107            case Account: return R.layout.keyguard_account_view;
1108            case SimPin: return R.layout.keyguard_sim_pin_view;
1109            case SimPuk: return R.layout.keyguard_sim_puk_view;
1110            default:
1111                return 0;
1112        }
1113    }
1114
1115    private boolean addWidget(int appId, int pageIndex, boolean updateDbIfFailed) {
1116        AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appId);
1117        if (appWidgetInfo != null) {
1118            AppWidgetHostView view = mAppWidgetHost.createView(mContext, appId, appWidgetInfo);
1119            addWidget(view, pageIndex);
1120            return true;
1121        } else {
1122            if (updateDbIfFailed) {
1123                Log.w(TAG, "*** AppWidgetInfo for app widget id " + appId + "  was null for user"
1124                        + mUserId + ", deleting");
1125                mAppWidgetHost.deleteAppWidgetId(appId);
1126                mLockPatternUtils.removeAppWidget(appId);
1127            }
1128            return false;
1129        }
1130    }
1131
1132    private final CameraWidgetFrame.Callbacks mCameraWidgetCallbacks =
1133        new CameraWidgetFrame.Callbacks() {
1134            @Override
1135            public void onLaunchingCamera() {
1136                setSliderHandleAlpha(0);
1137            }
1138
1139            @Override
1140            public void onCameraLaunchedSuccessfully() {
1141                if (mAppWidgetContainer.isCameraPage(mAppWidgetContainer.getCurrentPage())) {
1142                    mAppWidgetContainer.scrollLeft();
1143                }
1144                setSliderHandleAlpha(1);
1145                mShowSecurityWhenReturn = true;
1146            }
1147
1148            @Override
1149            public void onCameraLaunchedUnsuccessfully() {
1150                setSliderHandleAlpha(1);
1151            }
1152
1153            private void setSliderHandleAlpha(float alpha) {
1154                SlidingChallengeLayout slider =
1155                        (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
1156                if (slider != null) {
1157                    slider.setHandleAlpha(alpha);
1158                }
1159            }
1160        };
1161
1162    private final KeyguardActivityLauncher mActivityLauncher = new KeyguardActivityLauncher() {
1163        @Override
1164        Context getContext() {
1165            return mContext;
1166        }
1167
1168        @Override
1169        KeyguardSecurityCallback getCallback() {
1170            return mCallback;
1171        }
1172
1173        @Override
1174        LockPatternUtils getLockPatternUtils() {
1175            return mLockPatternUtils;
1176        }
1177    };
1178
1179    private int numWidgets() {
1180        final int childCount = mAppWidgetContainer.getChildCount();
1181        int widgetCount = 0;
1182        for (int i = 0; i < childCount; i++) {
1183            if (mAppWidgetContainer.isWidgetPage(i)) {
1184                widgetCount++;
1185            }
1186        }
1187        return widgetCount;
1188    }
1189
1190    private void addDefaultWidgets() {
1191        if (!mSafeModeEnabled && !widgetsDisabled()) {
1192            LayoutInflater inflater = LayoutInflater.from(mContext);
1193            View addWidget = inflater.inflate(R.layout.keyguard_add_widget, this, false);
1194            mAppWidgetContainer.addWidget(addWidget, 0);
1195            View addWidgetButton = addWidget.findViewById(R.id.keyguard_add_widget_view);
1196            addWidgetButton.setOnClickListener(new OnClickListener() {
1197                @Override
1198                public void onClick(View v) {
1199                    // Pass in an invalid widget id... the picker will allocate an ID for us
1200                    mActivityLauncher.launchWidgetPicker(AppWidgetManager.INVALID_APPWIDGET_ID);
1201                }
1202            });
1203        }
1204
1205        // We currently disable cameras in safe mode because we support loading 3rd party
1206        // cameras we can't trust.  TODO: plumb safe mode into camera creation code and only
1207        // inflate system-provided camera?
1208        if (!mSafeModeEnabled && !cameraDisabledByDpm() && mUserSetupCompleted
1209                && mContext.getResources().getBoolean(R.bool.kg_enable_camera_default_widget)) {
1210            View cameraWidget =
1211                    CameraWidgetFrame.create(mContext, mCameraWidgetCallbacks, mActivityLauncher);
1212            if (cameraWidget != null) {
1213                mAppWidgetContainer.addWidget(cameraWidget);
1214            }
1215        }
1216
1217        enableUserSelectorIfNecessary();
1218    }
1219
1220    /**
1221     * Create KeyguardTransportControlView on demand.
1222     * @return
1223     */
1224    private KeyguardTransportControlView getOrCreateTransportControl() {
1225        if (mTransportControl == null) {
1226            LayoutInflater inflater = LayoutInflater.from(mContext);
1227            mTransportControl = (KeyguardTransportControlView)
1228                    inflater.inflate(R.layout.keyguard_transport_control_view, this, false);
1229            mTransportControl.setTransportControlCallback(new TransportControlCallback() {
1230                public void userActivity() {
1231                    mViewMediatorCallback.userActivity();
1232                }
1233            });
1234        }
1235        return mTransportControl;
1236    }
1237
1238    private int getInsertPageIndex() {
1239        View addWidget = mAppWidgetContainer.findViewById(R.id.keyguard_add_widget);
1240        int insertionIndex = mAppWidgetContainer.indexOfChild(addWidget);
1241        if (insertionIndex < 0) {
1242            insertionIndex = 0; // no add widget page found
1243        } else {
1244            insertionIndex++; // place after add widget
1245        }
1246        return insertionIndex;
1247    }
1248
1249    private void addDefaultStatusWidget(int index) {
1250        LayoutInflater inflater = LayoutInflater.from(mContext);
1251        View statusWidget = inflater.inflate(R.layout.keyguard_status_view, null, true);
1252        mAppWidgetContainer.addWidget(statusWidget, index);
1253    }
1254
1255    private void addWidgetsFromSettings() {
1256        if (mSafeModeEnabled || widgetsDisabled()) {
1257            addDefaultStatusWidget(0);
1258            return;
1259        }
1260
1261        int insertionIndex = getInsertPageIndex();
1262
1263        // Add user-selected widget
1264        final int[] widgets = mLockPatternUtils.getAppWidgets();
1265
1266        if (widgets == null) {
1267            Log.d(TAG, "Problem reading widgets");
1268        } else {
1269            for (int i = widgets.length -1; i >= 0; i--) {
1270                if (widgets[i] == LockPatternUtils.ID_DEFAULT_STATUS_WIDGET) {
1271                    addDefaultStatusWidget(insertionIndex);
1272                } else {
1273                    // We add the widgets from left to right, starting after the first page after
1274                    // the add page. We count down, since the order will be persisted from right
1275                    // to left, starting after camera.
1276                    addWidget(widgets[i], insertionIndex, true);
1277                }
1278            }
1279        }
1280    }
1281
1282    private int allocateIdForDefaultAppWidget() {
1283        int appWidgetId;
1284        Resources res = getContext().getResources();
1285        ComponentName defaultAppWidget = new ComponentName(
1286                res.getString(R.string.widget_default_package_name),
1287                res.getString(R.string.widget_default_class_name));
1288
1289        // Note: we don't support configuring the widget
1290        appWidgetId = mAppWidgetHost.allocateAppWidgetId();
1291
1292        try {
1293            mAppWidgetManager.bindAppWidgetId(appWidgetId, defaultAppWidget);
1294        } catch (IllegalArgumentException e) {
1295            Log.e(TAG, "Error when trying to bind default AppWidget: " + e);
1296            mAppWidgetHost.deleteAppWidgetId(appWidgetId);
1297            appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
1298        }
1299        return appWidgetId;
1300    }
1301
1302    public void checkAppWidgetConsistency() {
1303        // Since this method may bind a widget (which we can't do until boot completed) we
1304        // may have to defer it until after boot complete.
1305        if (!KeyguardUpdateMonitor.getInstance(mContext).hasBootCompleted()) {
1306            mCheckAppWidgetConsistencyOnBootCompleted = true;
1307            return;
1308        }
1309        final int childCount = mAppWidgetContainer.getChildCount();
1310        boolean widgetPageExists = false;
1311        for (int i = 0; i < childCount; i++) {
1312            if (mAppWidgetContainer.isWidgetPage(i)) {
1313                widgetPageExists = true;
1314                break;
1315            }
1316        }
1317        if (!widgetPageExists) {
1318            final int insertPageIndex = getInsertPageIndex();
1319
1320            final boolean userAddedWidgetsEnabled = !widgetsDisabled();
1321
1322            boolean addedDefaultAppWidget = false;
1323
1324            if (!mSafeModeEnabled) {
1325                if (userAddedWidgetsEnabled) {
1326                    int appWidgetId = allocateIdForDefaultAppWidget();
1327                    if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
1328                        addedDefaultAppWidget = addWidget(appWidgetId, insertPageIndex, true);
1329                    }
1330                } else {
1331                    // note: even if widgetsDisabledByDpm() returns true, we still bind/create
1332                    // the default appwidget if possible
1333                    int appWidgetId = mLockPatternUtils.getFallbackAppWidgetId();
1334                    if (appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
1335                        appWidgetId = allocateIdForDefaultAppWidget();
1336                        if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
1337                            mLockPatternUtils.writeFallbackAppWidgetId(appWidgetId);
1338                        }
1339                    }
1340                    if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
1341                        addedDefaultAppWidget = addWidget(appWidgetId, insertPageIndex, false);
1342                        if (!addedDefaultAppWidget) {
1343                            mAppWidgetHost.deleteAppWidgetId(appWidgetId);
1344                            mLockPatternUtils.writeFallbackAppWidgetId(
1345                                    AppWidgetManager.INVALID_APPWIDGET_ID);
1346                        }
1347                    }
1348                }
1349            }
1350
1351            // Use the built-in status/clock view if we can't inflate the default widget
1352            if (!addedDefaultAppWidget) {
1353                addDefaultStatusWidget(insertPageIndex);
1354            }
1355
1356            // trigger DB updates only if user-added widgets are enabled
1357            if (!mSafeModeEnabled && userAddedWidgetsEnabled) {
1358                mAppWidgetContainer.onAddView(
1359                        mAppWidgetContainer.getChildAt(insertPageIndex), insertPageIndex);
1360            }
1361        }
1362    }
1363
1364    Runnable mSwitchPageRunnable = new Runnable() {
1365        @Override
1366        public void run() {
1367           showAppropriateWidgetPage();
1368        }
1369    };
1370
1371    static class SavedState extends BaseSavedState {
1372        int transportState;
1373        int appWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
1374        Rect insets = new Rect();
1375
1376        SavedState(Parcelable superState) {
1377            super(superState);
1378        }
1379
1380        private SavedState(Parcel in) {
1381            super(in);
1382            this.transportState = in.readInt();
1383            this.appWidgetToShow = in.readInt();
1384            this.insets = in.readParcelable(null);
1385        }
1386
1387        @Override
1388        public void writeToParcel(Parcel out, int flags) {
1389            super.writeToParcel(out, flags);
1390            out.writeInt(this.transportState);
1391            out.writeInt(this.appWidgetToShow);
1392            out.writeParcelable(insets, 0);
1393        }
1394
1395        public static final Parcelable.Creator<SavedState> CREATOR
1396                = new Parcelable.Creator<SavedState>() {
1397            public SavedState createFromParcel(Parcel in) {
1398                return new SavedState(in);
1399            }
1400
1401            public SavedState[] newArray(int size) {
1402                return new SavedState[size];
1403            }
1404        };
1405    }
1406
1407    @Override
1408    public Parcelable onSaveInstanceState() {
1409        if (DEBUG) Log.d(TAG, "onSaveInstanceState, tstate=" + mTransportState);
1410        Parcelable superState = super.onSaveInstanceState();
1411        SavedState ss = new SavedState(superState);
1412        // If the transport is showing, force it to show it on restore.
1413        final boolean showing = mTransportControl != null
1414                && mAppWidgetContainer.getWidgetPageIndex(mTransportControl) >= 0;
1415        ss.transportState =  showing ? TRANSPORT_VISIBLE : mTransportState;
1416        ss.appWidgetToShow = mAppWidgetToShow;
1417        ss.insets.set(mInsets);
1418        return ss;
1419    }
1420
1421    @Override
1422    public void onRestoreInstanceState(Parcelable state) {
1423        if (!(state instanceof SavedState)) {
1424            super.onRestoreInstanceState(state);
1425            return;
1426        }
1427        SavedState ss = (SavedState) state;
1428        super.onRestoreInstanceState(ss.getSuperState());
1429        mTransportState = (ss.transportState);
1430        mAppWidgetToShow = ss.appWidgetToShow;
1431        setInsets(ss.insets);
1432        if (DEBUG) Log.d(TAG, "onRestoreInstanceState, transport=" + mTransportState);
1433        post(mSwitchPageRunnable);
1434    }
1435
1436    @Override
1437    protected boolean fitSystemWindows(Rect insets) {
1438        setInsets(insets);
1439        return true;
1440    }
1441
1442    private void setInsets(Rect insets) {
1443        mInsets.set(insets);
1444        if (mSlidingChallengeLayout != null) mSlidingChallengeLayout.setInsets(mInsets);
1445        if (mMultiPaneChallengeLayout != null) mMultiPaneChallengeLayout.setInsets(mInsets);
1446    }
1447
1448    @Override
1449    public void onWindowFocusChanged(boolean hasWindowFocus) {
1450        super.onWindowFocusChanged(hasWindowFocus);
1451        if (DEBUG) Log.d(TAG, "Window is " + (hasWindowFocus ? "focused" : "unfocused"));
1452        if (hasWindowFocus && mShowSecurityWhenReturn) {
1453            SlidingChallengeLayout slider =
1454                (SlidingChallengeLayout) findViewById(R.id.sliding_layout);
1455            if (slider != null) {
1456                slider.setHandleAlpha(1);
1457                slider.showChallenge(true);
1458            }
1459            mShowSecurityWhenReturn = false;
1460        }
1461    }
1462
1463    private void showAppropriateWidgetPage() {
1464        int state = mTransportState;
1465        ensureTransportPresentOrRemoved(state);
1466        int pageToShow = getAppropriateWidgetPage(state);
1467        mAppWidgetContainer.setCurrentPage(pageToShow);
1468    }
1469
1470    /**
1471     * Examines the current state and adds the transport to the widget pager when the state changes.
1472     *
1473     * Showing the initial transport and keeping it around is a bit tricky because the signals
1474     * coming from music players aren't always clear. Here's how the states are handled:
1475     *
1476     * {@link TRANSPORT_GONE} means we have no reason to show the transport - remove it if present.
1477     *
1478     * {@link TRANSPORT_INVISIBLE} means we have potential to show the transport because a music
1479     * player is registered but not currently playing music (or we don't know the state yet). The
1480     * code adds it conditionally on play state.
1481     *
1482     * {@link #TRANSPORT_VISIBLE} means a music player is active and transport should be showing.
1483     *
1484     * Once the transport is showing, we always show it until keyguard is dismissed. This state is
1485     * maintained by onSave/RestoreInstanceState(). This state is cleared in
1486     * {@link KeyguardViewManager#hide} when keyguard is dismissed, which causes the transport to be
1487     * gone when keyguard is restarted until we get an update with the current state.
1488     *
1489     * @param state
1490     */
1491    private void ensureTransportPresentOrRemoved(int state) {
1492        final boolean showing = getWidgetPosition(R.id.keyguard_transport_control) != -1;
1493        final boolean visible = state == TRANSPORT_VISIBLE;
1494        final boolean shouldBeVisible = state == TRANSPORT_INVISIBLE && isMusicPlaying(state);
1495        if (!showing && (visible || shouldBeVisible)) {
1496            if (DEBUGXPORT) Log.v(TAG, "add transport");
1497            // insert to left of camera if it exists, otherwise after right-most widget
1498            int lastWidget = mAppWidgetContainer.getChildCount() - 1;
1499            int position = 0; // handle no widget case
1500            if (lastWidget >= 0) {
1501                position = mAppWidgetContainer.isCameraPage(lastWidget) ?
1502                        lastWidget : lastWidget + 1;
1503            }
1504            mAppWidgetContainer.addWidget(getOrCreateTransportControl(), position);
1505        } else if (showing && state == TRANSPORT_GONE) {
1506            if (DEBUGXPORT) Log.v(TAG, "remove transport");
1507            mAppWidgetContainer.removeWidget(getOrCreateTransportControl());
1508            mTransportControl = null;
1509            KeyguardUpdateMonitor.getInstance(getContext()).dispatchSetBackground(null);
1510        }
1511    }
1512
1513    private CameraWidgetFrame findCameraPage() {
1514        for (int i = mAppWidgetContainer.getChildCount() - 1; i >= 0; i--) {
1515            if (mAppWidgetContainer.isCameraPage(i)) {
1516                return (CameraWidgetFrame) mAppWidgetContainer.getChildAt(i);
1517            }
1518        }
1519        return null;
1520    }
1521
1522    boolean isMusicPage(int pageIndex) {
1523        return pageIndex >= 0 && pageIndex == getWidgetPosition(R.id.keyguard_transport_control);
1524    }
1525
1526    private int getAppropriateWidgetPage(int musicTransportState) {
1527        // assumes at least one widget (besides camera + add)
1528        if (mAppWidgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) {
1529            final int childCount = mAppWidgetContainer.getChildCount();
1530            for (int i = 0; i < childCount; i++) {
1531                if (mAppWidgetContainer.getWidgetPageAt(i).getContentAppWidgetId()
1532                        == mAppWidgetToShow) {
1533                    return i;
1534                }
1535            }
1536            mAppWidgetToShow = AppWidgetManager.INVALID_APPWIDGET_ID;
1537        }
1538        // if music playing, show transport
1539        if (musicTransportState == TRANSPORT_VISIBLE) {
1540            if (DEBUG) Log.d(TAG, "Music playing, show transport");
1541            return mAppWidgetContainer.getWidgetPageIndex(getOrCreateTransportControl());
1542        }
1543
1544        // else show the right-most widget (except for camera)
1545        int rightMost = mAppWidgetContainer.getChildCount() - 1;
1546        if (mAppWidgetContainer.isCameraPage(rightMost)) {
1547            rightMost--;
1548        }
1549        if (DEBUG) Log.d(TAG, "Show right-most page " + rightMost);
1550        return rightMost;
1551    }
1552
1553    private void enableUserSelectorIfNecessary() {
1554        if (!UserManager.supportsMultipleUsers()) {
1555            return; // device doesn't support multi-user mode
1556        }
1557        final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
1558        if (um == null) {
1559            Throwable t = new Throwable();
1560            t.fillInStackTrace();
1561            Log.e(TAG, "user service is null.", t);
1562            return;
1563        }
1564
1565        // if there are multiple users, we need to enable to multi-user switcher
1566        final List<UserInfo> users = um.getUsers(true);
1567        if (users == null) {
1568            Throwable t = new Throwable();
1569            t.fillInStackTrace();
1570            Log.e(TAG, "list of users is null.", t);
1571            return;
1572        }
1573
1574        final View multiUserView = findViewById(R.id.keyguard_user_selector);
1575        if (multiUserView == null) {
1576            Throwable t = new Throwable();
1577            t.fillInStackTrace();
1578            Log.e(TAG, "can't find user_selector in layout.", t);
1579            return;
1580        }
1581
1582        if (users.size() > 1) {
1583            if (multiUserView instanceof KeyguardMultiUserSelectorView) {
1584                mKeyguardMultiUserSelectorView = (KeyguardMultiUserSelectorView) multiUserView;
1585                mKeyguardMultiUserSelectorView.setVisibility(View.VISIBLE);
1586                mKeyguardMultiUserSelectorView.addUsers(users);
1587                UserSwitcherCallback callback = new UserSwitcherCallback() {
1588                    @Override
1589                    public void hideSecurityView(int duration) {
1590                        mSecurityViewContainer.animate().alpha(0).setDuration(duration);
1591                    }
1592
1593                    @Override
1594                    public void showSecurityView() {
1595                        mSecurityViewContainer.setAlpha(1.0f);
1596                    }
1597
1598                    @Override
1599                    public void showUnlockHint() {
1600                        if (mKeyguardSelectorView != null) {
1601                            mKeyguardSelectorView.showUsabilityHint();
1602                        }
1603                    }
1604
1605                    @Override
1606                    public void userActivity() {
1607                        if (mViewMediatorCallback != null) {
1608                            mViewMediatorCallback.userActivity();
1609                        }
1610                    }
1611                };
1612                mKeyguardMultiUserSelectorView.setCallback(callback);
1613            } else {
1614                Throwable t = new Throwable();
1615                t.fillInStackTrace();
1616                if (multiUserView == null) {
1617                    Log.e(TAG, "could not find the user_selector.", t);
1618                } else {
1619                    Log.e(TAG, "user_selector is the wrong type.", t);
1620                }
1621            }
1622        }
1623    }
1624
1625    @Override
1626    public void cleanUp() {
1627        // Make sure we let go of all widgets and their package contexts promptly. If we don't do
1628        // this, and the associated application is uninstalled, it can cause a soft reboot.
1629        int count = mAppWidgetContainer.getChildCount();
1630        for (int i = 0; i < count; i++) {
1631            KeyguardWidgetFrame frame = mAppWidgetContainer.getWidgetPageAt(i);
1632            frame.removeAllViews();
1633        }
1634    }
1635
1636    /**
1637     * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
1638     * some cases where we wish to disable it, notably when the menu button placement or technology
1639     * is prone to false positives.
1640     *
1641     * @return true if the menu key should be enabled
1642     */
1643    private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
1644    private boolean shouldEnableMenuKey() {
1645        final Resources res = getResources();
1646        final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen);
1647        final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
1648        final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
1649        return !configDisabled || isTestHarness || fileOverride;
1650    }
1651
1652    public void goToWidget(int appWidgetId) {
1653        mAppWidgetToShow = appWidgetId;
1654        mSwitchPageRunnable.run();
1655    }
1656
1657    public boolean handleMenuKey() {
1658        // The following enables the MENU key to work for testing automation
1659        if (shouldEnableMenuKey()) {
1660            showNextSecurityScreenOrFinish(false);
1661            return true;
1662        }
1663        return false;
1664    }
1665
1666    public boolean handleBackKey() {
1667        if (mCurrentSecuritySelection == SecurityMode.Account) {
1668            // go back to primary screen and re-disable back
1669            setBackButtonEnabled(false);
1670            showPrimarySecurityScreen(false /*turningOff*/);
1671            return true;
1672        }
1673        if (mCurrentSecuritySelection != SecurityMode.None) {
1674            mCallback.dismiss(false);
1675            return true;
1676        }
1677        return false;
1678    }
1679
1680    /**
1681     *  Dismisses the keyguard by going to the next screen or making it gone.
1682     */
1683    public void dismiss() {
1684        showNextSecurityScreenOrFinish(false);
1685    }
1686
1687    public void showAssistant() {
1688        final Intent intent = ((SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE))
1689          .getAssistIntent(mContext, true, UserHandle.USER_CURRENT);
1690
1691        if (intent == null) return;
1692
1693        final ActivityOptions opts = ActivityOptions.makeCustomAnimation(mContext,
1694                R.anim.keyguard_action_assist_enter, R.anim.keyguard_action_assist_exit,
1695                getHandler(), null);
1696
1697        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
1698
1699        mActivityLauncher.launchActivityWithAnimation(
1700                intent, false, opts.toBundle(), null, null);
1701    }
1702
1703    public void dispatch(MotionEvent event) {
1704        mAppWidgetContainer.handleExternalCameraEvent(event);
1705    }
1706
1707    public void launchCamera() {
1708        mActivityLauncher.launchCamera(getHandler(), null);
1709    }
1710
1711}
1712