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