1 /**
2  * Copyright (C) 2018 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 android.hardware.face;
18 
19 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
20 import static android.Manifest.permission.MANAGE_BIOMETRIC;
21 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.RequiresPermission;
26 import android.annotation.SystemService;
27 import android.app.ActivityManager;
28 import android.content.Context;
29 import android.hardware.biometrics.BiometricAuthenticator;
30 import android.hardware.biometrics.BiometricConstants;
31 import android.hardware.biometrics.BiometricFaceConstants;
32 import android.hardware.biometrics.CryptoObject;
33 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
34 import android.os.Binder;
35 import android.os.CancellationSignal;
36 import android.os.CancellationSignal.OnCancelListener;
37 import android.os.Handler;
38 import android.os.IBinder;
39 import android.os.IRemoteCallback;
40 import android.os.Looper;
41 import android.os.PowerManager;
42 import android.os.RemoteException;
43 import android.os.Trace;
44 import android.os.UserHandle;
45 import android.util.Log;
46 import android.util.Slog;
47 
48 import com.android.internal.R;
49 import com.android.internal.os.SomeArgs;
50 
51 import java.util.List;
52 
53 /**
54  * A class that coordinates access to the face authentication hardware.
55  * @hide
56  */
57 @SystemService(Context.FACE_SERVICE)
58 public class FaceManager implements BiometricAuthenticator, BiometricFaceConstants {
59 
60     private static final String TAG = "FaceManager";
61     private static final boolean DEBUG = true;
62     private static final int MSG_ENROLL_RESULT = 100;
63     private static final int MSG_ACQUIRED = 101;
64     private static final int MSG_AUTHENTICATION_SUCCEEDED = 102;
65     private static final int MSG_AUTHENTICATION_FAILED = 103;
66     private static final int MSG_ERROR = 104;
67     private static final int MSG_REMOVED = 105;
68     private static final int MSG_GET_FEATURE_COMPLETED = 106;
69     private static final int MSG_SET_FEATURE_COMPLETED = 107;
70 
71     private IFaceService mService;
72     private final Context mContext;
73     private IBinder mToken = new Binder();
74     private AuthenticationCallback mAuthenticationCallback;
75     private EnrollmentCallback mEnrollmentCallback;
76     private RemovalCallback mRemovalCallback;
77     private SetFeatureCallback mSetFeatureCallback;
78     private GetFeatureCallback mGetFeatureCallback;
79     private CryptoObject mCryptoObject;
80     private Face mRemovalFace;
81     private Handler mHandler;
82 
83     private IFaceServiceReceiver mServiceReceiver = new IFaceServiceReceiver.Stub() {
84 
85         @Override // binder call
86         public void onEnrollResult(long deviceId, int faceId, int remaining) {
87             mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
88                     new Face(null, faceId, deviceId)).sendToTarget();
89         }
90 
91         @Override // binder call
92         public void onAcquired(long deviceId, int acquireInfo, int vendorCode) {
93             mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode, deviceId).sendToTarget();
94         }
95 
96         @Override // binder call
97         public void onAuthenticationSucceeded(long deviceId, Face face, int userId) {
98             mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, face).sendToTarget();
99         }
100 
101         @Override // binder call
102         public void onAuthenticationFailed(long deviceId) {
103             mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
104         }
105 
106         @Override // binder call
107         public void onError(long deviceId, int error, int vendorCode) {
108             mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
109         }
110 
111         @Override // binder call
112         public void onRemoved(long deviceId, int faceId, int remaining) {
113             mHandler.obtainMessage(MSG_REMOVED, remaining, 0,
114                     new Face(null, faceId, deviceId)).sendToTarget();
115         }
116 
117         @Override
118         public void onEnumerated(long deviceId, int faceId, int remaining) {
119             // TODO: Finish. Low priority since it's not used.
120         }
121 
122         @Override
123         public void onFeatureSet(boolean success, int feature) {
124             mHandler.obtainMessage(MSG_SET_FEATURE_COMPLETED, feature, 0, success).sendToTarget();
125         }
126 
127         @Override
128         public void onFeatureGet(boolean success, int feature, boolean value) {
129             SomeArgs args = SomeArgs.obtain();
130             args.arg1 = success;
131             args.argi1 = feature;
132             args.arg2 = value;
133             mHandler.obtainMessage(MSG_GET_FEATURE_COMPLETED, args).sendToTarget();
134         }
135     };
136 
137     /**
138      * @hide
139      */
FaceManager(Context context, IFaceService service)140     public FaceManager(Context context, IFaceService service) {
141         mContext = context;
142         mService = service;
143         if (mService == null) {
144             Slog.v(TAG, "FaceAuthenticationManagerService was null");
145         }
146         mHandler = new MyHandler(context);
147     }
148 
149     /**
150      * Request authentication of a crypto object. This call operates the face recognition hardware
151      * and starts capturing images. It terminates when
152      * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
153      * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at
154      * which point the object is no longer valid. The operation can be canceled by using the
155      * provided cancel object.
156      *
157      * @param crypto   object associated with the call or null if none required.
158      * @param cancel   an object that can be used to cancel authentication
159      * @param flags    optional flags; should be 0
160      * @param callback an object to receive authentication events
161      * @param handler  an optional handler to handle callback events
162      * @throws IllegalArgumentException if the crypto operation is not supported or is not backed
163      *                                  by
164      *                                  <a href="{@docRoot}training/articles/keystore.html">Android
165      *                                  Keystore facility</a>.
166      * @throws IllegalStateException    if the crypto primitive is not initialized.
167      * @hide
168      */
169     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
authenticate(@ullable CryptoObject crypto, @Nullable CancellationSignal cancel, int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler)170     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
171             int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
172         authenticate(crypto, cancel, flags, callback, handler, mContext.getUserId());
173     }
174 
175     /**
176      * Use the provided handler thread for events.
177      */
useHandler(Handler handler)178     private void useHandler(Handler handler) {
179         if (handler != null) {
180             mHandler = new MyHandler(handler.getLooper());
181         } else if (mHandler.getLooper() != mContext.getMainLooper()) {
182             mHandler = new MyHandler(mContext.getMainLooper());
183         }
184     }
185 
186     /**
187      * Request authentication of a crypto object. This call operates the face recognition hardware
188      * and starts capturing images. It terminates when
189      * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
190      * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at
191      * which point the object is no longer valid. The operation can be canceled by using the
192      * provided cancel object.
193      *
194      * @param crypto   object associated with the call or null if none required.
195      * @param cancel   an object that can be used to cancel authentication
196      * @param flags    optional flags; should be 0
197      * @param callback an object to receive authentication events
198      * @param handler  an optional handler to handle callback events
199      * @param userId   userId to authenticate for
200      * @throws IllegalArgumentException if the crypto operation is not supported or is not backed
201      *                                  by
202      *                                  <a href="{@docRoot}training/articles/keystore.html">Android
203      *                                  Keystore facility</a>.
204      * @throws IllegalStateException    if the crypto primitive is not initialized.
205      * @hide
206      */
authenticate(@ullable CryptoObject crypto, @Nullable CancellationSignal cancel, int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler, int userId)207     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
208             int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler,
209             int userId) {
210         if (callback == null) {
211             throw new IllegalArgumentException("Must supply an authentication callback");
212         }
213 
214         if (cancel != null) {
215             if (cancel.isCanceled()) {
216                 Log.w(TAG, "authentication already canceled");
217                 return;
218             } else {
219                 cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
220             }
221         }
222 
223         if (mService != null) {
224             try {
225                 useHandler(handler);
226                 mAuthenticationCallback = callback;
227                 mCryptoObject = crypto;
228                 long sessionId = crypto != null ? crypto.getOpId() : 0;
229                 Trace.beginSection("FaceManager#authenticate");
230                 mService.authenticate(mToken, sessionId, userId, mServiceReceiver,
231                         flags, mContext.getOpPackageName());
232             } catch (RemoteException e) {
233                 Log.w(TAG, "Remote exception while authenticating: ", e);
234                 if (callback != null) {
235                     // Though this may not be a hardware issue, it will cause apps to give up or
236                     // try again later.
237                     callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
238                             getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
239                                     0 /* vendorCode */));
240                 }
241             } finally {
242                 Trace.endSection();
243             }
244         }
245     }
246 
247     /**
248      * Request face authentication enrollment. This call operates the face authentication hardware
249      * and starts capturing images. Progress will be indicated by callbacks to the
250      * {@link EnrollmentCallback} object. It terminates when
251      * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or
252      * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at
253      * which point the object is no longer valid. The operation can be canceled by using the
254      * provided cancel object.
255      *
256      * @param token    a unique token provided by a recent creation or verification of device
257      *                 credentials (e.g. pin, pattern or password).
258      * @param cancel   an object that can be used to cancel enrollment
259      * @param flags    optional flags
260      * @param userId   the user to whom this face will belong to
261      * @param callback an object to receive enrollment events
262      * @hide
263      */
264     @RequiresPermission(MANAGE_BIOMETRIC)
enroll(int userId, byte[] token, CancellationSignal cancel, EnrollmentCallback callback, int[] disabledFeatures)265     public void enroll(int userId, byte[] token, CancellationSignal cancel,
266             EnrollmentCallback callback, int[] disabledFeatures) {
267         if (callback == null) {
268             throw new IllegalArgumentException("Must supply an enrollment callback");
269         }
270 
271         if (cancel != null) {
272             if (cancel.isCanceled()) {
273                 Log.w(TAG, "enrollment already canceled");
274                 return;
275             } else {
276                 cancel.setOnCancelListener(new OnEnrollCancelListener());
277             }
278         }
279 
280         if (mService != null) {
281             try {
282                 mEnrollmentCallback = callback;
283                 Trace.beginSection("FaceManager#enroll");
284                 mService.enroll(userId, mToken, token, mServiceReceiver,
285                         mContext.getOpPackageName(), disabledFeatures);
286             } catch (RemoteException e) {
287                 Log.w(TAG, "Remote exception in enroll: ", e);
288                 if (callback != null) {
289                     // Though this may not be a hardware issue, it will cause apps to give up or
290                     // try again later.
291                     callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
292                             getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
293                                 0 /* vendorCode */));
294                 }
295             } finally {
296                 Trace.endSection();
297             }
298         }
299     }
300 
301     /**
302      * Requests an auth token to tie sensitive operations to the confirmation of
303      * existing device credentials (e.g. pin/pattern/password).
304      *
305      * @hide
306      */
307     @RequiresPermission(MANAGE_BIOMETRIC)
generateChallenge()308     public long generateChallenge() {
309         long result = 0;
310         if (mService != null) {
311             try {
312                 result = mService.generateChallenge(mToken);
313             } catch (RemoteException e) {
314                 throw e.rethrowFromSystemServer();
315             }
316         }
317         return result;
318     }
319 
320     /**
321      * Invalidates the current auth token.
322      *
323      * @hide
324      */
325     @RequiresPermission(MANAGE_BIOMETRIC)
revokeChallenge()326     public int revokeChallenge() {
327         int result = 0;
328         if (mService != null) {
329             try {
330                 result = mService.revokeChallenge(mToken);
331             } catch (RemoteException e) {
332                 throw e.rethrowFromSystemServer();
333             }
334         }
335         return result;
336     }
337 
338     /**
339      * @hide
340      */
341     @RequiresPermission(MANAGE_BIOMETRIC)
setFeature(int userId, int feature, boolean enabled, byte[] token, SetFeatureCallback callback)342     public void setFeature(int userId, int feature, boolean enabled, byte[] token,
343             SetFeatureCallback callback) {
344         if (mService != null) {
345             try {
346                 mSetFeatureCallback = callback;
347                 mService.setFeature(userId, feature, enabled, token, mServiceReceiver,
348                         mContext.getOpPackageName());
349             } catch (RemoteException e) {
350                 throw e.rethrowFromSystemServer();
351             }
352         }
353     }
354 
355     /**
356      * @hide
357      */
358     @RequiresPermission(MANAGE_BIOMETRIC)
getFeature(int userId, int feature, GetFeatureCallback callback)359     public void getFeature(int userId, int feature, GetFeatureCallback callback) {
360         if (mService != null) {
361             try {
362                 mGetFeatureCallback = callback;
363                 mService.getFeature(userId, feature, mServiceReceiver, mContext.getOpPackageName());
364             } catch (RemoteException e) {
365                 throw e.rethrowFromSystemServer();
366             }
367         }
368     }
369 
370     /**
371      * Pokes the the driver to have it start looking for faces again.
372      * @hide
373      */
374     @RequiresPermission(MANAGE_BIOMETRIC)
userActivity()375     public void userActivity() {
376         if (mService != null) {
377             try {
378                 mService.userActivity();
379             } catch (RemoteException e) {
380                 throw e.rethrowFromSystemServer();
381             }
382         }
383     }
384 
385     /**
386      * Sets the active user. This is meant to be used to select the current profile for enrollment
387      * to allow separate enrolled faces for a work profile
388      *
389      * @hide
390      */
391     @RequiresPermission(MANAGE_BIOMETRIC)
392     @Override
setActiveUser(int userId)393     public void setActiveUser(int userId) {
394         if (mService != null) {
395             try {
396                 mService.setActiveUser(userId);
397             } catch (RemoteException e) {
398                 throw e.rethrowFromSystemServer();
399             }
400         }
401     }
402 
403     /**
404      * Remove given face template from face hardware and/or protected storage.
405      *
406      * @param face     the face item to remove
407      * @param userId   the user who this face belongs to
408      * @param callback an optional callback to verify that face templates have been
409      *                 successfully removed. May be null if no callback is required.
410      * @hide
411      */
412     @RequiresPermission(MANAGE_BIOMETRIC)
remove(Face face, int userId, RemovalCallback callback)413     public void remove(Face face, int userId, RemovalCallback callback) {
414         if (mService != null) {
415             try {
416                 mRemovalCallback = callback;
417                 mRemovalFace = face;
418                 mService.remove(mToken, face.getBiometricId(), userId, mServiceReceiver,
419                         mContext.getOpPackageName());
420             } catch (RemoteException e) {
421                 Log.w(TAG, "Remote exception in remove: ", e);
422                 if (callback != null) {
423                     callback.onRemovalError(face, FACE_ERROR_HW_UNAVAILABLE,
424                             getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
425                                 0 /* vendorCode */));
426                 }
427             }
428         }
429     }
430 
431     /**
432      * Obtain the enrolled face template.
433      *
434      * @return the current face item
435      * @hide
436      */
437     @RequiresPermission(MANAGE_BIOMETRIC)
getEnrolledFaces(int userId)438     public List<Face> getEnrolledFaces(int userId) {
439         if (mService != null) {
440             try {
441                 return mService.getEnrolledFaces(userId, mContext.getOpPackageName());
442             } catch (RemoteException e) {
443                 throw e.rethrowFromSystemServer();
444             }
445         }
446         return null;
447     }
448 
449     /**
450      * Obtain the enrolled face template.
451      *
452      * @return the current face item
453      * @hide
454      */
455     @RequiresPermission(MANAGE_BIOMETRIC)
getEnrolledFaces()456     public List<Face> getEnrolledFaces() {
457         return getEnrolledFaces(UserHandle.myUserId());
458     }
459 
460     /**
461      * Determine if there is a face enrolled.
462      *
463      * @return true if a face is enrolled, false otherwise
464      */
465     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
466     @Override
hasEnrolledTemplates()467     public boolean hasEnrolledTemplates() {
468         if (mService != null) {
469             try {
470                 return mService.hasEnrolledFaces(
471                         UserHandle.myUserId(), mContext.getOpPackageName());
472             } catch (RemoteException e) {
473                 throw e.rethrowFromSystemServer();
474             }
475         }
476         return false;
477     }
478 
479     /**
480      * @hide
481      */
482     @RequiresPermission(allOf = {
483             USE_BIOMETRIC_INTERNAL,
484             INTERACT_ACROSS_USERS})
485     @Override
hasEnrolledTemplates(int userId)486     public boolean hasEnrolledTemplates(int userId) {
487         if (mService != null) {
488             try {
489                 return mService.hasEnrolledFaces(userId, mContext.getOpPackageName());
490             } catch (RemoteException e) {
491                 throw e.rethrowFromSystemServer();
492             }
493         }
494         return false;
495     }
496 
497     /**
498      * Determine if face authentication sensor hardware is present and functional.
499      *
500      * @return true if hardware is present and functional, false otherwise.
501      */
502     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
503     @Override
isHardwareDetected()504     public boolean isHardwareDetected() {
505         if (mService != null) {
506             try {
507                 long deviceId = 0; /* TODO: plumb hardware id to FPMS */
508                 return mService.isHardwareDetected(deviceId, mContext.getOpPackageName());
509             } catch (RemoteException e) {
510                 throw e.rethrowFromSystemServer();
511             }
512         } else {
513             Log.w(TAG, "isFaceHardwareDetected(): Service not connected!");
514         }
515         return false;
516     }
517 
518     /**
519      * Retrieves the authenticator token for binding keys to the lifecycle
520      * of the calling user's face. Used only by internal clients.
521      *
522      * @hide
523      */
getAuthenticatorId()524     public long getAuthenticatorId() {
525         if (mService != null) {
526             try {
527                 return mService.getAuthenticatorId(mContext.getOpPackageName());
528             } catch (RemoteException e) {
529                 throw e.rethrowFromSystemServer();
530             }
531         } else {
532             Log.w(TAG, "getAuthenticatorId(): Service not connected!");
533         }
534         return 0;
535     }
536 
537     /**
538      * @hide
539      */
540     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
addLockoutResetCallback(final LockoutResetCallback callback)541     public void addLockoutResetCallback(final LockoutResetCallback callback) {
542         if (mService != null) {
543             try {
544                 final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
545                 mService.addLockoutResetCallback(
546                         new IBiometricServiceLockoutResetCallback.Stub() {
547 
548                             @Override
549                             public void onLockoutReset(long deviceId,
550                                     IRemoteCallback serverCallback)
551                                     throws RemoteException {
552                                 try {
553                                     final PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
554                                             PowerManager.PARTIAL_WAKE_LOCK,
555                                             "faceLockoutResetCallback");
556                                     wakeLock.acquire();
557                                     mHandler.post(() -> {
558                                         try {
559                                             callback.onLockoutReset();
560                                         } finally {
561                                             wakeLock.release();
562                                         }
563                                     });
564                                 } finally {
565                                     serverCallback.sendResult(null /* data */);
566                                 }
567                             }
568                         });
569             } catch (RemoteException e) {
570                 throw e.rethrowFromSystemServer();
571             }
572         } else {
573             Log.w(TAG, "addLockoutResetCallback(): Service not connected!");
574         }
575     }
576 
getCurrentUserId()577     private int getCurrentUserId() {
578         try {
579             return ActivityManager.getService().getCurrentUser().id;
580         } catch (RemoteException e) {
581             throw e.rethrowFromSystemServer();
582         }
583     }
584 
cancelEnrollment()585     private void cancelEnrollment() {
586         if (mService != null) {
587             try {
588                 mService.cancelEnrollment(mToken);
589             } catch (RemoteException e) {
590                 throw e.rethrowFromSystemServer();
591             }
592         }
593     }
594 
cancelAuthentication(CryptoObject cryptoObject)595     private void cancelAuthentication(CryptoObject cryptoObject) {
596         if (mService != null) {
597             try {
598                 mService.cancelAuthentication(mToken, mContext.getOpPackageName());
599             } catch (RemoteException e) {
600                 throw e.rethrowFromSystemServer();
601             }
602         }
603     }
604 
605     /**
606      * @hide
607      */
getErrorString(Context context, int errMsg, int vendorCode)608     public static String getErrorString(Context context, int errMsg, int vendorCode) {
609         switch (errMsg) {
610             case FACE_ERROR_HW_UNAVAILABLE:
611                 return context.getString(
612                         com.android.internal.R.string.face_error_hw_not_available);
613             case FACE_ERROR_UNABLE_TO_PROCESS:
614                 return context.getString(
615                         com.android.internal.R.string.face_error_unable_to_process);
616             case FACE_ERROR_TIMEOUT:
617                 return context.getString(com.android.internal.R.string.face_error_timeout);
618             case FACE_ERROR_NO_SPACE:
619                 return context.getString(com.android.internal.R.string.face_error_no_space);
620             case FACE_ERROR_CANCELED:
621                 return context.getString(com.android.internal.R.string.face_error_canceled);
622             case FACE_ERROR_LOCKOUT:
623                 return context.getString(com.android.internal.R.string.face_error_lockout);
624             case FACE_ERROR_LOCKOUT_PERMANENT:
625                 return context.getString(
626                         com.android.internal.R.string.face_error_lockout_permanent);
627             case FACE_ERROR_USER_CANCELED:
628                 return context.getString(com.android.internal.R.string.face_error_user_canceled);
629             case FACE_ERROR_NOT_ENROLLED:
630                 return context.getString(com.android.internal.R.string.face_error_not_enrolled);
631             case FACE_ERROR_HW_NOT_PRESENT:
632                 return context.getString(com.android.internal.R.string.face_error_hw_not_present);
633             case FACE_ERROR_VENDOR: {
634                 String[] msgArray = context.getResources().getStringArray(
635                         com.android.internal.R.array.face_error_vendor);
636                 if (vendorCode < msgArray.length) {
637                     return msgArray[vendorCode];
638                 }
639             }
640         }
641         Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode);
642         return "";
643     }
644 
645     /**
646      * @hide
647      */
getAcquiredString(Context context, int acquireInfo, int vendorCode)648     public static String getAcquiredString(Context context, int acquireInfo, int vendorCode) {
649         switch (acquireInfo) {
650             case FACE_ACQUIRED_GOOD:
651                 return null;
652             case FACE_ACQUIRED_INSUFFICIENT:
653                 return context.getString(R.string.face_acquired_insufficient);
654             case FACE_ACQUIRED_TOO_BRIGHT:
655                 return context.getString(R.string.face_acquired_too_bright);
656             case FACE_ACQUIRED_TOO_DARK:
657                 return context.getString(R.string.face_acquired_too_dark);
658             case FACE_ACQUIRED_TOO_CLOSE:
659                 return context.getString(R.string.face_acquired_too_close);
660             case FACE_ACQUIRED_TOO_FAR:
661                 return context.getString(R.string.face_acquired_too_far);
662             case FACE_ACQUIRED_TOO_HIGH:
663                 return context.getString(R.string.face_acquired_too_high);
664             case FACE_ACQUIRED_TOO_LOW:
665                 return context.getString(R.string.face_acquired_too_low);
666             case FACE_ACQUIRED_TOO_RIGHT:
667                 return context.getString(R.string.face_acquired_too_right);
668             case FACE_ACQUIRED_TOO_LEFT:
669                 return context.getString(R.string.face_acquired_too_left);
670             case FACE_ACQUIRED_POOR_GAZE:
671                 return context.getString(R.string.face_acquired_poor_gaze);
672             case FACE_ACQUIRED_NOT_DETECTED:
673                 return context.getString(R.string.face_acquired_not_detected);
674             case FACE_ACQUIRED_TOO_MUCH_MOTION:
675                 return context.getString(R.string.face_acquired_too_much_motion);
676             case FACE_ACQUIRED_RECALIBRATE:
677                 return context.getString(R.string.face_acquired_recalibrate);
678             case FACE_ACQUIRED_TOO_DIFFERENT:
679                 return context.getString(R.string.face_acquired_too_different);
680             case FACE_ACQUIRED_TOO_SIMILAR:
681                 return context.getString(R.string.face_acquired_too_similar);
682             case FACE_ACQUIRED_PAN_TOO_EXTREME:
683                 return context.getString(R.string.face_acquired_pan_too_extreme);
684             case FACE_ACQUIRED_TILT_TOO_EXTREME:
685                 return context.getString(R.string.face_acquired_tilt_too_extreme);
686             case FACE_ACQUIRED_ROLL_TOO_EXTREME:
687                 return context.getString(R.string.face_acquired_roll_too_extreme);
688             case FACE_ACQUIRED_FACE_OBSCURED:
689                 return context.getString(R.string.face_acquired_obscured);
690             case FACE_ACQUIRED_START:
691                 return null;
692             case FACE_ACQUIRED_SENSOR_DIRTY:
693                 return context.getString(R.string.face_acquired_sensor_dirty);
694             case FACE_ACQUIRED_VENDOR: {
695                 String[] msgArray = context.getResources().getStringArray(
696                         R.array.face_acquired_vendor);
697                 if (vendorCode < msgArray.length) {
698                     return msgArray[vendorCode];
699                 }
700             }
701         }
702         Slog.w(TAG, "Invalid acquired message: " + acquireInfo + ", " + vendorCode);
703         return null;
704     }
705 
706     /**
707      * Used so BiometricPrompt can map the face ones onto existing public constants.
708      * @hide
709      */
getMappedAcquiredInfo(int acquireInfo, int vendorCode)710     public static int getMappedAcquiredInfo(int acquireInfo, int vendorCode) {
711         switch (acquireInfo) {
712             case FACE_ACQUIRED_GOOD:
713                 return BiometricConstants.BIOMETRIC_ACQUIRED_GOOD;
714             case FACE_ACQUIRED_INSUFFICIENT:
715             case FACE_ACQUIRED_TOO_BRIGHT:
716             case FACE_ACQUIRED_TOO_DARK:
717                 return BiometricConstants.BIOMETRIC_ACQUIRED_INSUFFICIENT;
718             case FACE_ACQUIRED_TOO_CLOSE:
719             case FACE_ACQUIRED_TOO_FAR:
720             case FACE_ACQUIRED_TOO_HIGH:
721             case FACE_ACQUIRED_TOO_LOW:
722             case FACE_ACQUIRED_TOO_RIGHT:
723             case FACE_ACQUIRED_TOO_LEFT:
724                 return BiometricConstants.BIOMETRIC_ACQUIRED_PARTIAL;
725             case FACE_ACQUIRED_POOR_GAZE:
726             case FACE_ACQUIRED_NOT_DETECTED:
727             case FACE_ACQUIRED_TOO_MUCH_MOTION:
728             case FACE_ACQUIRED_RECALIBRATE:
729                 return BiometricConstants.BIOMETRIC_ACQUIRED_INSUFFICIENT;
730             case FACE_ACQUIRED_VENDOR:
731                 return BiometricConstants.BIOMETRIC_ACQUIRED_VENDOR_BASE + vendorCode;
732             default:
733                 return BiometricConstants.BIOMETRIC_ACQUIRED_GOOD;
734         }
735     }
736 
737     /**
738      * Container for callback data from {@link FaceManager#authenticate(CryptoObject,
739      * CancellationSignal, int, AuthenticationCallback, Handler)}.
740      */
741     public static class AuthenticationResult {
742         private Face mFace;
743         private CryptoObject mCryptoObject;
744         private int mUserId;
745 
746         /**
747          * Authentication result
748          *
749          * @param crypto the crypto object
750          * @param face   the recognized face data, if allowed.
751          * @hide
752          */
AuthenticationResult(CryptoObject crypto, Face face, int userId)753         public AuthenticationResult(CryptoObject crypto, Face face, int userId) {
754             mCryptoObject = crypto;
755             mFace = face;
756             mUserId = userId;
757         }
758 
759         /**
760          * Obtain the crypto object associated with this transaction
761          *
762          * @return crypto object provided to {@link FaceManager#authenticate
763          * (CryptoObject,
764          * CancellationSignal, int, AuthenticationCallback, Handler)}.
765          */
getCryptoObject()766         public CryptoObject getCryptoObject() {
767             return mCryptoObject;
768         }
769 
770         /**
771          * Obtain the Face associated with this operation. Applications are strongly
772          * discouraged from associating specific faces with specific applications or operations.
773          *
774          * @hide
775          */
getFace()776         public Face getFace() {
777             return mFace;
778         }
779 
780         /**
781          * Obtain the userId for which this face was authenticated.
782          *
783          * @hide
784          */
getUserId()785         public int getUserId() {
786             return mUserId;
787         }
788     }
789 
790     /**
791      * Callback structure provided to {@link FaceManager#authenticate(CryptoObject,
792      * CancellationSignal, int, AuthenticationCallback, Handler)}. Users of {@link
793      * FaceManager#authenticate(CryptoObject, CancellationSignal,
794      * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening
795      * to face events.
796      */
797     public abstract static class AuthenticationCallback
798             extends BiometricAuthenticator.AuthenticationCallback {
799 
800         /**
801          * Called when an unrecoverable error has been encountered and the operation is complete.
802          * No further callbacks will be made on this object.
803          *
804          * @param errorCode An integer identifying the error message
805          * @param errString A human-readable error string that can be shown in UI
806          */
onAuthenticationError(int errorCode, CharSequence errString)807         public void onAuthenticationError(int errorCode, CharSequence errString) {
808         }
809 
810         /**
811          * Called when a recoverable error has been encountered during authentication. The help
812          * string is provided to give the user guidance for what went wrong, such as
813          * "Sensor dirty, please clean it."
814          *
815          * @param helpCode   An integer identifying the error message
816          * @param helpString A human-readable string that can be shown in UI
817          */
onAuthenticationHelp(int helpCode, CharSequence helpString)818         public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
819         }
820 
821         /**
822          * Called when a face is recognized.
823          *
824          * @param result An object containing authentication-related data
825          */
onAuthenticationSucceeded(AuthenticationResult result)826         public void onAuthenticationSucceeded(AuthenticationResult result) {
827         }
828 
829         /**
830          * Called when a face is detected but not recognized.
831          */
onAuthenticationFailed()832         public void onAuthenticationFailed() {
833         }
834 
835         /**
836          * Called when a face image has been acquired, but wasn't processed yet.
837          *
838          * @param acquireInfo one of FACE_ACQUIRED_* constants
839          * @hide
840          */
onAuthenticationAcquired(int acquireInfo)841         public void onAuthenticationAcquired(int acquireInfo) {
842         }
843     }
844 
845     /**
846      * Callback structure provided to {@link FaceManager#enroll(long,
847      * EnrollmentCallback, CancellationSignal, int). Users of {@link #FaceAuthenticationManager()}
848      * must provide an implementation of this to {@link FaceManager#enroll(long,
849      * CancellationSignal, int, EnrollmentCallback) for listening to face enrollment events.
850      *
851      * @hide
852      */
853     public abstract static class EnrollmentCallback {
854 
855         /**
856          * Called when an unrecoverable error has been encountered and the operation is complete.
857          * No further callbacks will be made on this object.
858          *
859          * @param errMsgId  An integer identifying the error message
860          * @param errString A human-readable error string that can be shown in UI
861          */
onEnrollmentError(int errMsgId, CharSequence errString)862         public void onEnrollmentError(int errMsgId, CharSequence errString) {
863         }
864 
865         /**
866          * Called when a recoverable error has been encountered during enrollment. The help
867          * string is provided to give the user guidance for what went wrong, such as
868          * "Image too dark, uncover light source" or what they need to do next, such as
869          * "Rotate face up / down."
870          *
871          * @param helpMsgId  An integer identifying the error message
872          * @param helpString A human-readable string that can be shown in UI
873          */
onEnrollmentHelp(int helpMsgId, CharSequence helpString)874         public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
875         }
876 
877         /**
878          * Called as each enrollment step progresses. Enrollment is considered complete when
879          * remaining reaches 0. This function will not be called if enrollment fails. See
880          * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)}
881          *
882          * @param remaining The number of remaining steps
883          */
onEnrollmentProgress(int remaining)884         public void onEnrollmentProgress(int remaining) {
885         }
886     }
887 
888     /**
889      * Callback structure provided to {@link #remove}. Users of {@link FaceManager}
890      * may
891      * optionally provide an implementation of this to
892      * {@link #remove(Face, int, RemovalCallback)} for listening to face template
893      * removal events.
894      *
895      * @hide
896      */
897     public abstract static class RemovalCallback {
898 
899         /**
900          * Called when the given face can't be removed.
901          *
902          * @param face      The face that the call attempted to remove
903          * @param errMsgId  An associated error message id
904          * @param errString An error message indicating why the face id can't be removed
905          */
onRemovalError(Face face, int errMsgId, CharSequence errString)906         public void onRemovalError(Face face, int errMsgId, CharSequence errString) {
907         }
908 
909         /**
910          * Called when a given face is successfully removed.
911          *
912          * @param face The face template that was removed.
913          */
onRemovalSucceeded(Face face, int remaining)914         public void onRemovalSucceeded(Face face, int remaining) {
915         }
916     }
917 
918     /**
919      * @hide
920      */
921     public abstract static class LockoutResetCallback {
922 
923         /**
924          * Called when lockout period expired and clients are allowed to listen for face
925          * authentication
926          * again.
927          */
onLockoutReset()928         public void onLockoutReset() {
929         }
930     }
931 
932     /**
933      * @hide
934      */
935     public abstract static class SetFeatureCallback {
onCompleted(boolean success, int feature)936         public abstract void onCompleted(boolean success, int feature);
937     }
938 
939     /**
940      * @hide
941      */
942     public abstract static class GetFeatureCallback {
onCompleted(boolean success, int feature, boolean value)943         public abstract void onCompleted(boolean success, int feature, boolean value);
944     }
945 
946     private class OnEnrollCancelListener implements OnCancelListener {
947         @Override
onCancel()948         public void onCancel() {
949             cancelEnrollment();
950         }
951     }
952 
953     private class OnAuthenticationCancelListener implements OnCancelListener {
954         private CryptoObject mCrypto;
955 
OnAuthenticationCancelListener(CryptoObject crypto)956         OnAuthenticationCancelListener(CryptoObject crypto) {
957             mCrypto = crypto;
958         }
959 
960         @Override
onCancel()961         public void onCancel() {
962             cancelAuthentication(mCrypto);
963         }
964     }
965 
966     private class MyHandler extends Handler {
MyHandler(Context context)967         private MyHandler(Context context) {
968             super(context.getMainLooper());
969         }
970 
MyHandler(Looper looper)971         private MyHandler(Looper looper) {
972             super(looper);
973         }
974 
975         @Override
handleMessage(android.os.Message msg)976         public void handleMessage(android.os.Message msg) {
977             Trace.beginSection("FaceManager#handleMessage: " + Integer.toString(msg.what));
978             switch (msg.what) {
979                 case MSG_ENROLL_RESULT:
980                     sendEnrollResult((Face) msg.obj, msg.arg1 /* remaining */);
981                     break;
982                 case MSG_ACQUIRED:
983                     sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */,
984                             msg.arg2 /* vendorCode */);
985                     break;
986                 case MSG_AUTHENTICATION_SUCCEEDED:
987                     sendAuthenticatedSucceeded((Face) msg.obj, msg.arg1 /* userId */);
988                     break;
989                 case MSG_AUTHENTICATION_FAILED:
990                     sendAuthenticatedFailed();
991                     break;
992                 case MSG_ERROR:
993                     sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */,
994                             msg.arg2 /* vendorCode */);
995                     break;
996                 case MSG_REMOVED:
997                     sendRemovedResult((Face) msg.obj, msg.arg1 /* remaining */);
998                     break;
999                 case MSG_SET_FEATURE_COMPLETED:
1000                     sendSetFeatureCompleted((boolean) msg.obj /* success */,
1001                             msg.arg1 /* feature */);
1002                     break;
1003                 case MSG_GET_FEATURE_COMPLETED:
1004                     SomeArgs args = (SomeArgs) msg.obj;
1005                     sendGetFeatureCompleted((boolean) args.arg1 /* success */,
1006                             args.argi1 /* feature */,
1007                             (boolean) args.arg2 /* value */);
1008                     args.recycle();
1009                     break;
1010                 default:
1011                     Log.w(TAG, "Unknown message: " + msg.what);
1012             }
1013             Trace.endSection();
1014         }
1015     }
1016 
sendSetFeatureCompleted(boolean success, int feature)1017     private void sendSetFeatureCompleted(boolean success, int feature) {
1018         if (mSetFeatureCallback == null) {
1019             return;
1020         }
1021         mSetFeatureCallback.onCompleted(success, feature);
1022     }
1023 
sendGetFeatureCompleted(boolean success, int feature, boolean value)1024     private void sendGetFeatureCompleted(boolean success, int feature, boolean value) {
1025         if (mGetFeatureCallback == null) {
1026             return;
1027         }
1028         mGetFeatureCallback.onCompleted(success, feature, value);
1029     }
1030 
sendRemovedResult(Face face, int remaining)1031     private void sendRemovedResult(Face face, int remaining) {
1032         if (mRemovalCallback == null) {
1033             return;
1034         }
1035         if (face == null) {
1036             Log.e(TAG, "Received MSG_REMOVED, but face is null");
1037             return;
1038         }
1039         mRemovalCallback.onRemovalSucceeded(face, remaining);
1040     }
1041 
sendErrorResult(long deviceId, int errMsgId, int vendorCode)1042     private void sendErrorResult(long deviceId, int errMsgId, int vendorCode) {
1043         // emulate HAL 2.1 behavior and send real errMsgId
1044         final int clientErrMsgId = errMsgId == FACE_ERROR_VENDOR
1045                 ? (vendorCode + FACE_ERROR_VENDOR_BASE) : errMsgId;
1046         if (mEnrollmentCallback != null) {
1047             mEnrollmentCallback.onEnrollmentError(clientErrMsgId,
1048                     getErrorString(mContext, errMsgId, vendorCode));
1049         } else if (mAuthenticationCallback != null) {
1050             mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
1051                     getErrorString(mContext, errMsgId, vendorCode));
1052         } else if (mRemovalCallback != null) {
1053             mRemovalCallback.onRemovalError(mRemovalFace, clientErrMsgId,
1054                     getErrorString(mContext, errMsgId, vendorCode));
1055         }
1056     }
1057 
sendEnrollResult(Face face, int remaining)1058     private void sendEnrollResult(Face face, int remaining) {
1059         if (mEnrollmentCallback != null) {
1060             mEnrollmentCallback.onEnrollmentProgress(remaining);
1061         }
1062     }
1063 
sendAuthenticatedSucceeded(Face face, int userId)1064     private void sendAuthenticatedSucceeded(Face face, int userId) {
1065         if (mAuthenticationCallback != null) {
1066             final AuthenticationResult result =
1067                     new AuthenticationResult(mCryptoObject, face, userId);
1068             mAuthenticationCallback.onAuthenticationSucceeded(result);
1069         }
1070     }
1071 
sendAuthenticatedFailed()1072     private void sendAuthenticatedFailed() {
1073         if (mAuthenticationCallback != null) {
1074             mAuthenticationCallback.onAuthenticationFailed();
1075         }
1076     }
1077 
sendAcquiredResult(long deviceId, int acquireInfo, int vendorCode)1078     private void sendAcquiredResult(long deviceId, int acquireInfo, int vendorCode) {
1079         if (mAuthenticationCallback != null) {
1080             mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
1081         }
1082         final String msg = getAcquiredString(mContext, acquireInfo, vendorCode);
1083         final int clientInfo = acquireInfo == FACE_ACQUIRED_VENDOR
1084                 ? (vendorCode + FACE_ACQUIRED_VENDOR_BASE) : acquireInfo;
1085         if (mEnrollmentCallback != null) {
1086             mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg);
1087         } else if (mAuthenticationCallback != null && msg != null) {
1088             mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
1089         }
1090     }
1091 }
1092