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