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