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