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