FaceUnlock.java revision 5cf17879a31b7b78c09ec50b727f921840dcf783
1/*
2 * Copyright (C) 2012 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.keyguard;
18
19import com.android.internal.policy.IFaceLockCallback;
20import com.android.internal.policy.IFaceLockInterface;
21import com.android.internal.widget.LockPatternUtils;
22
23import android.app.admin.DevicePolicyManager;
24import android.content.ComponentName;
25import android.content.Context;
26import android.content.Intent;
27import android.content.ServiceConnection;
28import android.os.Handler;
29import android.os.IBinder;
30import android.os.Looper;
31import android.os.Message;
32import android.os.PowerManager;
33import android.os.RemoteException;
34import android.os.UserHandle;
35import android.util.Log;
36import android.view.View;
37
38public class FaceUnlock implements BiometricSensorUnlock, Handler.Callback {
39
40    private static final boolean DEBUG = KeyguardConstants.DEBUG;
41    private static final String TAG = "FULLockscreen";
42    private static final String FACE_LOCK_PACKAGE = "com.android.facelock";
43
44    private final Context mContext;
45    private final LockPatternUtils mLockPatternUtils;
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_SERVICE_CONNECTED = 0;
58    private final int MSG_SERVICE_DISCONNECTED = 1;
59    private final int MSG_UNLOCK = 2;
60    private final int MSG_CANCEL = 3;
61    private final int MSG_REPORT_FAILED_ATTEMPT = 4;
62    private final int MSG_POKE_WAKELOCK = 5;
63
64    // TODO: This was added for the purpose of adhering to what the biometric interface expects
65    // the isRunning() function to return.  However, it is probably not necessary to have both
66    // mRunning and mServiceRunning.  I'd just rather wait to change that logic.
67    private volatile boolean mIsRunning = false;
68
69    // So the user has a consistent amount of time when brought to the backup method from Face
70    // Unlock
71    private final int BACKUP_LOCK_TIMEOUT = 5000;
72
73    KeyguardSecurityCallback mKeyguardScreenCallback;
74
75    /**
76     * Stores some of the structures that Face Unlock will need to access and creates the handler
77     * will be used to execute messages on the UI thread.
78     */
79    public FaceUnlock(Context context) {
80        mContext = context;
81        mLockPatternUtils = new LockPatternUtils(context);
82        mHandler = new Handler(this);
83    }
84
85    public void setKeyguardCallback(KeyguardSecurityCallback keyguardScreenCallback) {
86        mKeyguardScreenCallback = keyguardScreenCallback;
87    }
88
89    /**
90     * Stores and displays the view that Face Unlock is allowed to draw within.
91     * TODO: since the layout object will eventually be shared by multiple biometric unlock
92     * methods, we will have to add our other views (background, cancel button) here.
93     */
94    public void initializeView(View biometricUnlockView) {
95        Log.d(TAG, "initializeView()");
96        mFaceUnlockView = biometricUnlockView;
97    }
98
99    /**
100     * Indicates whether Face Unlock is currently running.
101     */
102    public boolean isRunning() {
103        return mIsRunning;
104    }
105
106    /**
107     * Dismisses face unlock and goes to the backup lock
108     */
109    public void stopAndShowBackup() {
110        if (DEBUG) Log.d(TAG, "stopAndShowBackup()");
111        mHandler.sendEmptyMessage(MSG_CANCEL);
112    }
113
114    /**
115     * Binds to the Face Unlock service.  Face Unlock will be started when the bind completes.  The
116     * Face Unlock view is displayed to hide the backup lock while the service is starting up.
117     * Called on the UI thread.
118     */
119    public boolean start() {
120        if (DEBUG) Log.d(TAG, "start()");
121        if (mHandler.getLooper() != Looper.myLooper()) {
122            Log.e(TAG, "start() called off of the UI thread");
123        }
124
125        if (mIsRunning) {
126            Log.w(TAG, "start() called when already running");
127        }
128
129        if (!mBoundToService) {
130            Log.d(TAG, "Binding to Face Unlock service for user="
131                    + mLockPatternUtils.getCurrentUser());
132            mContext.bindServiceAsUser(
133                    new Intent(IFaceLockInterface.class.getName()).setPackage(FACE_LOCK_PACKAGE),
134                    mConnection,
135                    Context.BIND_AUTO_CREATE,
136                    new UserHandle(mLockPatternUtils.getCurrentUser()));
137            mBoundToService = true;
138        } else {
139            Log.w(TAG, "Attempt to bind to Face Unlock when already bound");
140        }
141
142        mIsRunning = true;
143        return true;
144    }
145
146    /**
147     * Stops Face Unlock and unbinds from the service.  Called on the UI thread.
148     */
149    public boolean stop() {
150        if (DEBUG) Log.d(TAG, "stop()");
151        if (mHandler.getLooper() != Looper.myLooper()) {
152            Log.e(TAG, "stop() called from non-UI thread");
153        }
154
155        // Clearing any old service connected messages.
156        mHandler.removeMessages(MSG_SERVICE_CONNECTED);
157
158        boolean mWasRunning = mIsRunning;
159
160        stopUi();
161
162        if (mBoundToService) {
163            if (mService != null) {
164                try {
165                    mService.unregisterCallback(mFaceUnlockCallback);
166                } catch (RemoteException e) {
167                    // Not much we can do
168                }
169            }
170            Log.d(TAG, "Unbinding from Face Unlock service");
171            mContext.unbindService(mConnection);
172            mBoundToService = false;
173        } else {
174            // This is usually not an error when this happens.  Sometimes we will tell it to
175            // unbind multiple times because it's called from both onWindowFocusChanged and
176            // onDetachedFromWindow.
177            if (DEBUG) Log.d(TAG, "Attempt to unbind from Face Unlock when not bound");
178        }
179        mIsRunning = false;
180        return mWasRunning;
181    }
182
183    /**
184     * Frees up resources used by Face Unlock and stops it if it is still running.
185     */
186    public void cleanUp() {
187        if (DEBUG) Log.d(TAG, "cleanUp()");
188        if (mService != null) {
189            try {
190                mService.unregisterCallback(mFaceUnlockCallback);
191            } catch (RemoteException e) {
192                // Not much we can do
193            }
194            stopUi();
195            mService = null;
196        }
197    }
198
199    /**
200     * Returns the Device Policy Manager quality for Face Unlock, which is BIOMETRIC_WEAK.
201     */
202    public int getQuality() {
203        return DevicePolicyManager.PASSWORD_QUALITY_BIOMETRIC_WEAK;
204    }
205
206    /**
207     * Handles messages such that everything happens on the UI thread in a deterministic order.
208     * Calls from the Face Unlock service come from binder threads.  Calls from lockscreen typically
209     * come from the UI thread.  This makes sure there are no race conditions between those calls.
210     */
211    public boolean handleMessage(Message msg) {
212        switch (msg.what) {
213            case MSG_SERVICE_CONNECTED:
214                handleServiceConnected();
215                break;
216            case MSG_SERVICE_DISCONNECTED:
217                handleServiceDisconnected();
218                break;
219            case MSG_UNLOCK:
220                handleUnlock(msg.arg1);
221                break;
222            case MSG_CANCEL:
223                handleCancel();
224                break;
225            case MSG_REPORT_FAILED_ATTEMPT:
226                handleReportFailedAttempt();
227                break;
228            case MSG_POKE_WAKELOCK:
229                handlePokeWakelock(msg.arg1);
230                break;
231            default:
232                Log.e(TAG, "Unhandled message");
233                return false;
234        }
235        return true;
236    }
237
238    /**
239     * Tells the service to start its UI via an AIDL interface.  Called when the
240     * onServiceConnected() callback is received.
241     */
242    void handleServiceConnected() {
243        Log.d(TAG, "handleServiceConnected()");
244
245        // It is possible that an unbind has occurred in the time between the bind and when this
246        // function is reached.  If an unbind has already occurred, proceeding on to call startUi()
247        // can result in a fatal error.  Note that the onServiceConnected() callback is
248        // asynchronous, so this possibility would still exist if we executed this directly in
249        // onServiceConnected() rather than using a handler.
250        if (!mBoundToService) {
251            Log.d(TAG, "Dropping startUi() in handleServiceConnected() because no longer bound");
252            return;
253        }
254
255        try {
256            mService.registerCallback(mFaceUnlockCallback);
257        } catch (RemoteException e) {
258            Log.e(TAG, "Caught exception connecting to Face Unlock: " + e.toString());
259            mService = null;
260            mBoundToService = false;
261            mIsRunning = false;
262            return;
263        }
264
265        if (mFaceUnlockView != null) {
266            IBinder windowToken = mFaceUnlockView.getWindowToken();
267            if (windowToken != null) {
268                // When switching between portrait and landscape view while Face Unlock is running,
269                // the screen will eventually go dark unless we poke the wakelock when Face Unlock
270                // is restarted.
271                mKeyguardScreenCallback.userActivity(0);
272
273                int[] position;
274                position = new int[2];
275                mFaceUnlockView.getLocationInWindow(position);
276                startUi(windowToken, position[0], position[1], mFaceUnlockView.getWidth(),
277                        mFaceUnlockView.getHeight());
278            } else {
279                Log.e(TAG, "windowToken is null in handleServiceConnected()");
280            }
281        }
282    }
283
284    /**
285     * Called when the onServiceDisconnected() callback is received.  This should not happen during
286     * normal operation.  It indicates an error has occurred.
287     */
288    void handleServiceDisconnected() {
289        Log.e(TAG, "handleServiceDisconnected()");
290        // TODO: this lock may no longer be needed now that everything is being called from a
291        // handler
292        synchronized (mServiceRunningLock) {
293            mService = null;
294            mServiceRunning = false;
295        }
296        mBoundToService = false;
297        mIsRunning = false;
298    }
299
300    /**
301     * Stops the Face Unlock service and tells the device to grant access to the user.
302     */
303    void handleUnlock(int authenticatedUserId) {
304        if (DEBUG) Log.d(TAG, "handleUnlock()");
305        stop();
306        int currentUserId = mLockPatternUtils.getCurrentUser();
307        if (authenticatedUserId == currentUserId) {
308            if (DEBUG) Log.d(TAG, "Unlocking for user " + authenticatedUserId);
309            mKeyguardScreenCallback.reportUnlockAttempt(true);
310            mKeyguardScreenCallback.dismiss(true);
311        } else {
312            Log.d(TAG, "Ignoring unlock for authenticated user (" + authenticatedUserId +
313                    ") because the current user is " + currentUserId);
314        }
315    }
316
317    /**
318     * Stops the Face Unlock service and goes to the backup lock.
319     */
320    void handleCancel() {
321        if (DEBUG) Log.d(TAG, "handleCancel()");
322        // We are going to the backup method, so we don't want to see Face Unlock again until the
323        // next time the user visits keyguard.
324        KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(false);
325
326        mKeyguardScreenCallback.showBackupSecurity();
327        stop();
328        mKeyguardScreenCallback.userActivity(BACKUP_LOCK_TIMEOUT);
329    }
330
331    /**
332     * Increments the number of failed Face Unlock attempts.
333     */
334    void handleReportFailedAttempt() {
335        if (DEBUG) Log.d(TAG, "handleReportFailedAttempt()");
336        // We are going to the backup method, so we don't want to see Face Unlock again until the
337        // next time the user visits keyguard.
338        KeyguardUpdateMonitor.getInstance(mContext).setAlternateUnlockEnabled(false);
339
340        mKeyguardScreenCallback.reportUnlockAttempt(false);
341    }
342
343    /**
344     * If the screen is on, pokes the wakelock to keep the screen alive and active for a specific
345     * amount of time.
346     */
347    void handlePokeWakelock(int millis) {
348      PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
349      if (powerManager.isScreenOn()) {
350        mKeyguardScreenCallback.userActivity(millis);
351      }
352    }
353
354    /**
355     * Implements service connection methods.
356     */
357    private ServiceConnection mConnection = new ServiceConnection() {
358        /**
359         * Called when the Face Unlock service connects after calling bind().
360         */
361        public void onServiceConnected(ComponentName className, IBinder iservice) {
362            Log.d(TAG, "Connected to Face Unlock service");
363            mService = IFaceLockInterface.Stub.asInterface(iservice);
364            mHandler.sendEmptyMessage(MSG_SERVICE_CONNECTED);
365        }
366
367        /**
368         * Called if the Face Unlock service unexpectedly disconnects.  This indicates an error.
369         */
370        public void onServiceDisconnected(ComponentName className) {
371            Log.e(TAG, "Unexpected disconnect from Face Unlock service");
372            mHandler.sendEmptyMessage(MSG_SERVICE_DISCONNECTED);
373        }
374    };
375
376    /**
377     * Tells the Face Unlock service to start displaying its UI and start processing.
378     */
379    private void startUi(IBinder windowToken, int x, int y, int w, int h) {
380        if (DEBUG) Log.d(TAG, "startUi()");
381        synchronized (mServiceRunningLock) {
382            if (!mServiceRunning) {
383                Log.d(TAG, "Starting Face Unlock");
384                try {
385                    mService.startUi(windowToken, x, y, w, h,
386                            mLockPatternUtils.isBiometricWeakLivelinessEnabled());
387                } catch (RemoteException e) {
388                    Log.e(TAG, "Caught exception starting Face Unlock: " + e.toString());
389                    return;
390                }
391                mServiceRunning = true;
392            } else {
393                Log.w(TAG, "startUi() attempted while running");
394            }
395        }
396    }
397
398    /**
399     * Tells the Face Unlock service to stop displaying its UI and stop processing.
400     */
401    private void stopUi() {
402        if (DEBUG) Log.d(TAG, "stopUi()");
403        // Note that attempting to stop Face Unlock when it's not running is not an issue.
404        // Face Unlock can return, which stops it and then we try to stop it when the
405        // screen is turned off.  That's why we check.
406        synchronized (mServiceRunningLock) {
407            if (mServiceRunning) {
408                Log.d(TAG, "Stopping Face Unlock");
409                try {
410                    mService.stopUi();
411                } catch (RemoteException e) {
412                    Log.e(TAG, "Caught exception stopping Face Unlock: " + e.toString());
413                }
414                mServiceRunning = false;
415            } else {
416                // This is usually not an error when this happens.  Sometimes we will tell it to
417                // stop multiple times because it's called from both onWindowFocusChanged and
418                // onDetachedFromWindow.
419                if (DEBUG) Log.d(TAG, "stopUi() attempted while not running");
420            }
421        }
422    }
423
424    /**
425     * Implements the AIDL biometric unlock service callback interface.
426     */
427    private final IFaceLockCallback mFaceUnlockCallback = new IFaceLockCallback.Stub() {
428        /**
429         * Called when Face Unlock wants to grant access to the user.
430         */
431        public void unlock() {
432            if (DEBUG) Log.d(TAG, "unlock()");
433            Message message = mHandler.obtainMessage(MSG_UNLOCK, UserHandle.getCallingUserId(), -1);
434            mHandler.sendMessage(message);
435        }
436
437        /**
438         * Called when Face Unlock wants to go to the backup.
439         */
440        public void cancel() {
441            if (DEBUG) Log.d(TAG, "cancel()");
442            mHandler.sendEmptyMessage(MSG_CANCEL);
443        }
444
445        /**
446         * Called when Face Unlock wants to increment the number of failed attempts.
447         */
448        public void reportFailedAttempt() {
449            if (DEBUG) Log.d(TAG, "reportFailedAttempt()");
450            mHandler.sendEmptyMessage(MSG_REPORT_FAILED_ATTEMPT);
451        }
452
453        /**
454         * Called when Face Unlock wants to keep the screen alive and active for a specific amount
455         * of time.
456         */
457        public void pokeWakelock(int millis) {
458            if (DEBUG) Log.d(TAG, "pokeWakelock() for " + millis + "ms");
459            Message message = mHandler.obtainMessage(MSG_POKE_WAKELOCK, millis, -1);
460            mHandler.sendMessage(message);
461        }
462
463    };
464}
465