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