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