LockPatternKeyguardView.java revision cfdd6242eb0c26373b9c57d2ad1ddc126708189e
1/*
2 * Copyright (C) 2007 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.internal.policy.impl;
18
19import com.android.internal.R;
20import com.android.internal.policy.impl.LockPatternKeyguardView.UnlockMode;
21import com.android.internal.policy.IFaceLockCallback;
22import com.android.internal.policy.IFaceLockInterface;
23import com.android.internal.telephony.IccCard;
24import com.android.internal.widget.LockPatternUtils;
25import com.android.internal.widget.LockScreenWidgetCallback;
26import com.android.internal.widget.LockScreenWidgetInterface;
27import com.android.internal.widget.TransportControlView;
28
29import android.accounts.Account;
30import android.accounts.AccountManager;
31import android.accounts.AccountManagerCallback;
32import android.accounts.AccountManagerFuture;
33import android.accounts.AuthenticatorException;
34import android.accounts.OperationCanceledException;
35import android.app.AlertDialog;
36import android.app.admin.DevicePolicyManager;
37import android.content.ComponentName;
38import android.content.Context;
39import android.content.Intent;
40import android.content.res.Configuration;
41import android.content.res.Resources;
42import android.content.ServiceConnection;
43import android.graphics.Bitmap;
44import android.graphics.Canvas;
45import android.graphics.ColorFilter;
46import android.graphics.PixelFormat;
47import android.graphics.drawable.Drawable;
48import android.os.Bundle;
49import android.os.Handler;
50import android.os.Message;
51import android.os.IBinder;
52import android.os.RemoteException;
53import android.os.SystemClock;
54import android.os.SystemProperties;
55import android.telephony.TelephonyManager;
56import android.text.TextUtils;
57import android.util.Log;
58import android.util.Slog;
59import android.view.KeyEvent;
60import android.view.MotionEvent;
61import android.view.View;
62import android.view.WindowManager;
63import android.view.accessibility.AccessibilityManager;
64
65import java.io.IOException;
66
67
68/**
69 * The host view for all of the screens of the pattern unlock screen.  There are
70 * two {@link Mode}s of operation, lock and unlock.  This will show the appropriate
71 * screen, and listen for callbacks via
72 * {@link com.android.internal.policy.impl.KeyguardScreenCallback}
73 * from the current screen.
74 *
75 * This view, in turn, communicates back to
76 * {@link com.android.internal.policy.impl.KeyguardViewManager}
77 * via its {@link com.android.internal.policy.impl.KeyguardViewCallback}, as appropriate.
78 */
79public class LockPatternKeyguardView extends KeyguardViewBase implements Handler.Callback {
80
81    private static final int TRANSPORT_USERACTIVITY_TIMEOUT = 10000;
82
83    static final boolean DEBUG_CONFIGURATION = false;
84
85    // time after launching EmergencyDialer before the screen goes blank.
86    private static final int EMERGENCY_CALL_TIMEOUT = 10000;
87
88    // intent action for launching emergency dialer activity.
89    static final String ACTION_EMERGENCY_DIAL = "com.android.phone.EmergencyDialer.DIAL";
90
91    private static final boolean DEBUG = false;
92    private static final String TAG = "LockPatternKeyguardView";
93
94    private final KeyguardUpdateMonitor mUpdateMonitor;
95    private final KeyguardWindowController mWindowController;
96
97    private View mLockScreen;
98    private View mUnlockScreen;
99
100    private boolean mScreenOn = false;
101    private boolean mEnableFallback = false; // assume no fallback UI until we know better
102
103    private boolean mShowLockBeforeUnlock = false;
104
105    // The following were added to support FaceLock
106    private IFaceLockInterface mFaceLockService;
107    private boolean mBoundToFaceLockService = false;
108    private View mFaceLockAreaView;
109
110    private boolean mFaceLockServiceRunning = false;
111    private final Object mFaceLockServiceRunningLock = new Object();
112
113    private Handler mHandler;
114    private final int MSG_SHOW_FACELOCK_AREA_VIEW = 0;
115    private final int MSG_HIDE_FACELOCK_AREA_VIEW = 1;
116
117    // Long enough to stay black while dialer comes up
118    // Short enough to not be black if the user goes back immediately
119    private final int FACELOCK_VIEW_AREA_EMERGENCY_HIDE_TIMEOUT = 1000;
120
121    /**
122     * The current {@link KeyguardScreen} will use this to communicate back to us.
123     */
124    KeyguardScreenCallback mKeyguardScreenCallback;
125
126
127    private boolean mRequiresSim;
128
129
130    /**
131     * Either a lock screen (an informational keyguard screen), or an unlock
132     * screen (a means for unlocking the device) is shown at any given time.
133     */
134    enum Mode {
135        LockScreen,
136        UnlockScreen
137    }
138
139    /**
140     * The different types screens available for {@link Mode#UnlockScreen}.
141     * @see com.android.internal.policy.impl.LockPatternKeyguardView#getUnlockMode()
142     */
143    enum UnlockMode {
144
145        /**
146         * Unlock by drawing a pattern.
147         */
148        Pattern,
149
150        /**
151         * Unlock by entering a sim pin.
152         */
153        SimPin,
154
155        /**
156         * Unlock by entering a sim puk.
157         */
158        SimPuk,
159
160        /**
161         * Unlock by entering an account's login and password.
162         */
163        Account,
164
165        /**
166         * Unlock by entering a password or PIN
167         */
168        Password,
169
170        /**
171         * Unknown (uninitialized) value
172         */
173        Unknown
174    }
175
176    /**
177     * The current mode.
178     */
179    private Mode mMode = Mode.LockScreen;
180
181    /**
182     * Keeps track of what mode the current unlock screen is (cached from most recent computation in
183     * {@link #getUnlockMode}).
184     */
185    private UnlockMode mUnlockScreenMode = UnlockMode.Unknown;
186
187    private boolean mForgotPattern;
188
189    /**
190     * If true, it means we are in the process of verifying that the user
191     * can get past the lock screen per {@link #verifyUnlock()}
192     */
193    private boolean mIsVerifyUnlockOnly = false;
194
195
196    /**
197     * Used to lookup the state of the lock pattern
198     */
199    private final LockPatternUtils mLockPatternUtils;
200
201    /**
202     * The current configuration.
203     */
204    private Configuration mConfiguration;
205
206    private Runnable mRecreateRunnable = new Runnable() {
207        public void run() {
208            updateScreen(mMode, true);
209        }
210    };
211
212    private LockScreenWidgetCallback mWidgetCallback = new LockScreenWidgetCallback() {
213        public void userActivity(View self) {
214            mKeyguardScreenCallback.pokeWakelock(TRANSPORT_USERACTIVITY_TIMEOUT);
215        }
216
217        public void requestShow(View view) {
218            if (DEBUG) Log.v(TAG, "View " + view + " requested show transports");
219            view.setVisibility(View.VISIBLE);
220
221            // TODO: examine all widgets to derive clock status
222            mUpdateMonitor.reportClockVisible(false);
223        }
224
225        public void requestHide(View view) {
226            if (DEBUG) Log.v(TAG, "View " + view + " requested hide transports");
227            view.setVisibility(View.GONE);
228
229            // TODO: examine all widgets to derive clock status
230            mUpdateMonitor.reportClockVisible(true);
231        }
232    };
233
234    /**
235     * @return Whether we are stuck on the lock screen because the sim is
236     *   missing.
237     */
238    private boolean stuckOnLockScreenBecauseSimMissing() {
239        return mRequiresSim
240                && (!mUpdateMonitor.isDeviceProvisioned())
241                && (mUpdateMonitor.getSimState() == IccCard.State.ABSENT ||
242                    mUpdateMonitor.getSimState() == IccCard.State.PERM_DISABLED);
243    }
244
245    /**
246     * @param context Used to inflate, and create views.
247     * @param updateMonitor Knows the state of the world, and passed along to each
248     *   screen so they can use the knowledge, and also register for callbacks
249     *   on dynamic information.
250     * @param lockPatternUtils Used to look up state of lock pattern.
251     */
252    public LockPatternKeyguardView(
253            Context context,
254            KeyguardUpdateMonitor updateMonitor,
255            LockPatternUtils lockPatternUtils,
256            KeyguardWindowController controller) {
257        super(context);
258
259        mHandler = new Handler(this);
260        mConfiguration = context.getResources().getConfiguration();
261        mEnableFallback = false;
262        mRequiresSim = TextUtils.isEmpty(SystemProperties.get("keyguard.no_require_sim"));
263        mUpdateMonitor = updateMonitor;
264        mLockPatternUtils = lockPatternUtils;
265        mWindowController = controller;
266
267        mKeyguardScreenCallback = new KeyguardScreenCallback() {
268
269            public void goToLockScreen() {
270                mForgotPattern = false;
271                if (mIsVerifyUnlockOnly) {
272                    // navigating away from unlock screen during verify mode means
273                    // we are done and the user failed to authenticate.
274                    mIsVerifyUnlockOnly = false;
275                    getCallback().keyguardDone(false);
276                } else {
277                    updateScreen(Mode.LockScreen, false);
278                }
279            }
280
281            public void goToUnlockScreen() {
282                final IccCard.State simState = mUpdateMonitor.getSimState();
283                if (stuckOnLockScreenBecauseSimMissing()
284                         || (simState == IccCard.State.PUK_REQUIRED
285                             && !mLockPatternUtils.isPukUnlockScreenEnable())){
286                    // stuck on lock screen when sim missing or
287                    // puk'd but puk unlock screen is disabled
288                    return;
289                }
290                if (!isSecure()) {
291                    getCallback().keyguardDone(true);
292                } else {
293                    updateScreen(Mode.UnlockScreen, false);
294                }
295            }
296
297            public void forgotPattern(boolean isForgotten) {
298                if (mEnableFallback) {
299                    mForgotPattern = isForgotten;
300                    updateScreen(Mode.UnlockScreen, false);
301                }
302            }
303
304            public boolean isSecure() {
305                return LockPatternKeyguardView.this.isSecure();
306            }
307
308            public boolean isVerifyUnlockOnly() {
309                return mIsVerifyUnlockOnly;
310            }
311
312            public void recreateMe(Configuration config) {
313                removeCallbacks(mRecreateRunnable);
314                post(mRecreateRunnable);
315            }
316
317            public void takeEmergencyCallAction() {
318                // FaceLock must be stopped if it is running when emergency call is pressed
319                stopAndUnbindFromFaceLock();
320
321                // Delay hiding FaceLock area so unlock doesn't display while dialer is coming up
322                mHandler.sendEmptyMessageDelayed(MSG_HIDE_FACELOCK_AREA_VIEW,
323                        FACELOCK_VIEW_AREA_EMERGENCY_HIDE_TIMEOUT);
324
325                pokeWakelock(EMERGENCY_CALL_TIMEOUT);
326                if (TelephonyManager.getDefault().getCallState()
327                        == TelephonyManager.CALL_STATE_OFFHOOK) {
328                    mLockPatternUtils.resumeCall();
329                } else {
330                    Intent intent = new Intent(ACTION_EMERGENCY_DIAL);
331                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
332                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
333                    getContext().startActivity(intent);
334                }
335            }
336
337            public void pokeWakelock() {
338                getCallback().pokeWakelock();
339            }
340
341            public void pokeWakelock(int millis) {
342                getCallback().pokeWakelock(millis);
343            }
344
345            public void keyguardDone(boolean authenticated) {
346                getCallback().keyguardDone(authenticated);
347            }
348
349            public void keyguardDoneDrawing() {
350                // irrelevant to keyguard screen, they shouldn't be calling this
351            }
352
353            public void reportFailedUnlockAttempt() {
354                mUpdateMonitor.reportFailedAttempt();
355                final int failedAttempts = mUpdateMonitor.getFailedAttempts();
356                if (DEBUG) Log.d(TAG, "reportFailedPatternAttempt: #" + failedAttempts +
357                    " (enableFallback=" + mEnableFallback + ")");
358
359                final boolean usingPattern = mLockPatternUtils.getKeyguardStoredPasswordQuality()
360                        == DevicePolicyManager.PASSWORD_QUALITY_SOMETHING;
361
362                final int failedAttemptsBeforeWipe = mLockPatternUtils.getDevicePolicyManager()
363                        .getMaximumFailedPasswordsForWipe(null);
364
365                final int failedAttemptWarning = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
366                        - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
367
368                final int remainingBeforeWipe = failedAttemptsBeforeWipe > 0 ?
369                        (failedAttemptsBeforeWipe - failedAttempts)
370                        : Integer.MAX_VALUE; // because DPM returns 0 if no restriction
371
372                if (remainingBeforeWipe < LockPatternUtils.FAILED_ATTEMPTS_BEFORE_WIPE_GRACE) {
373                    // If we reach this code, it means the user has installed a DevicePolicyManager
374                    // that requests device wipe after N attempts.  Once we get below the grace
375                    // period, we'll post this dialog every time as a clear warning until the
376                    // bombshell hits and the device is wiped.
377                    if (remainingBeforeWipe > 0) {
378                        showAlmostAtWipeDialog(failedAttempts, remainingBeforeWipe);
379                    } else {
380                        // Too many attempts. The device will be wiped shortly.
381                        Slog.i(TAG, "Too many unlock attempts; device will be wiped!");
382                        showWipeDialog(failedAttempts);
383                    }
384                } else if (usingPattern && mEnableFallback) {
385                    if (failedAttempts == failedAttemptWarning) {
386                        showAlmostAtAccountLoginDialog();
387                    } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
388                        mLockPatternUtils.setPermanentlyLocked(true);
389                        updateScreen(mMode, false);
390                    }
391                } else {
392                    final boolean showTimeout =
393                        (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
394                    if (showTimeout) {
395                        showTimeoutDialog();
396                    }
397                }
398                mLockPatternUtils.reportFailedPasswordAttempt();
399            }
400
401            public boolean doesFallbackUnlockScreenExist() {
402                return mEnableFallback;
403            }
404
405            public void reportSuccessfulUnlockAttempt() {
406                mLockPatternUtils.reportSuccessfulPasswordAttempt();
407            }
408        };
409
410        /**
411         * We'll get key events the current screen doesn't use. see
412         * {@link KeyguardViewBase#onKeyDown(int, android.view.KeyEvent)}
413         */
414        setFocusableInTouchMode(true);
415        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
416
417        updateScreen(getInitialMode(), false);
418        maybeEnableFallback(context);
419    }
420
421    private class AccountAnalyzer implements AccountManagerCallback<Bundle> {
422        private final AccountManager mAccountManager;
423        private final Account[] mAccounts;
424        private int mAccountIndex;
425
426        private AccountAnalyzer(AccountManager accountManager) {
427            mAccountManager = accountManager;
428            mAccounts = accountManager.getAccountsByType("com.google");
429        }
430
431        private void next() {
432            // if we are ready to enable the fallback or if we depleted the list of accounts
433            // then finish and get out
434            if (mEnableFallback || mAccountIndex >= mAccounts.length) {
435                if (mUnlockScreen == null) {
436                    if (DEBUG) Log.w(TAG, "no unlock screen when trying to enable fallback");
437                } else if (mUnlockScreen instanceof PatternUnlockScreen) {
438                    ((PatternUnlockScreen)mUnlockScreen).setEnableFallback(mEnableFallback);
439                }
440                return;
441            }
442
443            // lookup the confirmCredentials intent for the current account
444            mAccountManager.confirmCredentials(mAccounts[mAccountIndex], null, null, this, null);
445        }
446
447        public void start() {
448            mEnableFallback = false;
449            mAccountIndex = 0;
450            next();
451        }
452
453        public void run(AccountManagerFuture<Bundle> future) {
454            try {
455                Bundle result = future.getResult();
456                if (result.getParcelable(AccountManager.KEY_INTENT) != null) {
457                    mEnableFallback = true;
458                }
459            } catch (OperationCanceledException e) {
460                // just skip the account if we are unable to query it
461            } catch (IOException e) {
462                // just skip the account if we are unable to query it
463            } catch (AuthenticatorException e) {
464                // just skip the account if we are unable to query it
465            } finally {
466                mAccountIndex++;
467                next();
468            }
469        }
470    }
471
472    private void maybeEnableFallback(Context context) {
473        // Ask the account manager if we have an account that can be used as a
474        // fallback in case the user forgets his pattern.
475        AccountAnalyzer accountAnalyzer = new AccountAnalyzer(AccountManager.get(context));
476        accountAnalyzer.start();
477    }
478
479
480    // TODO:
481    // This overloaded method was added to workaround a race condition in the framework between
482    // notification for orientation changed, layout() and switching resources.  This code attempts
483    // to avoid drawing the incorrect layout while things are in transition.  The method can just
484    // be removed once the race condition is fixed. See bugs 2262578 and 2292713.
485    @Override
486    protected void dispatchDraw(Canvas canvas) {
487        if (DEBUG) Log.v(TAG, "*** dispatchDraw() time: " + SystemClock.elapsedRealtime());
488        super.dispatchDraw(canvas);
489    }
490
491    @Override
492    public void reset() {
493        mIsVerifyUnlockOnly = false;
494        mForgotPattern = false;
495        post(mRecreateRunnable);
496    }
497
498    @Override
499    public void onScreenTurnedOff() {
500        mScreenOn = false;
501        mForgotPattern = false;
502        if (mMode == Mode.LockScreen) {
503            ((KeyguardScreen) mLockScreen).onPause();
504        } else {
505            ((KeyguardScreen) mUnlockScreen).onPause();
506        }
507
508        // When screen is turned off, need to unbind from FaceLock service if using FaceLock
509        stopAndUnbindFromFaceLock();
510    }
511
512    @Override
513    public void onScreenTurnedOn() {
514        mScreenOn = true;
515        show();
516
517        // When screen is turned on, need to bind to FaceLock service if we are using FaceLock
518        // But only if not dealing with a call
519        if (mUpdateMonitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE) {
520            bindToFaceLock();
521        } else {
522            mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW);
523        }
524    }
525
526    @Override
527    public void show() {
528        if (mMode == Mode.LockScreen) {
529            ((KeyguardScreen) mLockScreen).onResume();
530        } else {
531            ((KeyguardScreen) mUnlockScreen).onResume();
532        }
533
534        if (mLockPatternUtils.usingBiometricWeak()) {
535            mHandler.sendEmptyMessage(MSG_SHOW_FACELOCK_AREA_VIEW);
536        } else {
537            mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW);
538        }
539    }
540
541    private void recreateLockScreen() {
542        if (mLockScreen != null) {
543            ((KeyguardScreen) mLockScreen).onPause();
544            ((KeyguardScreen) mLockScreen).cleanUp();
545            removeView(mLockScreen);
546        }
547
548        mLockScreen = createLockScreen();
549        mLockScreen.setVisibility(View.INVISIBLE);
550        addView(mLockScreen);
551    }
552
553    private void recreateUnlockScreen(UnlockMode unlockMode) {
554        if (mUnlockScreen != null) {
555            ((KeyguardScreen) mUnlockScreen).onPause();
556            ((KeyguardScreen) mUnlockScreen).cleanUp();
557            removeView(mUnlockScreen);
558        }
559
560        mUnlockScreen = createUnlockScreenFor(unlockMode);
561        mUnlockScreen.setVisibility(View.INVISIBLE);
562        addView(mUnlockScreen);
563    }
564
565    @Override
566    protected void onDetachedFromWindow() {
567        removeCallbacks(mRecreateRunnable);
568
569        // When view is hidden, need to unbind from FaceLock service if we are using FaceLock
570        // e.g., when device becomes unlocked
571        stopAndUnbindFromFaceLock();
572
573        super.onDetachedFromWindow();
574    }
575
576    protected void onConfigurationChanged(Configuration newConfig) {
577        Resources resources = getResources();
578        mShowLockBeforeUnlock = resources.getBoolean(R.bool.config_enableLockBeforeUnlockScreen);
579        mConfiguration = newConfig;
580        if (DEBUG_CONFIGURATION) Log.v(TAG, "**** re-creating lock screen since config changed");
581        removeCallbacks(mRecreateRunnable);
582        post(mRecreateRunnable);
583    }
584
585    @Override
586    protected boolean dispatchHoverEvent(MotionEvent event) {
587        // Do not let the screen to get locked while the user is disabled and touch
588        // exploring. A blind user will need significantly more time to find and
589        // interact with the lock screen views.
590        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
591        if (accessibilityManager.isEnabled() && accessibilityManager.isTouchExplorationEnabled()) {
592            getCallback().pokeWakelock();
593        }
594        return super.dispatchHoverEvent(event);
595    }
596
597    @Override
598    public void wakeWhenReadyTq(int keyCode) {
599        if (DEBUG) Log.d(TAG, "onWakeKey");
600        if (keyCode == KeyEvent.KEYCODE_MENU && isSecure() && (mMode == Mode.LockScreen)
601                && (mUpdateMonitor.getSimState() != IccCard.State.PUK_REQUIRED)) {
602            if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU");
603            updateScreen(Mode.UnlockScreen, false);
604            getCallback().pokeWakelock();
605        } else {
606            if (DEBUG) Log.d(TAG, "poking wake lock immediately");
607            getCallback().pokeWakelock();
608        }
609    }
610
611    @Override
612    public void verifyUnlock() {
613        if (!isSecure()) {
614            // non-secure keyguard screens are successfull by default
615            getCallback().keyguardDone(true);
616        } else if (mUnlockScreenMode != UnlockMode.Pattern
617                && mUnlockScreenMode != UnlockMode.Password) {
618            // can only verify unlock when in pattern/password mode
619            getCallback().keyguardDone(false);
620        } else {
621            // otherwise, go to the unlock screen, see if they can verify it
622            mIsVerifyUnlockOnly = true;
623            updateScreen(Mode.UnlockScreen, false);
624        }
625    }
626
627    @Override
628    public void cleanUp() {
629        if (mLockScreen != null) {
630            ((KeyguardScreen) mLockScreen).onPause();
631            ((KeyguardScreen) mLockScreen).cleanUp();
632            this.removeView(mLockScreen);
633            mLockScreen = null;
634        }
635        if (mUnlockScreen != null) {
636            ((KeyguardScreen) mUnlockScreen).onPause();
637            ((KeyguardScreen) mUnlockScreen).cleanUp();
638            this.removeView(mUnlockScreen);
639            mUnlockScreen = null;
640        }
641    }
642
643    private boolean isSecure() {
644        UnlockMode unlockMode = getUnlockMode();
645        boolean secure = false;
646        switch (unlockMode) {
647            case Pattern:
648                secure = mLockPatternUtils.isLockPatternEnabled();
649                break;
650            case SimPin:
651                secure = mUpdateMonitor.getSimState() == IccCard.State.PIN_REQUIRED;
652                break;
653            case SimPuk:
654                secure = mUpdateMonitor.getSimState() == IccCard.State.PUK_REQUIRED;
655                break;
656            case Account:
657                secure = true;
658                break;
659            case Password:
660                secure = mLockPatternUtils.isLockPasswordEnabled();
661                break;
662            default:
663                throw new IllegalStateException("unknown unlock mode " + unlockMode);
664        }
665        return secure;
666    }
667
668    private void updateScreen(Mode mode, boolean force) {
669
670        if (DEBUG_CONFIGURATION) Log.v(TAG, "**** UPDATE SCREEN: mode=" + mode
671                + " last mode=" + mMode + ", force = " + force, new RuntimeException());
672
673        mMode = mode;
674
675        // Re-create the lock screen if necessary
676        if (mode == Mode.LockScreen || mShowLockBeforeUnlock) {
677            if (force || mLockScreen == null) {
678                recreateLockScreen();
679            }
680        }
681
682        // Re-create the unlock screen if necessary. This is primarily required to properly handle
683        // SIM state changes. This typically happens when this method is called by reset()
684        if (mode == Mode.UnlockScreen) {
685            final UnlockMode unlockMode = getUnlockMode();
686            if (force || mUnlockScreen == null || unlockMode != mUnlockScreenMode) {
687                recreateUnlockScreen(unlockMode);
688            }
689        }
690
691        // visibleScreen should never be null
692        final View goneScreen = (mode == Mode.LockScreen) ? mUnlockScreen : mLockScreen;
693        final View visibleScreen = (mode == Mode.LockScreen) ? mLockScreen : mUnlockScreen;
694
695        // do this before changing visibility so focus isn't requested before the input
696        // flag is set
697        mWindowController.setNeedsInput(((KeyguardScreen)visibleScreen).needsInput());
698
699        if (DEBUG_CONFIGURATION) {
700            Log.v(TAG, "Gone=" + goneScreen);
701            Log.v(TAG, "Visible=" + visibleScreen);
702        }
703
704        if (mScreenOn) {
705            if (goneScreen != null && goneScreen.getVisibility() == View.VISIBLE) {
706                ((KeyguardScreen) goneScreen).onPause();
707            }
708            if (visibleScreen.getVisibility() != View.VISIBLE) {
709                ((KeyguardScreen) visibleScreen).onResume();
710            }
711        }
712
713        if (goneScreen != null) {
714            goneScreen.setVisibility(View.GONE);
715        }
716        visibleScreen.setVisibility(View.VISIBLE);
717        requestLayout();
718
719        if (!visibleScreen.requestFocus()) {
720            throw new IllegalStateException("keyguard screen must be able to take "
721                    + "focus when shown " + visibleScreen.getClass().getCanonicalName());
722        }
723    }
724
725    View createLockScreen() {
726        View lockView = new LockScreen(
727                mContext,
728                mConfiguration,
729                mLockPatternUtils,
730                mUpdateMonitor,
731                mKeyguardScreenCallback);
732        initializeTransportControlView(lockView);
733        return lockView;
734    }
735
736    View createUnlockScreenFor(UnlockMode unlockMode) {
737        View unlockView = null;
738
739        if (DEBUG) Log.d(TAG,
740                "createUnlockScreenFor(" + unlockMode + "): mEnableFallback=" + mEnableFallback);
741
742        if (unlockMode == UnlockMode.Pattern) {
743            PatternUnlockScreen view = new PatternUnlockScreen(
744                    mContext,
745                    mConfiguration,
746                    mLockPatternUtils,
747                    mUpdateMonitor,
748                    mKeyguardScreenCallback,
749                    mUpdateMonitor.getFailedAttempts());
750            view.setEnableFallback(mEnableFallback);
751            unlockView = view;
752        } else if (unlockMode == UnlockMode.SimPuk) {
753            unlockView = new SimPukUnlockScreen(
754                    mContext,
755                    mConfiguration,
756                    mUpdateMonitor,
757                    mKeyguardScreenCallback,
758                    mLockPatternUtils);
759        } else if (unlockMode == UnlockMode.SimPin) {
760            unlockView = new SimUnlockScreen(
761                    mContext,
762                    mConfiguration,
763                    mUpdateMonitor,
764                    mKeyguardScreenCallback,
765                    mLockPatternUtils);
766        } else if (unlockMode == UnlockMode.Account) {
767            try {
768                unlockView = new AccountUnlockScreen(
769                        mContext,
770                        mConfiguration,
771                        mUpdateMonitor,
772                        mKeyguardScreenCallback,
773                        mLockPatternUtils);
774            } catch (IllegalStateException e) {
775                Log.i(TAG, "Couldn't instantiate AccountUnlockScreen"
776                      + " (IAccountsService isn't available)");
777                // TODO: Need a more general way to provide a
778                // platform-specific fallback UI here.
779                // For now, if we can't display the account login
780                // unlock UI, just bring back the regular "Pattern" unlock mode.
781
782                // (We do this by simply returning a regular UnlockScreen
783                // here.  This means that the user will still see the
784                // regular pattern unlock UI, regardless of the value of
785                // mUnlockScreenMode or whether or not we're in the
786                // "permanently locked" state.)
787                return createUnlockScreenFor(UnlockMode.Pattern);
788            }
789        } else if (unlockMode == UnlockMode.Password) {
790            unlockView = new PasswordUnlockScreen(
791                    mContext,
792                    mConfiguration,
793                    mLockPatternUtils,
794                    mUpdateMonitor,
795                    mKeyguardScreenCallback);
796        } else {
797            throw new IllegalArgumentException("unknown unlock mode " + unlockMode);
798        }
799        initializeTransportControlView(unlockView);
800        initializeFaceLockAreaView(unlockView); // Only shows view if FaceLock is enabled
801
802        mUnlockScreenMode = unlockMode;
803        return unlockView;
804    }
805
806    private void initializeTransportControlView(View view) {
807        com.android.internal.widget.TransportControlView tcv =
808                (TransportControlView) view.findViewById(R.id.transport);
809        if (tcv == null) {
810            if (DEBUG) Log.w(TAG, "Couldn't find transport control widget");
811        } else {
812            mUpdateMonitor.reportClockVisible(true);
813            tcv.setVisibility(View.GONE); // hide tcv until we get the callback below to show it.
814            tcv.setCallback(mWidgetCallback);
815        }
816    }
817
818    /**
819     * Given the current state of things, what should be the initial mode of
820     * the lock screen (lock or unlock).
821     */
822    private Mode getInitialMode() {
823        final IccCard.State simState = mUpdateMonitor.getSimState();
824        if (stuckOnLockScreenBecauseSimMissing() ||
825                (simState == IccCard.State.PUK_REQUIRED &&
826                        !mLockPatternUtils.isPukUnlockScreenEnable())) {
827            return Mode.LockScreen;
828        } else {
829            if (!isSecure() || mShowLockBeforeUnlock) {
830                return Mode.LockScreen;
831            } else {
832                return Mode.UnlockScreen;
833            }
834        }
835    }
836
837    /**
838     * Given the current state of things, what should the unlock screen be?
839     */
840    private UnlockMode getUnlockMode() {
841        final IccCard.State simState = mUpdateMonitor.getSimState();
842        UnlockMode currentMode;
843        if (simState == IccCard.State.PIN_REQUIRED) {
844            currentMode = UnlockMode.SimPin;
845        } else if (simState == IccCard.State.PUK_REQUIRED) {
846            currentMode = UnlockMode.SimPuk;
847        } else {
848            final int mode = mLockPatternUtils.getKeyguardStoredPasswordQuality();
849            switch (mode) {
850                case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
851                case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
852                case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
853                case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
854                    currentMode = UnlockMode.Password;
855                    break;
856                case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
857                case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
858                    // "forgot pattern" button is only available in the pattern mode...
859                    if (mForgotPattern || mLockPatternUtils.isPermanentlyLocked()) {
860                        currentMode = UnlockMode.Account;
861                    } else {
862                        currentMode = UnlockMode.Pattern;
863                    }
864                    break;
865                default:
866                   throw new IllegalStateException("Unknown unlock mode:" + mode);
867            }
868        }
869        return currentMode;
870    }
871
872    private void showDialog(String title, String message) {
873        final AlertDialog dialog = new AlertDialog.Builder(mContext)
874            .setTitle(title)
875            .setMessage(message)
876            .setNeutralButton(R.string.ok, null)
877            .create();
878        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
879        dialog.show();
880    }
881
882    private void showTimeoutDialog() {
883        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
884        int messageId = R.string.lockscreen_too_many_failed_attempts_dialog_message;
885        if (getUnlockMode() == UnlockMode.Password) {
886            if(mLockPatternUtils.getKeyguardStoredPasswordQuality() ==
887                DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) {
888                messageId = R.string.lockscreen_too_many_failed_pin_attempts_dialog_message;
889            } else {
890                messageId = R.string.lockscreen_too_many_failed_password_attempts_dialog_message;
891            }
892        }
893        String message = mContext.getString(messageId, mUpdateMonitor.getFailedAttempts(),
894                timeoutInSeconds);
895
896        showDialog(null, message);
897    }
898
899    private void showAlmostAtAccountLoginDialog() {
900        final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
901        final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
902                - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
903        String message = mContext.getString(R.string.lockscreen_failed_attempts_almost_glogin,
904                count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds);
905        showDialog(null, message);
906    }
907
908    private void showAlmostAtWipeDialog(int attempts, int remaining) {
909        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
910        String message = mContext.getString(
911                R.string.lockscreen_failed_attempts_almost_at_wipe, attempts, remaining);
912        showDialog(null, message);
913    }
914
915    private void showWipeDialog(int attempts) {
916        String message = mContext.getString(
917                R.string.lockscreen_failed_attempts_now_wiping, attempts);
918        showDialog(null, message);
919    }
920
921    /**
922     * Used to put wallpaper on the background of the lock screen.  Centers it
923     * Horizontally and pins the bottom (assuming that the lock screen is aligned
924     * with the bottom, so the wallpaper should extend above the top into the
925     * status bar).
926     */
927    static private class FastBitmapDrawable extends Drawable {
928        private Bitmap mBitmap;
929        private int mOpacity;
930
931        private FastBitmapDrawable(Bitmap bitmap) {
932            mBitmap = bitmap;
933            mOpacity = mBitmap.hasAlpha() ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
934        }
935
936        @Override
937        public void draw(Canvas canvas) {
938            canvas.drawBitmap(
939                    mBitmap,
940                    (getBounds().width() - mBitmap.getWidth()) / 2,
941                    (getBounds().height() - mBitmap.getHeight()),
942                    null);
943        }
944
945        @Override
946        public int getOpacity() {
947            return mOpacity;
948        }
949
950        @Override
951        public void setAlpha(int alpha) {
952        }
953
954        @Override
955        public void setColorFilter(ColorFilter cf) {
956        }
957
958        @Override
959        public int getIntrinsicWidth() {
960            return mBitmap.getWidth();
961        }
962
963        @Override
964        public int getIntrinsicHeight() {
965            return mBitmap.getHeight();
966        }
967
968        @Override
969        public int getMinimumWidth() {
970            return mBitmap.getWidth();
971        }
972
973        @Override
974        public int getMinimumHeight() {
975            return mBitmap.getHeight();
976        }
977    }
978
979    // Everything below pertains to FaceLock - might want to separate this out
980
981    // Only pattern and pin unlock screens actually have a view for the FaceLock area, so it's not
982    // uncommon for it to not exist.  But if it does exist, we need to make sure it's shown (hiding
983    // the fallback) if FaceLock is enabled, and make sure it's hidden (showing the unlock) if
984    // FaceLock is disabled
985    private void initializeFaceLockAreaView(View view) {
986        mFaceLockAreaView = view.findViewById(R.id.faceLockAreaView);
987        if (mFaceLockAreaView == null) {
988            if (DEBUG) Log.d(TAG, "Layout does not have faceLockAreaView");
989        }
990    }
991
992    // Handles covering or exposing FaceLock area on the client side when FaceLock starts or stops
993    // This needs to be done in a handler because the call could be coming from a callback from the
994    // FaceLock service that is in a thread that can't modify the UI
995    @Override
996    public boolean handleMessage(Message msg) {
997        switch (msg.what) {
998        case MSG_SHOW_FACELOCK_AREA_VIEW:
999            mFaceLockAreaView.setVisibility(View.VISIBLE);
1000            break;
1001        case MSG_HIDE_FACELOCK_AREA_VIEW:
1002            mFaceLockAreaView.setVisibility(View.GONE);
1003            break;
1004        default:
1005            Log.w(TAG, "Unhandled message");
1006            return false;
1007        }
1008        return true;
1009    }
1010
1011    // Binds to FaceLock service, but does not tell it to start
1012    public void bindToFaceLock() {
1013        if (mLockPatternUtils.usingBiometricWeak()) {
1014            if (!mBoundToFaceLockService) {
1015                if (DEBUG) Log.d(TAG, "before bind to FaceLock service");
1016                mContext.bindService(new Intent(IFaceLockInterface.class.getName()),
1017                        mFaceLockConnection,
1018                        Context.BIND_AUTO_CREATE);
1019                if (DEBUG) Log.d(TAG, "after bind to FaceLock service");
1020                mBoundToFaceLockService = true;
1021            } else {
1022                Log.w(TAG, "Attempt to bind to FaceLock when already bound");
1023            }
1024        }
1025    }
1026
1027    // Tells FaceLock to stop and then unbinds from the FaceLock service
1028    public void stopAndUnbindFromFaceLock() {
1029        if (mLockPatternUtils.usingBiometricWeak()) {
1030            stopFaceLock();
1031
1032            if (mBoundToFaceLockService) {
1033                if (DEBUG) Log.d(TAG, "before unbind from FaceLock service");
1034                mContext.unbindService(mFaceLockConnection);
1035                if (DEBUG) Log.d(TAG, "after unbind from FaceLock service");
1036                mBoundToFaceLockService = false;
1037            } else {
1038                // This could probably happen after the session when someone activates FaceLock
1039                // because it wasn't active when the phone was turned on
1040                Log.w(TAG, "Attempt to unbind from FaceLock when not bound");
1041            }
1042        }
1043    }
1044
1045    private ServiceConnection mFaceLockConnection = new ServiceConnection() {
1046        // Completes connection, registers callback and starts FaceLock when service is bound
1047        @Override
1048        public void onServiceConnected(ComponentName className, IBinder iservice) {
1049            mFaceLockService = IFaceLockInterface.Stub.asInterface(iservice);
1050            if (DEBUG) Log.d(TAG, "Connected to FaceLock service");
1051            try {
1052                mFaceLockService.registerCallback(mFaceLockCallback);
1053            } catch (RemoteException e) {
1054                throw new RuntimeException("Remote exception");
1055            }
1056
1057            if (mFaceLockAreaView != null) {
1058                startFaceLock(mFaceLockAreaView.getWindowToken(),
1059                        mFaceLockAreaView.getLeft(), mFaceLockAreaView.getTop(),
1060                        mFaceLockAreaView.getWidth(), mFaceLockAreaView.getHeight());
1061            }
1062        }
1063
1064        // Cleans up if FaceLock service unexpectedly disconnects
1065        @Override
1066        public void onServiceDisconnected(ComponentName className) {
1067            synchronized(mFaceLockServiceRunningLock) {
1068                mFaceLockService = null;
1069                mFaceLockServiceRunning = false;
1070            }
1071            Log.w(TAG, "Unexpected disconnect from FaceLock service");
1072        }
1073    };
1074
1075    // Tells the FaceLock service to start displaying its UI and perform recognition
1076    public void startFaceLock(IBinder windowToken, int x, int y, int h, int w)
1077    {
1078        if (mLockPatternUtils.usingBiometricWeak()) {
1079            synchronized (mFaceLockServiceRunningLock) {
1080                if (!mFaceLockServiceRunning) {
1081                    if (DEBUG) Log.d(TAG, "Starting FaceLock");
1082                    try {
1083                        mFaceLockService.startUi(windowToken, x, y, h, w);
1084                    } catch (RemoteException e) {
1085                        throw new RuntimeException("Remote exception");
1086                    }
1087                    mFaceLockServiceRunning = true;
1088                } else {
1089                    if (DEBUG) Log.w(TAG, "startFaceLock() attempted while running");
1090                }
1091            }
1092        }
1093    }
1094
1095    // Tells the FaceLock service to stop displaying its UI and stop recognition
1096    public void stopFaceLock()
1097    {
1098        if (mLockPatternUtils.usingBiometricWeak()) {
1099            // Note that attempting to stop FaceLock when it's not running is not an issue.
1100            // FaceLock can return, which stops it and then we try to stop it when the
1101            // screen is turned off.  That's why we check.
1102            synchronized (mFaceLockServiceRunningLock) {
1103                if (mFaceLockServiceRunning) {
1104                    try {
1105                        if (DEBUG) Log.d(TAG, "Stopping FaceLock");
1106                        mFaceLockService.stopUi();
1107                    } catch (RemoteException e) {
1108                        throw new RuntimeException("Remote exception");
1109                    }
1110                    mFaceLockServiceRunning = false;
1111                }
1112            }
1113        }
1114    }
1115
1116    // Implements the FaceLock service callback interface defined in AIDL
1117    private final IFaceLockCallback mFaceLockCallback = new IFaceLockCallback.Stub() {
1118
1119        // Stops the FaceLock UI and indicates that the phone should be unlocked
1120        @Override
1121        public void unlock() {
1122            if (DEBUG) Log.d(TAG, "FaceLock unlock()");
1123            mHandler.sendEmptyMessage(MSG_SHOW_FACELOCK_AREA_VIEW); // Keep fallback covered
1124            stopFaceLock();
1125
1126            mKeyguardScreenCallback.keyguardDone(true);
1127            mKeyguardScreenCallback.reportSuccessfulUnlockAttempt();
1128        }
1129
1130        // Stops the FaceLock UI and exposes the backup method without unlocking
1131        // This means either the user has cancelled out or FaceLock failed to recognize them
1132        @Override
1133        public void cancel() {
1134            if (DEBUG) Log.d(TAG, "FaceLock cancel()");
1135            mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW); // Expose fallback
1136            stopFaceLock();
1137        }
1138    };
1139}
1140