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