LockPatternKeyguardView.java revision d814d4fa0c76a0384f7e32816e1ca36f237911c6
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 if (usingPattern && mEnableFallback) {
425                    if (failedAttempts == failedAttemptWarning) {
426                        showAlmostAtAccountLoginDialog();
427                    } else if (failedAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET) {
428                        mLockPatternUtils.setPermanentlyLocked(true);
429                        updateScreen(mMode, false);
430                    }
431                } else {
432                    final boolean showTimeout =
433                        (failedAttempts % LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT) == 0;
434                    if (showTimeout) {
435                        showTimeoutDialog();
436                    }
437                }
438                mLockPatternUtils.reportFailedPasswordAttempt();
439            }
440
441            public boolean doesFallbackUnlockScreenExist() {
442                return mEnableFallback;
443            }
444
445            public void reportSuccessfulUnlockAttempt() {
446                mFailedFaceUnlockAttempts = 0;
447                mLockPatternUtils.reportSuccessfulPasswordAttempt();
448            }
449        };
450
451        /**
452         * We'll get key events the current screen doesn't use. see
453         * {@link KeyguardViewBase#onKeyDown(int, android.view.KeyEvent)}
454         */
455        setFocusableInTouchMode(true);
456        setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
457
458        updateScreen(getInitialMode(), false);
459        maybeEnableFallback(context);
460    }
461
462    private class AccountAnalyzer implements AccountManagerCallback<Bundle> {
463        private final AccountManager mAccountManager;
464        private final Account[] mAccounts;
465        private int mAccountIndex;
466
467        private AccountAnalyzer(AccountManager accountManager) {
468            mAccountManager = accountManager;
469            mAccounts = accountManager.getAccountsByType("com.google");
470        }
471
472        private void next() {
473            // if we are ready to enable the fallback or if we depleted the list of accounts
474            // then finish and get out
475            if (mEnableFallback || mAccountIndex >= mAccounts.length) {
476                if (mUnlockScreen == null) {
477                    if (DEBUG) Log.w(TAG, "no unlock screen when trying to enable fallback");
478                } else if (mUnlockScreen instanceof PatternUnlockScreen) {
479                    ((PatternUnlockScreen)mUnlockScreen).setEnableFallback(mEnableFallback);
480                }
481                return;
482            }
483
484            // lookup the confirmCredentials intent for the current account
485            mAccountManager.confirmCredentials(mAccounts[mAccountIndex], null, null, this, null);
486        }
487
488        public void start() {
489            mEnableFallback = false;
490            mAccountIndex = 0;
491            next();
492        }
493
494        public void run(AccountManagerFuture<Bundle> future) {
495            try {
496                Bundle result = future.getResult();
497                if (result.getParcelable(AccountManager.KEY_INTENT) != null) {
498                    mEnableFallback = true;
499                }
500            } catch (OperationCanceledException e) {
501                // just skip the account if we are unable to query it
502            } catch (IOException e) {
503                // just skip the account if we are unable to query it
504            } catch (AuthenticatorException e) {
505                // just skip the account if we are unable to query it
506            } finally {
507                mAccountIndex++;
508                next();
509            }
510        }
511    }
512
513    private void maybeEnableFallback(Context context) {
514        // Ask the account manager if we have an account that can be used as a
515        // fallback in case the user forgets his pattern.
516        AccountAnalyzer accountAnalyzer = new AccountAnalyzer(AccountManager.get(context));
517        accountAnalyzer.start();
518    }
519
520
521    // TODO:
522    // This overloaded method was added to workaround a race condition in the framework between
523    // notification for orientation changed, layout() and switching resources.  This code attempts
524    // to avoid drawing the incorrect layout while things are in transition.  The method can just
525    // be removed once the race condition is fixed. See bugs 2262578 and 2292713.
526    @Override
527    protected void dispatchDraw(Canvas canvas) {
528        if (DEBUG) Log.v(TAG, "*** dispatchDraw() time: " + SystemClock.elapsedRealtime());
529        super.dispatchDraw(canvas);
530    }
531
532    @Override
533    public void reset() {
534        mIsVerifyUnlockOnly = false;
535        mForgotPattern = false;
536        post(mRecreateRunnable);
537    }
538
539    @Override
540    public void onScreenTurnedOff() {
541        if (DEBUG) Log.d(TAG, "screen off");
542        mScreenOn = false;
543        mForgotPattern = false;
544        mHasOverlay = mUpdateMonitor.getPhoneState() != TelephonyManager.CALL_STATE_IDLE;
545        if (mMode == Mode.LockScreen) {
546            ((KeyguardScreen) mLockScreen).onPause();
547        } else {
548            ((KeyguardScreen) mUnlockScreen).onPause();
549        }
550
551        saveWidgetState();
552
553        // When screen is turned off, need to unbind from FaceLock service if using FaceLock
554        stopAndUnbindFromFaceLock();
555    }
556
557    /** When screen is turned on and focused, need to bind to FaceLock service if we are using
558     *  FaceLock, but only if we're not dealing with a call
559    */
560    private void activateFaceLockIfAble() {
561        final boolean tooManyFaceUnlockTries =
562                (mFailedFaceUnlockAttempts >= FAILED_FACE_UNLOCK_ATTEMPTS_BEFORE_BACKUP);
563        final int failedBackupAttempts = mUpdateMonitor.getFailedAttempts();
564        final boolean backupIsTimedOut =
565                (failedBackupAttempts >= LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT);
566        if (tooManyFaceUnlockTries) Log.i(TAG, "tooManyFaceUnlockTries: " + tooManyFaceUnlockTries);
567        if (mUpdateMonitor.getPhoneState() == TelephonyManager.CALL_STATE_IDLE
568                && !mHasOverlay
569                && !tooManyFaceUnlockTries
570                && !backupIsTimedOut) {
571            bindToFaceLock();
572            // Show FaceLock area, but only for a little bit so lockpattern will become visible if
573            // FaceLock fails to start or crashes
574            if (mLockPatternUtils.usingBiometricWeak() &&
575                    mLockPatternUtils.isBiometricWeakInstalled()) {
576                showFaceLockAreaWithTimeout(FACELOCK_VIEW_AREA_SERVICE_TIMEOUT);
577            }
578        } else {
579            hideFaceLockArea();
580        }
581    }
582
583    @Override
584    public void onScreenTurnedOn() {
585        if (DEBUG) Log.d(TAG, "screen on");
586        boolean runFaceLock = false;
587        //Make sure to start facelock iff the screen is both on and focused
588        synchronized(mFaceLockStartupLock) {
589            mScreenOn = true;
590            runFaceLock = mWindowFocused;
591        }
592
593        show();
594
595        restoreWidgetState();
596
597        if (runFaceLock) activateFaceLockIfAble();
598    }
599
600    private void saveWidgetState() {
601        if (mTransportControlView != null) {
602            if (DEBUG) Log.v(TAG, "Saving widget state");
603            mSavedState = mTransportControlView.onSaveInstanceState();
604        }
605    }
606
607    private void restoreWidgetState() {
608        if (mTransportControlView != null) {
609            if (DEBUG) Log.v(TAG, "Restoring widget state");
610            if (mSavedState != null) {
611                mTransportControlView.onRestoreInstanceState(mSavedState);
612            }
613        }
614    }
615
616    /** Unbind from facelock if something covers this window (such as an alarm)
617     * bind to facelock if the lockscreen window just came into focus, and the screen is on
618     */
619    @Override
620    public void onWindowFocusChanged (boolean hasWindowFocus) {
621        if (DEBUG) Log.d(TAG, hasWindowFocus ? "focused" : "unfocused");
622        boolean runFaceLock = false;
623        //Make sure to start facelock iff the screen is both on and focused
624        synchronized(mFaceLockStartupLock) {
625            if(mScreenOn && !mWindowFocused) runFaceLock = hasWindowFocus;
626            mWindowFocused = hasWindowFocus;
627        }
628        if(!hasWindowFocus) {
629            mHasOverlay = true;
630            stopAndUnbindFromFaceLock();
631            hideFaceLockArea();
632        } else if (runFaceLock) {
633            activateFaceLockIfAble();
634        }
635    }
636
637    @Override
638    public void show() {
639        if (mMode == Mode.LockScreen) {
640            ((KeyguardScreen) mLockScreen).onResume();
641        } else {
642            ((KeyguardScreen) mUnlockScreen).onResume();
643        }
644
645        if (mLockPatternUtils.usingBiometricWeak() &&
646            mLockPatternUtils.isBiometricWeakInstalled() && !mHasOverlay) {
647            // Note that show() gets called before the screen turns off to set it up for next time
648            // it is turned on.  We don't want to set a timeout on the FaceLock area here because it
649            // may be gone by the time the screen is turned on again.  We set the timout when the
650            // screen turns on instead.
651            showFaceLockArea();
652        } else {
653            hideFaceLockArea();
654        }
655    }
656
657    private void recreateLockScreen() {
658        if (mLockScreen != null) {
659            ((KeyguardScreen) mLockScreen).onPause();
660            ((KeyguardScreen) mLockScreen).cleanUp();
661            removeView(mLockScreen);
662        }
663
664        mLockScreen = createLockScreen();
665        mLockScreen.setVisibility(View.INVISIBLE);
666        addView(mLockScreen);
667    }
668
669    private void recreateUnlockScreen(UnlockMode unlockMode) {
670        if (mUnlockScreen != null) {
671            ((KeyguardScreen) mUnlockScreen).onPause();
672            ((KeyguardScreen) mUnlockScreen).cleanUp();
673            removeView(mUnlockScreen);
674        }
675
676        mUnlockScreen = createUnlockScreenFor(unlockMode);
677        mUnlockScreen.setVisibility(View.INVISIBLE);
678        addView(mUnlockScreen);
679    }
680
681    @Override
682    protected void onDetachedFromWindow() {
683        removeCallbacks(mRecreateRunnable);
684
685        // When view is hidden, need to unbind from FaceLock service if we are using FaceLock
686        // e.g., when device becomes unlocked
687        stopAndUnbindFromFaceLock();
688
689        super.onDetachedFromWindow();
690    }
691
692    protected void onConfigurationChanged(Configuration newConfig) {
693        Resources resources = getResources();
694        mShowLockBeforeUnlock = resources.getBoolean(R.bool.config_enableLockBeforeUnlockScreen);
695        mConfiguration = newConfig;
696        if (DEBUG_CONFIGURATION) Log.v(TAG, "**** re-creating lock screen since config changed");
697        saveWidgetState();
698        removeCallbacks(mRecreateRunnable);
699        post(mRecreateRunnable);
700    }
701
702    //Ignore these events; they are implemented only because they come from the same interface
703    @Override
704    public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel)
705    {}
706    @Override
707    public void onTimeChanged() {}
708    @Override
709    public void onRefreshCarrierInfo(CharSequence plmn, CharSequence spn) {}
710    @Override
711    public void onRingerModeChanged(int state) {}
712    @Override
713    public void onClockVisibilityChanged() {}
714    @Override
715    public void onDeviceProvisioned() {}
716
717    //We need to stop faceunlock when a phonecall comes in
718    @Override
719    public void onPhoneStateChanged(int phoneState) {
720        if (DEBUG) Log.d(TAG, "phone state: " + phoneState);
721        if(phoneState == TelephonyManager.CALL_STATE_RINGING) {
722            mHasOverlay = true;
723            stopAndUnbindFromFaceLock();
724            hideFaceLockArea();
725        }
726    }
727
728    @Override
729    protected boolean dispatchHoverEvent(MotionEvent event) {
730        // Do not let the screen to get locked while the user is disabled and touch
731        // exploring. A blind user will need significantly more time to find and
732        // interact with the lock screen views.
733        AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
734        if (accessibilityManager.isEnabled() && accessibilityManager.isTouchExplorationEnabled()) {
735            getCallback().pokeWakelock();
736        }
737        return super.dispatchHoverEvent(event);
738    }
739
740    @Override
741    public void wakeWhenReadyTq(int keyCode) {
742        if (DEBUG) Log.d(TAG, "onWakeKey");
743        if (keyCode == KeyEvent.KEYCODE_MENU && isSecure() && (mMode == Mode.LockScreen)
744                && (mUpdateMonitor.getSimState() != IccCard.State.PUK_REQUIRED)) {
745            if (DEBUG) Log.d(TAG, "switching screens to unlock screen because wake key was MENU");
746            updateScreen(Mode.UnlockScreen, false);
747            getCallback().pokeWakelock();
748        } else {
749            if (DEBUG) Log.d(TAG, "poking wake lock immediately");
750            getCallback().pokeWakelock();
751        }
752    }
753
754    @Override
755    public void verifyUnlock() {
756        if (!isSecure()) {
757            // non-secure keyguard screens are successfull by default
758            getCallback().keyguardDone(true);
759        } else if (mUnlockScreenMode != UnlockMode.Pattern
760                && mUnlockScreenMode != UnlockMode.Password) {
761            // can only verify unlock when in pattern/password mode
762            getCallback().keyguardDone(false);
763        } else {
764            // otherwise, go to the unlock screen, see if they can verify it
765            mIsVerifyUnlockOnly = true;
766            updateScreen(Mode.UnlockScreen, false);
767        }
768    }
769
770    @Override
771    public void cleanUp() {
772        if (mLockScreen != null) {
773            ((KeyguardScreen) mLockScreen).onPause();
774            ((KeyguardScreen) mLockScreen).cleanUp();
775            this.removeView(mLockScreen);
776            mLockScreen = null;
777        }
778        if (mUnlockScreen != null) {
779            ((KeyguardScreen) mUnlockScreen).onPause();
780            ((KeyguardScreen) mUnlockScreen).cleanUp();
781            this.removeView(mUnlockScreen);
782            mUnlockScreen = null;
783        }
784        mUpdateMonitor.removeCallback(this);
785    }
786
787    private boolean isSecure() {
788        UnlockMode unlockMode = getUnlockMode();
789        boolean secure = false;
790        switch (unlockMode) {
791            case Pattern:
792                secure = mLockPatternUtils.isLockPatternEnabled();
793                break;
794            case SimPin:
795                secure = mUpdateMonitor.getSimState() == IccCard.State.PIN_REQUIRED;
796                break;
797            case SimPuk:
798                secure = mUpdateMonitor.getSimState() == IccCard.State.PUK_REQUIRED;
799                break;
800            case Account:
801                secure = true;
802                break;
803            case Password:
804                secure = mLockPatternUtils.isLockPasswordEnabled();
805                break;
806            default:
807                throw new IllegalStateException("unknown unlock mode " + unlockMode);
808        }
809        return secure;
810    }
811
812    private void updateScreen(Mode mode, boolean force) {
813
814        if (DEBUG_CONFIGURATION) Log.v(TAG, "**** UPDATE SCREEN: mode=" + mode
815                + " last mode=" + mMode + ", force = " + force, new RuntimeException());
816
817        mMode = mode;
818
819        // Re-create the lock screen if necessary
820        if (mode == Mode.LockScreen || mShowLockBeforeUnlock) {
821            if (force || mLockScreen == null) {
822                recreateLockScreen();
823            }
824        }
825
826        // Re-create the unlock screen if necessary. This is primarily required to properly handle
827        // SIM state changes. This typically happens when this method is called by reset()
828        if (mode == Mode.UnlockScreen) {
829            final UnlockMode unlockMode = getUnlockMode();
830            if (force || mUnlockScreen == null || unlockMode != mUnlockScreenMode) {
831                recreateUnlockScreen(unlockMode);
832            }
833        }
834
835        // visibleScreen should never be null
836        final View goneScreen = (mode == Mode.LockScreen) ? mUnlockScreen : mLockScreen;
837        final View visibleScreen = (mode == Mode.LockScreen) ? mLockScreen : mUnlockScreen;
838
839        // do this before changing visibility so focus isn't requested before the input
840        // flag is set
841        mWindowController.setNeedsInput(((KeyguardScreen)visibleScreen).needsInput());
842
843        if (DEBUG_CONFIGURATION) {
844            Log.v(TAG, "Gone=" + goneScreen);
845            Log.v(TAG, "Visible=" + visibleScreen);
846        }
847
848        if (mScreenOn) {
849            if (goneScreen != null && goneScreen.getVisibility() == View.VISIBLE) {
850                ((KeyguardScreen) goneScreen).onPause();
851            }
852            if (visibleScreen.getVisibility() != View.VISIBLE) {
853                ((KeyguardScreen) visibleScreen).onResume();
854            }
855        }
856
857        if (goneScreen != null) {
858            goneScreen.setVisibility(View.GONE);
859        }
860        visibleScreen.setVisibility(View.VISIBLE);
861        requestLayout();
862
863        if (!visibleScreen.requestFocus()) {
864            throw new IllegalStateException("keyguard screen must be able to take "
865                    + "focus when shown " + visibleScreen.getClass().getCanonicalName());
866        }
867    }
868
869    View createLockScreen() {
870        View lockView = new LockScreen(
871                mContext,
872                mConfiguration,
873                mLockPatternUtils,
874                mUpdateMonitor,
875                mKeyguardScreenCallback);
876        initializeTransportControlView(lockView);
877        return lockView;
878    }
879
880    View createUnlockScreenFor(UnlockMode unlockMode) {
881        View unlockView = null;
882
883        if (DEBUG) Log.d(TAG,
884                "createUnlockScreenFor(" + unlockMode + "): mEnableFallback=" + mEnableFallback);
885
886        if (unlockMode == UnlockMode.Pattern) {
887            PatternUnlockScreen view = new PatternUnlockScreen(
888                    mContext,
889                    mConfiguration,
890                    mLockPatternUtils,
891                    mUpdateMonitor,
892                    mKeyguardScreenCallback,
893                    mUpdateMonitor.getFailedAttempts());
894            view.setEnableFallback(mEnableFallback);
895            unlockView = view;
896        } else if (unlockMode == UnlockMode.SimPuk) {
897            unlockView = new SimPukUnlockScreen(
898                    mContext,
899                    mConfiguration,
900                    mUpdateMonitor,
901                    mKeyguardScreenCallback,
902                    mLockPatternUtils);
903        } else if (unlockMode == UnlockMode.SimPin) {
904            unlockView = new SimUnlockScreen(
905                    mContext,
906                    mConfiguration,
907                    mUpdateMonitor,
908                    mKeyguardScreenCallback,
909                    mLockPatternUtils);
910        } else if (unlockMode == UnlockMode.Account) {
911            try {
912                unlockView = new AccountUnlockScreen(
913                        mContext,
914                        mConfiguration,
915                        mUpdateMonitor,
916                        mKeyguardScreenCallback,
917                        mLockPatternUtils);
918            } catch (IllegalStateException e) {
919                Log.i(TAG, "Couldn't instantiate AccountUnlockScreen"
920                      + " (IAccountsService isn't available)");
921                // TODO: Need a more general way to provide a
922                // platform-specific fallback UI here.
923                // For now, if we can't display the account login
924                // unlock UI, just bring back the regular "Pattern" unlock mode.
925
926                // (We do this by simply returning a regular UnlockScreen
927                // here.  This means that the user will still see the
928                // regular pattern unlock UI, regardless of the value of
929                // mUnlockScreenMode or whether or not we're in the
930                // "permanently locked" state.)
931                return createUnlockScreenFor(UnlockMode.Pattern);
932            }
933        } else if (unlockMode == UnlockMode.Password) {
934            unlockView = new PasswordUnlockScreen(
935                    mContext,
936                    mConfiguration,
937                    mLockPatternUtils,
938                    mUpdateMonitor,
939                    mKeyguardScreenCallback);
940        } else {
941            throw new IllegalArgumentException("unknown unlock mode " + unlockMode);
942        }
943        initializeTransportControlView(unlockView);
944        initializeFaceLockAreaView(unlockView); // Only shows view if FaceLock is enabled
945
946        mUnlockScreenMode = unlockMode;
947        return unlockView;
948    }
949
950    private void initializeTransportControlView(View view) {
951        mTransportControlView = (TransportControlView) view.findViewById(R.id.transport);
952        if (mTransportControlView == null) {
953            if (DEBUG) Log.w(TAG, "Couldn't find transport control widget");
954        } else {
955            mUpdateMonitor.reportClockVisible(true);
956            mTransportControlView.setVisibility(View.GONE); // hide until it requests being shown.
957            mTransportControlView.setCallback(mWidgetCallback);
958        }
959    }
960
961    /**
962     * Given the current state of things, what should be the initial mode of
963     * the lock screen (lock or unlock).
964     */
965    private Mode getInitialMode() {
966        final IccCard.State simState = mUpdateMonitor.getSimState();
967        if (stuckOnLockScreenBecauseSimMissing() ||
968                (simState == IccCard.State.PUK_REQUIRED &&
969                        !mLockPatternUtils.isPukUnlockScreenEnable())) {
970            return Mode.LockScreen;
971        } else {
972            if (!isSecure() || mShowLockBeforeUnlock) {
973                return Mode.LockScreen;
974            } else {
975                return Mode.UnlockScreen;
976            }
977        }
978    }
979
980    /**
981     * Given the current state of things, what should the unlock screen be?
982     */
983    private UnlockMode getUnlockMode() {
984        final IccCard.State simState = mUpdateMonitor.getSimState();
985        UnlockMode currentMode;
986        if (simState == IccCard.State.PIN_REQUIRED) {
987            currentMode = UnlockMode.SimPin;
988        } else if (simState == IccCard.State.PUK_REQUIRED) {
989            currentMode = UnlockMode.SimPuk;
990        } else {
991            final int mode = mLockPatternUtils.getKeyguardStoredPasswordQuality();
992            switch (mode) {
993                case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
994                case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
995                case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
996                case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
997                    currentMode = UnlockMode.Password;
998                    break;
999                case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
1000                case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
1001                    // "forgot pattern" button is only available in the pattern mode...
1002                    if (mForgotPattern || mLockPatternUtils.isPermanentlyLocked()) {
1003                        currentMode = UnlockMode.Account;
1004                    } else {
1005                        currentMode = UnlockMode.Pattern;
1006                    }
1007                    break;
1008                default:
1009                   throw new IllegalStateException("Unknown unlock mode:" + mode);
1010            }
1011        }
1012        return currentMode;
1013    }
1014
1015    private void showDialog(String title, String message) {
1016        final AlertDialog dialog = new AlertDialog.Builder(mContext)
1017            .setTitle(title)
1018            .setMessage(message)
1019            .setNeutralButton(R.string.ok, null)
1020            .create();
1021        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
1022        dialog.show();
1023    }
1024
1025    private void showTimeoutDialog() {
1026        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
1027        int messageId = R.string.lockscreen_too_many_failed_attempts_dialog_message;
1028        if (getUnlockMode() == UnlockMode.Password) {
1029            if(mLockPatternUtils.getKeyguardStoredPasswordQuality() ==
1030                DevicePolicyManager.PASSWORD_QUALITY_NUMERIC) {
1031                messageId = R.string.lockscreen_too_many_failed_pin_attempts_dialog_message;
1032            } else {
1033                messageId = R.string.lockscreen_too_many_failed_password_attempts_dialog_message;
1034            }
1035        }
1036        String message = mContext.getString(messageId, mUpdateMonitor.getFailedAttempts(),
1037                timeoutInSeconds);
1038
1039        showDialog(null, message);
1040    }
1041
1042    private void showAlmostAtAccountLoginDialog() {
1043        final int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
1044        final int count = LockPatternUtils.FAILED_ATTEMPTS_BEFORE_RESET
1045                - LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT;
1046        String message = mContext.getString(R.string.lockscreen_failed_attempts_almost_glogin,
1047                count, LockPatternUtils.FAILED_ATTEMPTS_BEFORE_TIMEOUT, timeoutInSeconds);
1048        showDialog(null, message);
1049    }
1050
1051    private void showAlmostAtWipeDialog(int attempts, int remaining) {
1052        int timeoutInSeconds = (int) LockPatternUtils.FAILED_ATTEMPT_TIMEOUT_MS / 1000;
1053        String message = mContext.getString(
1054                R.string.lockscreen_failed_attempts_almost_at_wipe, attempts, remaining);
1055        showDialog(null, message);
1056    }
1057
1058    private void showWipeDialog(int attempts) {
1059        String message = mContext.getString(
1060                R.string.lockscreen_failed_attempts_now_wiping, attempts);
1061        showDialog(null, message);
1062    }
1063
1064    /**
1065     * Used to put wallpaper on the background of the lock screen.  Centers it
1066     * Horizontally and pins the bottom (assuming that the lock screen is aligned
1067     * with the bottom, so the wallpaper should extend above the top into the
1068     * status bar).
1069     */
1070    static private class FastBitmapDrawable extends Drawable {
1071        private Bitmap mBitmap;
1072        private int mOpacity;
1073
1074        private FastBitmapDrawable(Bitmap bitmap) {
1075            mBitmap = bitmap;
1076            mOpacity = mBitmap.hasAlpha() ? PixelFormat.TRANSLUCENT : PixelFormat.OPAQUE;
1077        }
1078
1079        @Override
1080        public void draw(Canvas canvas) {
1081            canvas.drawBitmap(
1082                    mBitmap,
1083                    (getBounds().width() - mBitmap.getWidth()) / 2,
1084                    (getBounds().height() - mBitmap.getHeight()),
1085                    null);
1086        }
1087
1088        @Override
1089        public int getOpacity() {
1090            return mOpacity;
1091        }
1092
1093        @Override
1094        public void setAlpha(int alpha) {
1095        }
1096
1097        @Override
1098        public void setColorFilter(ColorFilter cf) {
1099        }
1100
1101        @Override
1102        public int getIntrinsicWidth() {
1103            return mBitmap.getWidth();
1104        }
1105
1106        @Override
1107        public int getIntrinsicHeight() {
1108            return mBitmap.getHeight();
1109        }
1110
1111        @Override
1112        public int getMinimumWidth() {
1113            return mBitmap.getWidth();
1114        }
1115
1116        @Override
1117        public int getMinimumHeight() {
1118            return mBitmap.getHeight();
1119        }
1120    }
1121
1122    // Everything below pertains to FaceLock - might want to separate this out
1123
1124    // Only pattern and pin unlock screens actually have a view for the FaceLock area, so it's not
1125    // uncommon for it to not exist.  But if it does exist, we need to make sure it's shown (hiding
1126    // the fallback) if FaceLock is enabled, and make sure it's hidden (showing the unlock) if
1127    // FaceLock is disabled
1128    private void initializeFaceLockAreaView(View view) {
1129        mFaceLockAreaView = view.findViewById(R.id.faceLockAreaView);
1130        if (mFaceLockAreaView == null) {
1131            if (DEBUG) Log.d(TAG, "Layout does not have faceLockAreaView");
1132        }
1133    }
1134
1135    // Handles covering or exposing FaceLock area on the client side when FaceLock starts or stops
1136    // This needs to be done in a handler because the call could be coming from a callback from the
1137    // FaceLock service that is in a thread that can't modify the UI
1138    @Override
1139    public boolean handleMessage(Message msg) {
1140        switch (msg.what) {
1141        case MSG_SHOW_FACELOCK_AREA_VIEW:
1142            if (mFaceLockAreaView != null) {
1143                mFaceLockAreaView.setVisibility(View.VISIBLE);
1144            }
1145            break;
1146        case MSG_HIDE_FACELOCK_AREA_VIEW:
1147            if (mFaceLockAreaView != null) {
1148                mFaceLockAreaView.setVisibility(View.GONE);
1149            }
1150            break;
1151        default:
1152            Log.w(TAG, "Unhandled message");
1153            return false;
1154        }
1155        return true;
1156    }
1157
1158    // Removes show and hide messages from the message queue
1159    private void removeFaceLockAreaDisplayMessages() {
1160        mHandler.removeMessages(MSG_SHOW_FACELOCK_AREA_VIEW);
1161        mHandler.removeMessages(MSG_HIDE_FACELOCK_AREA_VIEW);
1162    }
1163
1164    // Shows the FaceLock area immediately
1165    private void showFaceLockArea() {
1166        // Remove messages to prevent a delayed hide message from undo-ing the show
1167        removeFaceLockAreaDisplayMessages();
1168        mHandler.sendEmptyMessage(MSG_SHOW_FACELOCK_AREA_VIEW);
1169    }
1170
1171    // Hides the FaceLock area immediately
1172    private void hideFaceLockArea() {
1173        // Remove messages to prevent a delayed show message from undo-ing the hide
1174        removeFaceLockAreaDisplayMessages();
1175        mHandler.sendEmptyMessage(MSG_HIDE_FACELOCK_AREA_VIEW);
1176    }
1177
1178    // Shows the FaceLock area for a period of time
1179    private void showFaceLockAreaWithTimeout(long timeoutMillis) {
1180        showFaceLockArea();
1181        mHandler.sendEmptyMessageDelayed(MSG_HIDE_FACELOCK_AREA_VIEW, timeoutMillis);
1182    }
1183
1184    // Binds to FaceLock service, but does not tell it to start
1185    public void bindToFaceLock() {
1186        if (mLockPatternUtils.usingBiometricWeak() &&
1187                mLockPatternUtils.isBiometricWeakInstalled()) {
1188            if (!mBoundToFaceLockService) {
1189                if (DEBUG) Log.d(TAG, "before bind to FaceLock service");
1190                mContext.bindService(new Intent(IFaceLockInterface.class.getName()),
1191                        mFaceLockConnection,
1192                        Context.BIND_AUTO_CREATE);
1193                if (DEBUG) Log.d(TAG, "after bind to FaceLock service");
1194                mBoundToFaceLockService = true;
1195            } else {
1196                Log.w(TAG, "Attempt to bind to FaceLock when already bound");
1197            }
1198        }
1199    }
1200
1201    // Tells FaceLock to stop and then unbinds from the FaceLock service
1202    public void stopAndUnbindFromFaceLock() {
1203        if (mLockPatternUtils.usingBiometricWeak() &&
1204                mLockPatternUtils.isBiometricWeakInstalled()) {
1205            stopFaceLock();
1206
1207            if (mBoundToFaceLockService) {
1208                if (DEBUG) Log.d(TAG, "before unbind from FaceLock service");
1209                mContext.unbindService(mFaceLockConnection);
1210                if (DEBUG) Log.d(TAG, "after unbind from FaceLock service");
1211                mBoundToFaceLockService = false;
1212            } else {
1213                // This could probably happen after the session when someone activates FaceLock
1214                // because it wasn't active when the phone was turned on
1215                Log.w(TAG, "Attempt to unbind from FaceLock when not bound");
1216            }
1217        }
1218    }
1219
1220    private ServiceConnection mFaceLockConnection = new ServiceConnection() {
1221        // Completes connection, registers callback and starts FaceLock when service is bound
1222        @Override
1223        public void onServiceConnected(ComponentName className, IBinder iservice) {
1224            mFaceLockService = IFaceLockInterface.Stub.asInterface(iservice);
1225            if (DEBUG) Log.d(TAG, "Connected to FaceLock service");
1226            try {
1227                mFaceLockService.registerCallback(mFaceLockCallback);
1228            } catch (RemoteException e) {
1229                Log.e(TAG, "Caught exception connecting to FaceLock: " + e.toString());
1230                mFaceLockService = null;
1231                mBoundToFaceLockService = false;
1232                return;
1233            }
1234
1235            if (mFaceLockAreaView != null) {
1236                startFaceLock(mFaceLockAreaView.getWindowToken(),
1237                        mFaceLockAreaView.getLeft(), mFaceLockAreaView.getTop(),
1238                        mFaceLockAreaView.getWidth(), mFaceLockAreaView.getHeight());
1239            }
1240        }
1241
1242        // Cleans up if FaceLock service unexpectedly disconnects
1243        @Override
1244        public void onServiceDisconnected(ComponentName className) {
1245            synchronized(mFaceLockServiceRunningLock) {
1246                mFaceLockService = null;
1247                mFaceLockServiceRunning = false;
1248            }
1249            mBoundToFaceLockService = false;
1250            Log.w(TAG, "Unexpected disconnect from FaceLock service");
1251        }
1252    };
1253
1254    // Tells the FaceLock service to start displaying its UI and perform recognition
1255    public void startFaceLock(IBinder windowToken, int x, int y, int h, int w)
1256    {
1257        if (mLockPatternUtils.usingBiometricWeak() &&
1258                mLockPatternUtils.isBiometricWeakInstalled()) {
1259            synchronized (mFaceLockServiceRunningLock) {
1260                if (!mFaceLockServiceRunning) {
1261                    if (DEBUG) Log.d(TAG, "Starting FaceLock");
1262                    try {
1263                        mFaceLockService.startUi(windowToken, x, y, h, w);
1264                    } catch (RemoteException e) {
1265                        Log.e(TAG, "Caught exception starting FaceLock: " + e.toString());
1266                        return;
1267                    }
1268                    mFaceLockServiceRunning = true;
1269                } else {
1270                    if (DEBUG) Log.w(TAG, "startFaceLock() attempted while running");
1271                }
1272            }
1273        }
1274    }
1275
1276    // Tells the FaceLock service to stop displaying its UI and stop recognition
1277    public void stopFaceLock()
1278    {
1279        if (mLockPatternUtils.usingBiometricWeak() &&
1280                mLockPatternUtils.isBiometricWeakInstalled()) {
1281            // Note that attempting to stop FaceLock when it's not running is not an issue.
1282            // FaceLock can return, which stops it and then we try to stop it when the
1283            // screen is turned off.  That's why we check.
1284            synchronized (mFaceLockServiceRunningLock) {
1285                if (mFaceLockServiceRunning) {
1286                    try {
1287                        if (DEBUG) Log.d(TAG, "Stopping FaceLock");
1288                        mFaceLockService.stopUi();
1289                    } catch (RemoteException e) {
1290                        Log.e(TAG, "Caught exception stopping FaceLock: " + e.toString());
1291                    }
1292                    mFaceLockServiceRunning = false;
1293                }
1294            }
1295        }
1296    }
1297
1298    // Implements the FaceLock service callback interface defined in AIDL
1299    private final IFaceLockCallback mFaceLockCallback = new IFaceLockCallback.Stub() {
1300
1301        // Stops the FaceLock UI and indicates that the phone should be unlocked
1302        @Override
1303        public void unlock() {
1304            if (DEBUG) Log.d(TAG, "FaceLock unlock()");
1305            showFaceLockArea(); // Keep fallback covered
1306            stopFaceLock();
1307
1308            mKeyguardScreenCallback.keyguardDone(true);
1309            mKeyguardScreenCallback.reportSuccessfulUnlockAttempt();
1310        }
1311
1312        // Stops the FaceLock UI and exposes the backup method without unlocking
1313        // This means the user has cancelled out
1314        @Override
1315        public void cancel() {
1316            if (DEBUG) Log.d(TAG, "FaceLock cancel()");
1317            hideFaceLockArea(); // Expose fallback
1318            stopFaceLock();
1319            mKeyguardScreenCallback.pokeWakelock(BACKUP_LOCK_TIMEOUT);
1320        }
1321
1322        // Stops the FaceLock UI and exposes the backup method without unlocking
1323        // This means FaceLock failed to recognize them
1324        @Override
1325        public void reportFailedAttempt() {
1326            if (DEBUG) Log.d(TAG, "FaceLock reportFailedAttempt()");
1327            mFailedFaceUnlockAttempts++;
1328            hideFaceLockArea(); // Expose fallback
1329            stopFaceLock();
1330            mKeyguardScreenCallback.pokeWakelock(BACKUP_LOCK_TIMEOUT);
1331        }
1332
1333        // Allows the Face Unlock service to poke the wake lock to keep the lockscreen alive
1334        @Override
1335        public void pokeWakelock() {
1336            if (DEBUG) Log.d(TAG, "FaceLock pokeWakelock()");
1337            mKeyguardScreenCallback.pokeWakelock();
1338        }
1339    };
1340}
1341