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