1/*
2 * Copyright (C) 2011 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.keyguard_obsolete;
18
19import com.android.internal.R;
20import com.android.internal.policy.IFaceLockCallback;
21import com.android.internal.policy.IFaceLockInterface;
22import com.android.internal.widget.LockPatternUtils;
23
24import android.app.admin.DevicePolicyManager;
25import android.content.ComponentName;
26import android.content.Context;
27import android.content.Intent;
28import android.content.ServiceConnection;
29import android.os.Handler;
30import android.os.IBinder;
31import android.os.Looper;
32import android.os.Message;
33import android.os.RemoteException;
34import android.telephony.TelephonyManager;
35import android.util.Log;
36import android.view.View;
37
38public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback {
39
40    private static final boolean DEBUG = false;
41    private static final String TAG = "FULLockscreen";
42
43    private final Context mContext;
44    private final LockPatternUtils mLockPatternUtils;
45    private final KeyguardUpdateMonitor mUpdateMonitor;
46
47    // TODO: is mServiceRunning needed or can we just use mIsRunning or check if mService is null?
48    private boolean mServiceRunning = false;
49    // TODO: now that the code has been restructure to do almost all operations from a handler, this
50    // lock may no longer be necessary.
51    private final Object mServiceRunningLock = new Object();
52    private IFaceLockInterface mService;
53    private boolean mBoundToService = false;
54    private View mFaceUnlockView;
55
56    private Handler mHandler;
57    private final int MSG_SHOW_FACE_UNLOCK_VIEW = 0;
58    private final int MSG_HIDE_FACE_UNLOCK_VIEW = 1;
59    private final int MSG_SERVICE_CONNECTED = 2;
60    private final int MSG_SERVICE_DISCONNECTED = 3;
61    private final int MSG_UNLOCK = 4;
62    private final int MSG_CANCEL = 5;
63    private final int MSG_REPORT_FAILED_ATTEMPT = 6;
64    //private final int MSG_EXPOSE_FALLBACK = 7;
65    private final int MSG_POKE_WAKELOCK = 8;
66
67    // TODO: This was added for the purpose of adhering to what the biometric interface expects
68    // the isRunning() function to return.  However, it is probably not necessary to have both
69    // mRunning and mServiceRunning.  I'd just rather wait to change that logic.
70    private volatile boolean mIsRunning = false;
71
72    // Long enough to stay visible while the service starts
73    // Short enough to not have to wait long for backup if service fails to start or crashes
74    // The service can take a couple of seconds to start on the first try after boot
75    private final int SERVICE_STARTUP_VIEW_TIMEOUT = 3000;
76
77    // So the user has a consistent amount of time when brought to the backup method from Face
78    // Unlock
79    private final int BACKUP_LOCK_TIMEOUT = 5000;
80
81    KeyguardScreenCallback mKeyguardScreenCallback;
82
83    /**
84     * Stores some of the structures that Face Unlock will need to access and creates the handler
85     * will be used to execute messages on the UI thread.
86     */
87    public FaceUnlock(Context context, KeyguardUpdateMonitor updateMonitor,
88            LockPatternUtils lockPatternUtils, KeyguardScreenCallback keyguardScreenCallback) {
89        mContext = context;
90        mUpdateMonitor = updateMonitor;
91        mLockPatternUtils = lockPatternUtils;
92        mKeyguardScreenCallback = keyguardScreenCallback;
93        mHandler = new Handler(this);
94    }
95
96    /**
97     * Stores and displays the view that Face Unlock is allowed to draw within.
98     * TODO: since the layout object will eventually be shared by multiple biometric unlock
99     * methods, we will have to add our other views (background, cancel button) here.
100     */
101    public void initializeView(View biometricUnlockView) {
102        Log.d(TAG, "initializeView()");
103        mFaceUnlockView = biometricUnlockView;
104    }
105
106    /**
107     * Indicates whether Face Unlock is currently running.
108     */
109    public boolean isRunning() {
110        return mIsRunning;
111    }
112
113    /**
114     * Sets the Face Unlock view to visible, hiding it after the specified amount of time.  If
115     * timeoutMillis is 0, no hide is performed.  Called on the UI thread.
116     */
117    public void show(long timeoutMillis) {
118        if (DEBUG) Log.d(TAG, "show()");
119        if (mHandler.getLooper() != Looper.myLooper()) {
120            Log.e(TAG, "show() called off of the UI thread");
121        }
122
123        removeDisplayMessages();
124        if (mFaceUnlockView != null) {
125            mFaceUnlockView.setVisibility(View.VISIBLE);
126        }
127        if (timeoutMillis > 0) {
128            mHandler.sendEmptyMessageDelayed(MSG_HIDE_FACE_UNLOCK_VIEW, timeoutMillis);
129        }
130    }
131
132    /**
133     * Hides the Face Unlock view.
134     */
135    public void hide() {
136        if (DEBUG) Log.d(TAG, "hide()");
137        // Remove messages to prevent a delayed show message from undo-ing the hide
138        removeDisplayMessages();
139        mHandler.sendEmptyMessage(MSG_HIDE_FACE_UNLOCK_VIEW);
140    }
141
142    /**
143     * Binds to the Face Unlock service.  Face Unlock will be started when the bind completes.  The
144     * Face Unlock view is displayed to hide the backup lock while the service is starting up.
145     * Called on the UI thread.
146     */
147    public boolean start() {
148        if (DEBUG) Log.d(TAG, "start()");
149        if (mHandler.getLooper() != Looper.myLooper()) {
150            Log.e(TAG, "start() called off of the UI thread");
151        }
152
153        if (mIsRunning) {
154            Log.w(TAG, "start() called when already running");
155        }
156
157        // Show Face Unlock view, but only for a little bit so lockpattern will become visible if
158        // Face Unlock fails to start or crashes
159        // This must show before bind to guarantee that Face Unlock has a place to display
160        show(SERVICE_STARTUP_VIEW_TIMEOUT);
161        if (!mBoundToService) {
162            Log.d(TAG, "Binding to Face Unlock service");
163            mContext.bindService(new Intent(IFaceLockInterface.class.getName()),
164                    mConnection,
165                    Context.BIND_AUTO_CREATE,
166                    mLockPatternUtils.getCurrentUser());
167            mBoundToService = true;
168        } else {
169            Log.w(TAG, "Attempt to bind to Face Unlock when already bound");
170        }
171
172        mIsRunning = true;
173        return true;
174    }
175
176    /**
177     * Stops Face Unlock and unbinds from the service.  Called on the UI thread.
178     */
179    public boolean stop() {
180        if (DEBUG) Log.d(TAG, "stop()");
181        if (mHandler.getLooper() != Looper.myLooper()) {
182            Log.e(TAG, "stop() called off of the UI thread");
183        }
184
185        boolean mWasRunning = mIsRunning;
186        stopUi();
187
188        if (mBoundToService) {
189            if (mService != null) {
190                try {
191                    mService.unregisterCallback(mFaceUnlockCallback);
192                } catch (RemoteException e) {
193                    // Not much we can do
194                }
195            }
196            Log.d(TAG, "Unbinding from Face Unlock service");
197            mContext.unbindService(mConnection);
198            mBoundToService = false;
199        } else {
200            // This is usually not an error when this happens.  Sometimes we will tell it to
201            // unbind multiple times because it's called from both onWindowFocusChanged and
202            // onDetachedFromWindow.
203            if (DEBUG) Log.d(TAG, "Attempt to unbind from Face Unlock when not bound");
204        }
205        mIsRunning = false;
206        return mWasRunning;
207    }
208
209    /**
210     * Frees up resources used by Face Unlock and stops it if it is still running.
211     */
212    public void cleanUp() {
213        if (DEBUG) Log.d(TAG, "cleanUp()");
214        if (mService != null) {
215            try {
216                mService.unregisterCallback(mFaceUnlockCallback);
217            } catch (RemoteException e) {
218                // Not much we can do
219            }
220            stopUi();
221            mService = null;
222        }
223    }
224
225    /**
226     * Returns the Device Policy Manager quality for Face Unlock, which is BIOMETRIC_WEAK.
227     */
228    public int getQuality() {
229        return DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
230    }
231
232    /**
233     * Handles messages such that everything happens on the UI thread in a deterministic order.
234     * Calls from the Face Unlock service come from binder threads.  Calls from lockscreen typically
235     * come from the UI thread.  This makes sure there are no race conditions between those calls.
236     */
237    @Override
238    public boolean handleMessage(Message msg) {
239        switch (msg.what) {
240            case MSG_SHOW_FACE_UNLOCK_VIEW:
241                handleShowFaceUnlockView();
242                break;
243            case MSG_HIDE_FACE_UNLOCK_VIEW:
244                handleHideFaceUnlockView();
245                break;
246            case MSG_SERVICE_CONNECTED:
247                handleServiceConnected();
248                break;
249            case MSG_SERVICE_DISCONNECTED:
250                handleServiceDisconnected();
251                break;
252            case MSG_UNLOCK:
253                handleUnlock();
254                break;
255            case MSG_CANCEL:
256                handleCancel();
257                break;
258            case MSG_REPORT_FAILED_ATTEMPT:
259                handleReportFailedAttempt();
260                break;
261                //case MSG_EXPOSE_FALLBACK:
262                //handleExposeFallback();
263                //break;
264            case MSG_POKE_WAKELOCK:
265                handlePokeWakelock(msg.arg1);
266                break;
267            default:
268                Log.e(TAG, "Unhandled message");
269                return false;
270        }
271        return true;
272    }
273
274    /**
275     * Sets the Face Unlock view to visible, thus covering the backup lock.
276     */
277    void handleShowFaceUnlockView() {
278        if (DEBUG) Log.d(TAG, "handleShowFaceUnlockView()");
279        if (mFaceUnlockView != null) {
280            mFaceUnlockView.setVisibility(View.VISIBLE);
281        } else {
282            Log.e(TAG, "mFaceUnlockView is null in handleShowFaceUnlockView()");
283        }
284    }
285
286    /**
287     * Sets the Face Unlock view to invisible, thus exposing the backup lock.
288     */
289    void handleHideFaceUnlockView() {
290        if (DEBUG) Log.d(TAG, "handleHideFaceUnlockView()");
291        if (mFaceUnlockView != null) {
292            mFaceUnlockView.setVisibility(View.INVISIBLE);
293        } else {
294            Log.e(TAG, "mFaceUnlockView is null in handleHideFaceUnlockView()");
295        }
296    }
297
298    /**
299     * Tells the service to start its UI via an AIDL interface.  Called when the
300     * onServiceConnected() callback is received.
301     */
302    void handleServiceConnected() {
303        Log.d(TAG, "handleServiceConnected()");
304
305        // It is possible that an unbind has occurred in the time between the bind and when this
306        // function is reached.  If an unbind has already occurred, proceeding on to call startUi()
307        // can result in a fatal error.  Note that the onServiceConnected() callback is
308        // asynchronous, so this possibility would still exist if we executed this directly in
309        // onServiceConnected() rather than using a handler.
310        if (!mBoundToService) {
311            Log.d(TAG, "Dropping startUi() in handleServiceConnected() because no longer bound");
312            return;
313        }
314
315        try {
316            mService.registerCallback(mFaceUnlockCallback);
317        } catch (RemoteException e) {
318            Log.e(TAG, "Caught exception connecting to Face Unlock: " + e.toString());
319            mService = null;
320            mBoundToService = false;
321            mIsRunning = false;
322            return;
323        }
324
325        if (mFaceUnlockView != null) {
326            IBinder windowToken = mFaceUnlockView.getWindowToken();
327            if (windowToken != null) {
328                // When switching between portrait and landscape view while Face Unlock is running,
329                // the screen will eventually go dark unless we poke the wakelock when Face Unlock
330                // is restarted.
331                mKeyguardScreenCallback.pokeWakelock();
332
333                int[] position;
334                position = new int[2];
335                mFaceUnlockView.getLocationInWindow(position);
336                startUi(windowToken, position[0], position[1], mFaceUnlockView.getWidth(),
337                        mFaceUnlockView.getHeight());
338            } else {
339                Log.e(TAG, "windowToken is null in handleServiceConnected()");
340            }
341        }
342    }
343
344    /**
345     * Called when the onServiceDisconnected() callback is received.  This should not happen during
346     * normal operation.  It indicates an error has occurred.
347     */
348    void handleServiceDisconnected() {
349        Log.e(TAG, "handleServiceDisconnected()");
350        // TODO: this lock may no longer be needed now that everything is being called from a
351        // handler
352        synchronized (mServiceRunningLock) {
353            mService = null;
354            mServiceRunning = false;
355        }
356        mBoundToService = false;
357        mIsRunning = false;
358    }
359
360    /**
361     * Stops the Face Unlock service and tells the device to grant access to the user.  Shows the
362     * Face Unlock view to keep the backup lock covered while the device unlocks.
363     */
364    void handleUnlock() {
365        if (DEBUG) Log.d(TAG, "handleUnlock()");
366        removeDisplayMessages();
367        if (mFaceUnlockView != null) {
368            mFaceUnlockView.setVisibility(View.VISIBLE);
369        } else {
370            Log.e(TAG, "mFaceUnlockView is null in handleUnlock()");
371        }
372        stop();
373        mKeyguardScreenCallback.keyguardDone(true);
374        mKeyguardScreenCallback.reportSuccessfulUnlockAttempt();
375    }
376
377    /**
378     * Stops the Face Unlock service and exposes the backup lock.
379     */
380    void handleCancel() {
381        if (DEBUG) Log.d(TAG, "handleCancel()");
382        if (mFaceUnlockView != null) {
383            mFaceUnlockView.setVisibility(View.INVISIBLE);
384        } else {
385            Log.e(TAG, "mFaceUnlockView is null in handleCancel()");
386        }
387        stop();
388        mKeyguardScreenCallback.pokeWakelock(BACKUP_LOCK_TIMEOUT);
389    }
390
391    /**
392     * Increments the number of failed Face Unlock attempts.
393     */
394    void handleReportFailedAttempt() {
395        if (DEBUG) Log.d(TAG, "handleReportFailedAttempt()");
396        mUpdateMonitor.reportFailedBiometricUnlockAttempt();
397    }
398
399    /**
400     * Hides the Face Unlock view to expose the backup lock.  Called when the Face Unlock service UI
401     * is started, indicating there is no need to continue displaying the underlying view because
402     * the service UI is now covering the backup lock.
403     */
404    //void handleExposeFallback() {
405    //    if (DEBUG) Log.d(TAG, "handleExposeFallback()");
406    //    if (mFaceUnlockView != null) {
407    //        mFaceUnlockView.setVisibility(View.INVISIBLE);
408    //    } else {
409    //        Log.e(TAG, "mFaceUnlockView is null in handleExposeFallback()");
410    //    }
411    //}
412
413    /**
414     * Pokes the wakelock to keep the screen alive and active for a specific amount of time.
415     */
416    void handlePokeWakelock(int millis) {
417        mKeyguardScreenCallback.pokeWakelock(millis);
418    }
419
420    /**
421     * Removes show and hide messages from the message queue.  Called to prevent delayed show/hide
422     * messages from undoing a new message.
423     */
424    private void removeDisplayMessages() {
425        mHandler.removeMessages(MSG_SHOW_FACE_UNLOCK_VIEW);
426        mHandler.removeMessages(MSG_HIDE_FACE_UNLOCK_VIEW);
427    }
428
429    /**
430     * Implements service connection methods.
431     */
432    private ServiceConnection mConnection = new ServiceConnection() {
433        /**
434         * Called when the Face Unlock service connects after calling bind().
435         */
436        @Override
437        public void onServiceConnected(ComponentName className, IBinder iservice) {
438            Log.d(TAG, "Connected to Face Unlock service");
439            mService = IFaceLockInterface.Stub.asInterface(iservice);
440            mHandler.sendEmptyMessage(MSG_SERVICE_CONNECTED);
441        }
442
443        /**
444         * Called if the Face Unlock service unexpectedly disconnects.  This indicates an error.
445         */
446        @Override
447        public void onServiceDisconnected(ComponentName className) {
448            Log.e(TAG, "Unexpected disconnect from Face Unlock service");
449            mHandler.sendEmptyMessage(MSG_SERVICE_DISCONNECTED);
450        }
451    };
452
453    /**
454     * Tells the Face Unlock service to start displaying its UI and start processing.
455     */
456    private void startUi(IBinder windowToken, int x, int y, int w, int h) {
457        if (DEBUG) Log.d(TAG, "startUi()");
458        synchronized (mServiceRunningLock) {
459            if (!mServiceRunning) {
460                Log.d(TAG, "Starting Face Unlock");
461                try {
462                    mService.startUi(windowToken, x, y, w, h,
463                            mLockPatternUtils.isBiometricWeakLivelinessEnabled());
464                } catch (RemoteException e) {
465                    Log.e(TAG, "Caught exception starting Face Unlock: " + e.toString());
466                    return;
467                }
468                mServiceRunning = true;
469            } else {
470                Log.w(TAG, "startUi() attempted while running");
471            }
472        }
473    }
474
475    /**
476     * Tells the Face Unlock service to stop displaying its UI and stop processing.
477     */
478    private void stopUi() {
479        if (DEBUG) Log.d(TAG, "stopUi()");
480        // Note that attempting to stop Face Unlock when it's not running is not an issue.
481        // Face Unlock can return, which stops it and then we try to stop it when the
482        // screen is turned off.  That's why we check.
483        synchronized (mServiceRunningLock) {
484            if (mServiceRunning) {
485                Log.d(TAG, "Stopping Face Unlock");
486                try {
487                    mService.stopUi();
488                } catch (RemoteException e) {
489                    Log.e(TAG, "Caught exception stopping Face Unlock: " + e.toString());
490                }
491                mServiceRunning = false;
492            } else {
493                // This is usually not an error when this happens.  Sometimes we will tell it to
494                // stop multiple times because it's called from both onWindowFocusChanged and
495                // onDetachedFromWindow.
496                if (DEBUG) Log.d(TAG, "stopUi() attempted while not running");
497            }
498        }
499    }
500
501    /**
502     * Implements the AIDL biometric unlock service callback interface.
503     */
504    private final IFaceLockCallback mFaceUnlockCallback = new IFaceLockCallback.Stub() {
505        /**
506         * Called when Face Unlock wants to grant access to the user.
507         */
508        @Override
509        public void unlock() {
510            if (DEBUG) Log.d(TAG, "unlock()");
511            mHandler.sendEmptyMessage(MSG_UNLOCK);
512        }
513
514        /**
515         * Called when Face Unlock wants to go to the backup.
516         */
517        @Override
518        public void cancel() {
519            if (DEBUG) Log.d(TAG, "cancel()");
520            mHandler.sendEmptyMessage(MSG_CANCEL);
521        }
522
523        /**
524         * Called when Face Unlock wants to increment the number of failed attempts.
525         */
526        @Override
527        public void reportFailedAttempt() {
528            if (DEBUG) Log.d(TAG, "reportFailedAttempt()");
529            mHandler.sendEmptyMessage(MSG_REPORT_FAILED_ATTEMPT);
530        }
531
532        /**
533         * Called when the Face Unlock service starts displaying the UI, indicating that the backup
534         * unlock can be exposed because the Face Unlock service is now covering the backup with its
535         * UI.
536         **/
537        //@Override
538        //public void exposeFallback() {
539        //    if (DEBUG) Log.d(TAG, "exposeFallback()");
540        //    mHandler.sendEmptyMessage(MSG_EXPOSE_FALLBACK);
541        //}
542
543        /**
544         * Called when Face Unlock wants to keep the screen alive and active for a specific amount
545         * of time.
546         */
547        public void pokeWakelock(int millis) {
548            if (DEBUG) Log.d(TAG, "pokeWakelock() for " + millis + "ms");
549            Message message = mHandler.obtainMessage(MSG_POKE_WAKELOCK, millis, -1);
550            mHandler.sendMessage(message);
551        }
552
553    };
554}
555