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 import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_LOCKOUT_NONE;
23 
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.annotation.RequiresPermission;
27 import android.annotation.SystemService;
28 import android.content.Context;
29 import android.content.pm.PackageManager;
30 import android.hardware.biometrics.BiometricAuthenticator;
31 import android.hardware.biometrics.BiometricConstants;
32 import android.hardware.biometrics.BiometricFaceConstants;
33 import android.hardware.biometrics.BiometricStateListener;
34 import android.hardware.biometrics.CryptoObject;
35 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
36 import android.os.Binder;
37 import android.os.CancellationSignal;
38 import android.os.CancellationSignal.OnCancelListener;
39 import android.os.Handler;
40 import android.os.IBinder;
41 import android.os.IRemoteCallback;
42 import android.os.Looper;
43 import android.os.PowerManager;
44 import android.os.RemoteException;
45 import android.os.Trace;
46 import android.os.UserHandle;
47 import android.provider.Settings;
48 import android.util.Slog;
49 import android.view.Surface;
50 
51 import com.android.internal.R;
52 import com.android.internal.os.SomeArgs;
53 
54 import java.util.ArrayList;
55 import java.util.List;
56 
57 /**
58  * A class that coordinates access to the face authentication hardware.
59  * @hide
60  */
61 @SystemService(Context.FACE_SERVICE)
62 public class FaceManager implements BiometricAuthenticator, BiometricFaceConstants {
63 
64     private static final String TAG = "FaceManager";
65 
66     private static final int MSG_ENROLL_RESULT = 100;
67     private static final int MSG_ACQUIRED = 101;
68     private static final int MSG_AUTHENTICATION_SUCCEEDED = 102;
69     private static final int MSG_AUTHENTICATION_FAILED = 103;
70     private static final int MSG_ERROR = 104;
71     private static final int MSG_REMOVED = 105;
72     private static final int MSG_GET_FEATURE_COMPLETED = 106;
73     private static final int MSG_SET_FEATURE_COMPLETED = 107;
74     private static final int MSG_CHALLENGE_GENERATED = 108;
75     private static final int MSG_FACE_DETECTED = 109;
76     private static final int MSG_AUTHENTICATION_FRAME = 112;
77     private static final int MSG_ENROLLMENT_FRAME = 113;
78 
79     private final IFaceService mService;
80     private final Context mContext;
81     private final IBinder mToken = new Binder();
82     @Nullable private AuthenticationCallback mAuthenticationCallback;
83     @Nullable private FaceDetectionCallback mFaceDetectionCallback;
84     @Nullable private EnrollmentCallback mEnrollmentCallback;
85     @Nullable private RemovalCallback mRemovalCallback;
86     @Nullable private SetFeatureCallback mSetFeatureCallback;
87     @Nullable private GetFeatureCallback mGetFeatureCallback;
88     @Nullable private GenerateChallengeCallback mGenerateChallengeCallback;
89     private CryptoObject mCryptoObject;
90     private Face mRemovalFace;
91     private Handler mHandler;
92     private List<FaceSensorPropertiesInternal> mProps = new ArrayList<>();
93 
94     private final IFaceServiceReceiver mServiceReceiver = new IFaceServiceReceiver.Stub() {
95 
96         @Override // binder call
97         public void onEnrollResult(Face face, int remaining) {
98             mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0, face).sendToTarget();
99         }
100 
101         @Override // binder call
102         public void onAcquired(int acquireInfo, int vendorCode) {
103             mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode).sendToTarget();
104         }
105 
106         @Override // binder call
107         public void onAuthenticationSucceeded(Face face, int userId, boolean isStrongBiometric) {
108             mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId,
109                     isStrongBiometric ? 1 : 0, face).sendToTarget();
110         }
111 
112         @Override // binder call
113         public void onFaceDetected(int sensorId, int userId, boolean isStrongBiometric) {
114             mHandler.obtainMessage(MSG_FACE_DETECTED, sensorId, userId, isStrongBiometric)
115                     .sendToTarget();
116         }
117 
118         @Override // binder call
119         public void onAuthenticationFailed() {
120             mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
121         }
122 
123         @Override // binder call
124         public void onError(int error, int vendorCode) {
125             mHandler.obtainMessage(MSG_ERROR, error, vendorCode).sendToTarget();
126         }
127 
128         @Override // binder call
129         public void onRemoved(Face face, int remaining) {
130             mHandler.obtainMessage(MSG_REMOVED, remaining, 0, face).sendToTarget();
131             if (remaining == 0) {
132                 Settings.Secure.putIntForUser(mContext.getContentResolver(),
133                         Settings.Secure.FACE_UNLOCK_RE_ENROLL, 0,
134                         UserHandle.USER_CURRENT);
135             }
136         }
137 
138         @Override
139         public void onFeatureSet(boolean success, int feature) {
140             mHandler.obtainMessage(MSG_SET_FEATURE_COMPLETED, feature, 0, success).sendToTarget();
141         }
142 
143         @Override
144         public void onFeatureGet(boolean success, int[] features, boolean[] featureState) {
145             SomeArgs args = SomeArgs.obtain();
146             args.arg1 = success;
147             args.arg2 = features;
148             args.arg3 = featureState;
149             mHandler.obtainMessage(MSG_GET_FEATURE_COMPLETED, args).sendToTarget();
150         }
151 
152         @Override
153         public void onChallengeGenerated(int sensorId, int userId, long challenge) {
154             mHandler.obtainMessage(MSG_CHALLENGE_GENERATED, sensorId, userId, challenge)
155                     .sendToTarget();
156         }
157 
158         @Override
159         public void onAuthenticationFrame(FaceAuthenticationFrame frame) {
160             mHandler.obtainMessage(MSG_AUTHENTICATION_FRAME, frame).sendToTarget();
161         }
162 
163         @Override
164         public void onEnrollmentFrame(FaceEnrollFrame frame) {
165             mHandler.obtainMessage(MSG_ENROLLMENT_FRAME, frame).sendToTarget();
166         }
167     };
168 
169     /**
170      * @hide
171      */
FaceManager(Context context, IFaceService service)172     public FaceManager(Context context, IFaceService service) {
173         mContext = context;
174         mService = service;
175         if (mService == null) {
176             Slog.v(TAG, "FaceAuthenticationManagerService was null");
177         }
178         mHandler = new MyHandler(context);
179         if (context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL)
180                 == PackageManager.PERMISSION_GRANTED) {
181             addAuthenticatorsRegisteredCallback(new IFaceAuthenticatorsRegisteredCallback.Stub() {
182                 @Override
183                 public void onAllAuthenticatorsRegistered(
184                         @NonNull List<FaceSensorPropertiesInternal> sensors) {
185                     mProps = sensors;
186                 }
187             });
188         }
189     }
190 
191     /**
192      * Use the provided handler thread for events.
193      */
useHandler(Handler handler)194     private void useHandler(Handler handler) {
195         if (handler != null) {
196             mHandler = new MyHandler(handler.getLooper());
197         } else if (mHandler.getLooper() != mContext.getMainLooper()) {
198             mHandler = new MyHandler(mContext.getMainLooper());
199         }
200     }
201 
202     /**
203      * @deprecated use {@link #authenticate(CryptoObject, CancellationSignal, AuthenticationCallback, Handler, FaceAuthenticateOptions)}.
204      */
205     @Deprecated
206     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
authenticate(@ullable CryptoObject crypto, @Nullable CancellationSignal cancel, @NonNull AuthenticationCallback callback, @Nullable Handler handler, int userId)207     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
208             @NonNull AuthenticationCallback callback, @Nullable Handler handler, int userId) {
209         authenticate(crypto, cancel, callback, handler, new FaceAuthenticateOptions.Builder()
210                 .setUserId(userId)
211                 .build());
212     }
213 
214     /**
215      * Request authentication. This call operates the face recognition hardware and starts capturing images.
216      * It terminates when
217      * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
218      * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at
219      * which point the object is no longer valid. The operation can be canceled by using the
220      * provided cancel object.
221      *
222      * @param crypto   object associated with the call or null if none required
223      * @param cancel   an object that can be used to cancel authentication
224      * @param callback an object to receive authentication events
225      * @param handler  an optional handler to handle callback events
226      * @param options  additional options to customize this request
227      * @throws IllegalArgumentException if the crypto operation is not supported or is not backed
228      *                                  by
229      *                                  <a href="{@docRoot}training/articles/keystore.html">Android
230      *                                  Keystore facility</a>.
231      * @throws IllegalStateException    if the crypto primitive is not initialized.
232      * @hide
233      */
234     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
authenticate(@ullable CryptoObject crypto, @Nullable CancellationSignal cancel, @NonNull AuthenticationCallback callback, @Nullable Handler handler, @NonNull FaceAuthenticateOptions options)235     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
236             @NonNull AuthenticationCallback callback, @Nullable Handler handler,
237             @NonNull FaceAuthenticateOptions options) {
238         if (callback == null) {
239             throw new IllegalArgumentException("Must supply an authentication callback");
240         }
241 
242         if (cancel != null && cancel.isCanceled()) {
243             Slog.w(TAG, "authentication already canceled");
244             return;
245         }
246 
247         options.setOpPackageName(mContext.getOpPackageName());
248         options.setAttributionTag(mContext.getAttributionTag());
249 
250         if (mService != null) {
251             try {
252                 useHandler(handler);
253                 mAuthenticationCallback = callback;
254                 mCryptoObject = crypto;
255                 final long operationId = crypto != null ? crypto.getOpId() : 0;
256                 Trace.beginSection("FaceManager#authenticate");
257                 final long authId = mService.authenticate(
258                         mToken, operationId, mServiceReceiver, options);
259                 if (cancel != null) {
260                     cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId));
261                 }
262             } catch (RemoteException e) {
263                 Slog.w(TAG, "Remote exception while authenticating: ", e);
264                 // Though this may not be a hardware issue, it will cause apps to give up or
265                 // try again later.
266                 callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
267                         getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
268                                 0 /* vendorCode */));
269             } finally {
270                 Trace.endSection();
271             }
272         }
273     }
274 
275     /**
276      * Uses the face hardware to detect for the presence of a face, without giving details about
277      * accept/reject/lockout.
278      * @hide
279      */
280     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
detectFace(@onNull CancellationSignal cancel, @NonNull FaceDetectionCallback callback, @NonNull FaceAuthenticateOptions options)281     public void detectFace(@NonNull CancellationSignal cancel,
282             @NonNull FaceDetectionCallback callback, @NonNull FaceAuthenticateOptions options) {
283         if (mService == null) {
284             return;
285         }
286 
287         if (cancel.isCanceled()) {
288             Slog.w(TAG, "Detection already cancelled");
289             return;
290         }
291 
292         options.setOpPackageName(mContext.getOpPackageName());
293         options.setAttributionTag(mContext.getAttributionTag());
294 
295         mFaceDetectionCallback = callback;
296 
297         try {
298             final long authId = mService.detectFace(mToken, mServiceReceiver, options);
299             cancel.setOnCancelListener(new OnFaceDetectionCancelListener(authId));
300         } catch (RemoteException e) {
301             Slog.w(TAG, "Remote exception when requesting finger detect", e);
302         }
303     }
304 
305     /**
306      * Defaults to {@link FaceManager#enroll(int, byte[], CancellationSignal, EnrollmentCallback,
307      * int[], Surface)} with {@code previewSurface} set to null.
308      *
309      * @see FaceManager#enroll(int, byte[], CancellationSignal, EnrollmentCallback, int[], Surface)
310      * @hide
311      */
312     @RequiresPermission(MANAGE_BIOMETRIC)
enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel, EnrollmentCallback callback, int[] disabledFeatures)313     public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
314             EnrollmentCallback callback, int[] disabledFeatures) {
315         enroll(userId, hardwareAuthToken, cancel, callback, disabledFeatures,
316                 null /* previewSurface */, false /* debugConsent */);
317     }
318 
319     /**
320      * Request face authentication enrollment. This call operates the face authentication hardware
321      * and starts capturing images. Progress will be indicated by callbacks to the
322      * {@link EnrollmentCallback} object. It terminates when
323      * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or
324      * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at
325      * which point the object is no longer valid. The operation can be canceled by using the
326      * provided cancel object.
327      *
328      * @param hardwareAuthToken a unique token provided by a recent creation or
329      *                          verification of device credentials (e.g. pin, pattern or password).
330      * @param cancel            an object that can be used to cancel enrollment
331      * @param userId            the user to whom this face will belong to
332      * @param callback          an object to receive enrollment events
333      * @param previewSurface    optional camera preview surface for a single-camera device.
334      *                          Must be null if not used.
335      * @param debugConsent      a feature flag that the user has consented to debug.
336      * @hide
337      */
338     @RequiresPermission(MANAGE_BIOMETRIC)
enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel, EnrollmentCallback callback, int[] disabledFeatures, @Nullable Surface previewSurface, boolean debugConsent)339     public void enroll(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
340             EnrollmentCallback callback, int[] disabledFeatures, @Nullable Surface previewSurface,
341             boolean debugConsent) {
342         if (callback == null) {
343             throw new IllegalArgumentException("Must supply an enrollment callback");
344         }
345 
346         if (cancel != null && cancel.isCanceled()) {
347             Slog.w(TAG, "enrollment already canceled");
348             return;
349         }
350 
351         if (hardwareAuthToken == null) {
352             callback.onEnrollmentError(FACE_ERROR_UNABLE_TO_PROCESS,
353                     getErrorString(mContext, FACE_ERROR_UNABLE_TO_PROCESS,
354                     0 /* vendorCode */));
355             return;
356         }
357 
358         if (getEnrolledFaces(userId).size()
359                 >= mContext.getResources().getInteger(R.integer.config_faceMaxTemplatesPerUser)) {
360             callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
361                     getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
362                             0 /* vendorCode */));
363             return;
364         }
365 
366         if (mService != null) {
367             try {
368                 mEnrollmentCallback = callback;
369                 Trace.beginSection("FaceManager#enroll");
370                 final long enrollId = mService.enroll(userId, mToken, hardwareAuthToken,
371                         mServiceReceiver, mContext.getOpPackageName(), disabledFeatures,
372                         previewSurface, debugConsent);
373                 if (cancel != null) {
374                     cancel.setOnCancelListener(new OnEnrollCancelListener(enrollId));
375                 }
376             } catch (RemoteException e) {
377                 Slog.w(TAG, "Remote exception in enroll: ", e);
378                 // Though this may not be a hardware issue, it will cause apps to give up or
379                 // try again later.
380                 callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
381                         getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
382                                 0 /* vendorCode */));
383             } finally {
384                 Trace.endSection();
385             }
386         }
387     }
388 
389     /**
390      * Request face authentication enrollment for a remote client, for example Android Auto.
391      * This call operates the face authentication hardware and starts capturing images.
392      * Progress will be indicated by callbacks to the
393      * {@link EnrollmentCallback} object. It terminates when
394      * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or
395      * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at
396      * which point the object is no longer valid. The operation can be canceled by using the
397      * provided cancel object.
398      *
399      * @param hardwareAuthToken    a unique token provided by a recent creation or verification of
400      *                 device credentials (e.g. pin, pattern or password).
401      * @param cancel   an object that can be used to cancel enrollment
402      * @param userId   the user to whom this face will belong to
403      * @param callback an object to receive enrollment events
404      * @hide
405      */
406     @RequiresPermission(MANAGE_BIOMETRIC)
enrollRemotely(int userId, byte[] hardwareAuthToken, CancellationSignal cancel, EnrollmentCallback callback, int[] disabledFeatures)407     public void enrollRemotely(int userId, byte[] hardwareAuthToken, CancellationSignal cancel,
408             EnrollmentCallback callback, int[] disabledFeatures) {
409         if (callback == null) {
410             throw new IllegalArgumentException("Must supply an enrollment callback");
411         }
412 
413         if (cancel != null && cancel.isCanceled()) {
414             Slog.w(TAG, "enrollRemotely is already canceled.");
415             return;
416         }
417 
418         if (mService != null) {
419             try {
420                 mEnrollmentCallback = callback;
421                 Trace.beginSection("FaceManager#enrollRemotely");
422                 final long enrolId = mService.enrollRemotely(userId, mToken, hardwareAuthToken,
423                         mServiceReceiver, mContext.getOpPackageName(), disabledFeatures);
424                 if (cancel != null) {
425                     cancel.setOnCancelListener(new OnEnrollCancelListener(enrolId));
426                 }
427             } catch (RemoteException e) {
428                 Slog.w(TAG, "Remote exception in enrollRemotely: ", e);
429                 // Though this may not be a hardware issue, it will cause apps to give up or
430                 // try again later.
431                 callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
432                         getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
433                                 0 /* vendorCode */));
434             } finally {
435                 Trace.endSection();
436             }
437         }
438     }
439 
440     /**
441      * Generates a unique random challenge in the TEE. A typical use case is to have it wrapped in a
442      * HardwareAuthenticationToken, minted by Gatekeeper upon PIN/Pattern/Password verification.
443      * The HardwareAuthenticationToken can then be sent to the biometric HAL together with a
444      * request to perform sensitive operation(s) (for example enroll or setFeature), represented
445      * by the challenge. Doing this ensures that a the sensitive operation cannot be performed
446      * unless the user has entered confirmed PIN/Pattern/Password.
447      *
448      * @see com.android.server.locksettings.LockSettingsService
449      *
450      * @hide
451      */
452     @RequiresPermission(MANAGE_BIOMETRIC)
generateChallenge(int sensorId, int userId, GenerateChallengeCallback callback)453     public void generateChallenge(int sensorId, int userId, GenerateChallengeCallback callback) {
454         if (mService != null) {
455             try {
456                 mGenerateChallengeCallback = callback;
457                 mService.generateChallenge(mToken, sensorId, userId, mServiceReceiver,
458                         mContext.getOpPackageName());
459             } catch (RemoteException e) {
460                 throw e.rethrowFromSystemServer();
461             }
462         }
463     }
464 
465     /**
466      * Same as {@link #generateChallenge(int, int, GenerateChallengeCallback)}, but assumes the
467      * first enumerated sensor.
468      *
469      * @hide
470      */
471     @RequiresPermission(MANAGE_BIOMETRIC)
generateChallenge(int userId, GenerateChallengeCallback callback)472     public void generateChallenge(int userId, GenerateChallengeCallback callback) {
473         final List<FaceSensorPropertiesInternal> faceSensorProperties =
474                 getSensorPropertiesInternal();
475         if (faceSensorProperties.isEmpty()) {
476             Slog.e(TAG, "No sensors");
477             return;
478         }
479 
480         final int sensorId = faceSensorProperties.get(0).sensorId;
481         generateChallenge(sensorId, userId, callback);
482     }
483 
484     /**
485      * Invalidates the current challenge.
486      *
487      * @hide
488      */
489     @RequiresPermission(MANAGE_BIOMETRIC)
revokeChallenge(int sensorId, int userId, long challenge)490     public void revokeChallenge(int sensorId, int userId, long challenge) {
491         if (mService != null) {
492             try {
493                 mService.revokeChallenge(mToken, sensorId, userId,
494                         mContext.getOpPackageName(), challenge);
495             } catch (RemoteException e) {
496                 throw e.rethrowFromSystemServer();
497             }
498         }
499     }
500 
501     /**
502      * Reset the lockout when user authenticates with strong auth (e.g. PIN, pattern or password)
503      *
504      * @param sensorId Sensor ID that this operation takes effect for
505      * @param userId User ID that this operation takes effect for.
506      * @param hardwareAuthToken An opaque token returned by password confirmation.
507      * @hide
508      */
509     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
resetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken)510     public void resetLockout(int sensorId, int userId, @Nullable byte[] hardwareAuthToken) {
511         if (mService != null) {
512             try {
513                 mService.resetLockout(mToken, sensorId, userId, hardwareAuthToken,
514                         mContext.getOpPackageName());
515             } catch (RemoteException e) {
516                 throw e.rethrowFromSystemServer();
517             }
518         }
519     }
520 
521     /**
522      * @hide
523      */
524     @RequiresPermission(MANAGE_BIOMETRIC)
setFeature(int userId, int feature, boolean enabled, byte[] hardwareAuthToken, SetFeatureCallback callback)525     public void setFeature(int userId, int feature, boolean enabled, byte[] hardwareAuthToken,
526             SetFeatureCallback callback) {
527         if (mService != null) {
528             try {
529                 mSetFeatureCallback = callback;
530                 mService.setFeature(mToken, userId, feature, enabled, hardwareAuthToken,
531                         mServiceReceiver, mContext.getOpPackageName());
532             } catch (RemoteException e) {
533                 throw e.rethrowFromSystemServer();
534             }
535         }
536     }
537 
538     /**
539      * @hide
540      */
541     @RequiresPermission(MANAGE_BIOMETRIC)
getFeature(int userId, int feature, GetFeatureCallback callback)542     public void getFeature(int userId, int feature, GetFeatureCallback callback) {
543         if (mService != null) {
544             try {
545                 mGetFeatureCallback = callback;
546                 mService.getFeature(mToken, userId, feature, mServiceReceiver,
547                         mContext.getOpPackageName());
548             } catch (RemoteException e) {
549                 throw e.rethrowFromSystemServer();
550             }
551         }
552     }
553 
554     /**
555      * Remove given face template from face hardware and/or protected storage.
556      *
557      * @param face     the face item to remove
558      * @param userId   the user who this face belongs to
559      * @param callback an optional callback to verify that face templates have been
560      *                 successfully removed. May be null if no callback is required.
561      * @hide
562      */
563     @RequiresPermission(MANAGE_BIOMETRIC)
remove(Face face, int userId, RemovalCallback callback)564     public void remove(Face face, int userId, RemovalCallback callback) {
565         if (mService != null) {
566             try {
567                 mRemovalCallback = callback;
568                 mRemovalFace = face;
569                 mService.remove(mToken, face.getBiometricId(), userId, mServiceReceiver,
570                         mContext.getOpPackageName());
571             } catch (RemoteException e) {
572                 throw e.rethrowFromSystemServer();
573             }
574         }
575     }
576 
577     /**
578      * Removes all face templates for the given user.
579      * @hide
580      */
581     @RequiresPermission(MANAGE_BIOMETRIC)
removeAll(int userId, @NonNull RemovalCallback callback)582     public void removeAll(int userId, @NonNull RemovalCallback callback) {
583         if (mService != null) {
584             try {
585                 mRemovalCallback = callback;
586                 mService.removeAll(mToken, userId, mServiceReceiver, mContext.getOpPackageName());
587             } catch (RemoteException e) {
588                 throw e.rethrowFromSystemServer();
589             }
590         }
591     }
592 
593     /**
594      * Obtain the enrolled face template.
595      *
596      * @return the current face item
597      * @hide
598      */
599     @RequiresPermission(MANAGE_BIOMETRIC)
getEnrolledFaces(int userId)600     public List<Face> getEnrolledFaces(int userId) {
601         final List<FaceSensorPropertiesInternal> faceSensorProperties =
602                 getSensorPropertiesInternal();
603         if (faceSensorProperties.isEmpty()) {
604             Slog.e(TAG, "No sensors");
605             return new ArrayList<>();
606         }
607 
608         if (mService != null) {
609             try {
610                 return mService.getEnrolledFaces(faceSensorProperties.get(0).sensorId, userId,
611                         mContext.getOpPackageName());
612             } catch (RemoteException e) {
613                 throw e.rethrowFromSystemServer();
614             }
615         }
616         return null;
617     }
618 
619     /**
620      * Obtain the enrolled face template.
621      *
622      * @return the current face item
623      * @hide
624      */
625     @RequiresPermission(MANAGE_BIOMETRIC)
getEnrolledFaces()626     public List<Face> getEnrolledFaces() {
627         return getEnrolledFaces(UserHandle.myUserId());
628     }
629 
630     /**
631      * Determine if there is a face enrolled.
632      *
633      * @return true if a face is enrolled, false otherwise
634      * @hide
635      */
636     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
hasEnrolledTemplates()637     public boolean hasEnrolledTemplates() {
638         return hasEnrolledTemplates(UserHandle.myUserId());
639     }
640 
641     /**
642      * @hide
643      */
644     @RequiresPermission(allOf = {
645             USE_BIOMETRIC_INTERNAL,
646             INTERACT_ACROSS_USERS})
hasEnrolledTemplates(int userId)647     public boolean hasEnrolledTemplates(int userId) {
648         final List<FaceSensorPropertiesInternal> faceSensorProperties =
649                 getSensorPropertiesInternal();
650         if (faceSensorProperties.isEmpty()) {
651             Slog.e(TAG, "No sensors");
652             return false;
653         }
654 
655         if (mService != null) {
656             try {
657                 return mService.hasEnrolledFaces(faceSensorProperties.get(0).sensorId, userId,
658                         mContext.getOpPackageName());
659             } catch (RemoteException e) {
660                 throw e.rethrowFromSystemServer();
661             }
662         }
663         return false;
664     }
665 
666     /**
667      * Determine if face authentication sensor hardware is present and functional.
668      *
669      * @return true if hardware is present and functional, false otherwise.
670      * @hide
671      */
672     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
isHardwareDetected()673     public boolean isHardwareDetected() {
674         final List<FaceSensorPropertiesInternal> faceSensorProperties =
675                 getSensorPropertiesInternal();
676         if (faceSensorProperties.isEmpty()) {
677             Slog.e(TAG, "No sensors");
678             return false;
679         }
680 
681         if (mService != null) {
682             try {
683                 return mService.isHardwareDetected(faceSensorProperties.get(0).sensorId,
684                         mContext.getOpPackageName());
685             } catch (RemoteException e) {
686                 throw e.rethrowFromSystemServer();
687             }
688         } else {
689             Slog.w(TAG, "isFaceHardwareDetected(): Service not connected!");
690         }
691         return false;
692     }
693 
694     /**
695      * Retrieves a list of properties for all face authentication sensors on the device.
696      * @hide
697      */
698     @NonNull
getSensorProperties()699     public List<FaceSensorProperties> getSensorProperties() {
700         final List<FaceSensorProperties> properties = new ArrayList<>();
701         final List<FaceSensorPropertiesInternal> internalProperties
702                 = getSensorPropertiesInternal();
703         for (FaceSensorPropertiesInternal internalProp : internalProperties) {
704             properties.add(FaceSensorProperties.from(internalProp));
705         }
706         return properties;
707     }
708 
709     /**
710      * Get statically configured sensor properties.
711      * @hide
712      */
713     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
714     @NonNull
getSensorPropertiesInternal()715     public List<FaceSensorPropertiesInternal> getSensorPropertiesInternal() {
716         try {
717             if (!mProps.isEmpty() || mService == null) {
718                 return mProps;
719             }
720             return mService.getSensorPropertiesInternal(mContext.getOpPackageName());
721         } catch (RemoteException e) {
722             e.rethrowFromSystemServer();
723         }
724         return mProps;
725     }
726 
727     /**
728      * Forwards BiometricStateListener to FaceService.
729      *
730      * @param listener new BiometricStateListener being added
731      * @hide
732      */
registerBiometricStateListener(@onNull BiometricStateListener listener)733     public void registerBiometricStateListener(@NonNull BiometricStateListener listener) {
734         try {
735             mService.registerBiometricStateListener(listener);
736         } catch (RemoteException e) {
737             throw e.rethrowFromSystemServer();
738         }
739     }
740 
741     /**
742      * Adds a callback that gets called when the service registers all of the face
743      * authenticators (HALs).
744      *
745      * If the face authenticators are already registered when the callback is added, the
746      * callback is invoked immediately.
747      *
748      * The callback is automatically removed after it's invoked.
749      *
750      * @hide
751      */
752     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
addAuthenticatorsRegisteredCallback( IFaceAuthenticatorsRegisteredCallback callback)753     public void addAuthenticatorsRegisteredCallback(
754             IFaceAuthenticatorsRegisteredCallback callback) {
755         if (mService != null) {
756             try {
757                 mService.addAuthenticatorsRegisteredCallback(callback);
758             } catch (RemoteException e) {
759                 throw e.rethrowFromSystemServer();
760             }
761         } else {
762             Slog.w(TAG, "addAuthenticatorsRegisteredCallback(): Service not connected!");
763         }
764     }
765 
766     /**
767      * @hide
768      */
769     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
770     @BiometricConstants.LockoutMode
getLockoutModeForUser(int sensorId, int userId)771     public int getLockoutModeForUser(int sensorId, int userId) {
772         if (mService != null) {
773             try {
774                 return mService.getLockoutModeForUser(sensorId, userId);
775             } catch (RemoteException e) {
776                 e.rethrowFromSystemServer();
777             }
778         }
779         return BIOMETRIC_LOCKOUT_NONE;
780     }
781 
782     /**
783      * @hide
784      */
785     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
addLockoutResetCallback(final LockoutResetCallback callback)786     public void addLockoutResetCallback(final LockoutResetCallback callback) {
787         if (mService != null) {
788             try {
789                 final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
790                 mService.addLockoutResetCallback(
791                         new IBiometricServiceLockoutResetCallback.Stub() {
792 
793                             @Override
794                             public void onLockoutReset(int sensorId, IRemoteCallback serverCallback)
795                                     throws RemoteException {
796                                 try {
797                                     final PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
798                                             PowerManager.PARTIAL_WAKE_LOCK,
799                                             "faceLockoutResetCallback");
800                                     wakeLock.acquire();
801                                     mHandler.post(() -> {
802                                         try {
803                                             callback.onLockoutReset(sensorId);
804                                         } finally {
805                                             wakeLock.release();
806                                         }
807                                     });
808                                 } finally {
809                                     serverCallback.sendResult(null /* data */);
810                                 }
811                             }
812                         }, mContext.getOpPackageName());
813             } catch (RemoteException e) {
814                 throw e.rethrowFromSystemServer();
815             }
816         } else {
817             Slog.w(TAG, "addLockoutResetCallback(): Service not connected!");
818         }
819     }
820 
821     /**
822      * Schedules a watchdog.
823      *
824      * @hide
825      */
826     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
scheduleWatchdog()827     public void scheduleWatchdog() {
828         try {
829             mService.scheduleWatchdog();
830         } catch (RemoteException e) {
831             throw e.rethrowFromSystemServer();
832         }
833     }
834 
cancelEnrollment(long requestId)835     private void cancelEnrollment(long requestId) {
836         if (mService != null) {
837             try {
838                 mService.cancelEnrollment(mToken, requestId);
839             } catch (RemoteException e) {
840                 throw e.rethrowFromSystemServer();
841             }
842         }
843     }
844 
cancelAuthentication(long requestId)845     private void cancelAuthentication(long requestId) {
846         if (mService != null) {
847             try {
848                 mService.cancelAuthentication(mToken, mContext.getOpPackageName(), requestId);
849             } catch (RemoteException e) {
850                 throw e.rethrowFromSystemServer();
851             }
852         }
853     }
854 
cancelFaceDetect(long requestId)855     private void cancelFaceDetect(long requestId) {
856         if (mService == null) {
857             return;
858         }
859 
860         try {
861             mService.cancelFaceDetect(mToken, mContext.getOpPackageName(), requestId);
862         } catch (RemoteException e) {
863             throw e.rethrowFromSystemServer();
864         }
865     }
866 
867     /**
868      * @hide
869      */
getErrorString(Context context, int errMsg, int vendorCode)870     public static String getErrorString(Context context, int errMsg, int vendorCode) {
871         switch (errMsg) {
872             case FACE_ERROR_HW_UNAVAILABLE:
873                 return context.getString(
874                         com.android.internal.R.string.face_error_hw_not_available);
875             case FACE_ERROR_UNABLE_TO_PROCESS:
876                 return context.getString(
877                         com.android.internal.R.string.face_error_unable_to_process);
878             case FACE_ERROR_TIMEOUT:
879                 return context.getString(com.android.internal.R.string.face_error_timeout);
880             case FACE_ERROR_NO_SPACE:
881                 return context.getString(com.android.internal.R.string.face_error_no_space);
882             case FACE_ERROR_CANCELED:
883                 return context.getString(com.android.internal.R.string.face_error_canceled);
884             case FACE_ERROR_LOCKOUT:
885                 return context.getString(com.android.internal.R.string.face_error_lockout);
886             case FACE_ERROR_LOCKOUT_PERMANENT:
887                 return context.getString(
888                         com.android.internal.R.string.face_error_lockout_permanent);
889             case FACE_ERROR_USER_CANCELED:
890                 return context.getString(com.android.internal.R.string.face_error_user_canceled);
891             case FACE_ERROR_NOT_ENROLLED:
892                 return context.getString(com.android.internal.R.string.face_error_not_enrolled);
893             case FACE_ERROR_HW_NOT_PRESENT:
894                 return context.getString(com.android.internal.R.string.face_error_hw_not_present);
895             case BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED:
896                 return context.getString(
897                         com.android.internal.R.string.face_error_security_update_required);
898             case BIOMETRIC_ERROR_RE_ENROLL:
899                 return context.getString(
900                         com.android.internal.R.string.face_recalibrate_notification_content);
901             case FACE_ERROR_VENDOR: {
902                 String[] msgArray = context.getResources().getStringArray(
903                         com.android.internal.R.array.face_error_vendor);
904                 if (vendorCode < msgArray.length) {
905                     return msgArray[vendorCode];
906                 }
907             }
908         }
909 
910         // This is used as a last resort in case a vendor string is missing
911         // It should not happen for anything other than FACE_ERROR_VENDOR, but
912         // warn and use the default if all else fails.
913         Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode);
914         return context.getString(
915                 com.android.internal.R.string.face_error_vendor_unknown);
916     }
917 
918     /**
919      * Used so BiometricPrompt can map the face ones onto existing public constants.
920      * @hide
921      */
getMappedAcquiredInfo(int acquireInfo, int vendorCode)922     public static int getMappedAcquiredInfo(int acquireInfo, int vendorCode) {
923         switch (acquireInfo) {
924             case FACE_ACQUIRED_GOOD:
925                 return BiometricConstants.BIOMETRIC_ACQUIRED_GOOD;
926             case FACE_ACQUIRED_INSUFFICIENT:
927             case FACE_ACQUIRED_TOO_BRIGHT:
928             case FACE_ACQUIRED_TOO_DARK:
929                 return BiometricConstants.BIOMETRIC_ACQUIRED_INSUFFICIENT;
930             case FACE_ACQUIRED_TOO_CLOSE:
931             case FACE_ACQUIRED_TOO_FAR:
932             case FACE_ACQUIRED_TOO_HIGH:
933             case FACE_ACQUIRED_TOO_LOW:
934             case FACE_ACQUIRED_TOO_RIGHT:
935             case FACE_ACQUIRED_TOO_LEFT:
936                 return BiometricConstants.BIOMETRIC_ACQUIRED_PARTIAL;
937             case FACE_ACQUIRED_POOR_GAZE:
938             case FACE_ACQUIRED_NOT_DETECTED:
939             case FACE_ACQUIRED_TOO_MUCH_MOTION:
940             case FACE_ACQUIRED_RECALIBRATE:
941                 return BiometricConstants.BIOMETRIC_ACQUIRED_INSUFFICIENT;
942             case FACE_ACQUIRED_VENDOR:
943                 return BiometricConstants.BIOMETRIC_ACQUIRED_VENDOR_BASE + vendorCode;
944             default:
945                 return BiometricConstants.BIOMETRIC_ACQUIRED_GOOD;
946         }
947     }
948 
949     /**
950      * Container for callback data from {@link FaceManager#authenticate(CryptoObject,
951      * CancellationSignal, int, AuthenticationCallback, Handler)}.
952      * @hide
953      */
954     public static class AuthenticationResult {
955         private final Face mFace;
956         private final CryptoObject mCryptoObject;
957         private final int mUserId;
958         private final boolean mIsStrongBiometric;
959 
960         /**
961          * Authentication result
962          *
963          * @param crypto the crypto object
964          * @param face   the recognized face data, if allowed.
965          * @hide
966          */
AuthenticationResult(CryptoObject crypto, Face face, int userId, boolean isStrongBiometric)967         public AuthenticationResult(CryptoObject crypto, Face face, int userId,
968                 boolean isStrongBiometric) {
969             mCryptoObject = crypto;
970             mFace = face;
971             mUserId = userId;
972             mIsStrongBiometric = isStrongBiometric;
973         }
974 
975         /**
976          * Obtain the crypto object associated with this transaction
977          *
978          * @return crypto object provided to {@link FaceManager#authenticate
979          * (CryptoObject,
980          * CancellationSignal, int, AuthenticationCallback, Handler)}.
981          */
getCryptoObject()982         public CryptoObject getCryptoObject() {
983             return mCryptoObject;
984         }
985 
986         /**
987          * Obtain the Face associated with this operation. Applications are strongly
988          * discouraged from associating specific faces with specific applications or operations.
989          *
990          * @hide
991          */
getFace()992         public Face getFace() {
993             return mFace;
994         }
995 
996         /**
997          * Obtain the userId for which this face was authenticated.
998          *
999          * @hide
1000          */
getUserId()1001         public int getUserId() {
1002             return mUserId;
1003         }
1004 
1005         /**
1006          * Check whether the strength of the face modality associated with this operation is strong
1007          * (i.e. not weak or convenience).
1008          *
1009          * @hide
1010          */
isStrongBiometric()1011         public boolean isStrongBiometric() {
1012             return mIsStrongBiometric;
1013         }
1014     }
1015 
1016     /**
1017      * Callback structure provided to {@link FaceManager#authenticate(CryptoObject,
1018      * CancellationSignal, int, AuthenticationCallback, Handler)}. Users of {@link
1019      * FaceManager#authenticate(CryptoObject, CancellationSignal,
1020      * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening
1021      * to face events.
1022      * @hide
1023      */
1024     public abstract static class AuthenticationCallback
1025             extends BiometricAuthenticator.AuthenticationCallback {
1026 
1027         /**
1028          * Called when an unrecoverable error has been encountered and the operation is complete.
1029          * No further callbacks will be made on this object.
1030          *
1031          * @param errorCode An integer identifying the error message
1032          * @param errString A human-readable error string that can be shown in UI
1033          */
onAuthenticationError(int errorCode, CharSequence errString)1034         public void onAuthenticationError(int errorCode, CharSequence errString) {
1035         }
1036 
1037         /**
1038          * Called when a recoverable error has been encountered during authentication. The help
1039          * string is provided to give the user guidance for what went wrong, such as
1040          * "Sensor dirty, please clean it."
1041          *
1042          * @param helpCode   An integer identifying the error message
1043          * @param helpString A human-readable string that can be shown in UI
1044          */
onAuthenticationHelp(int helpCode, CharSequence helpString)1045         public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
1046         }
1047 
1048         /**
1049          * Called when a face is recognized.
1050          *
1051          * @param result An object containing authentication-related data
1052          */
onAuthenticationSucceeded(AuthenticationResult result)1053         public void onAuthenticationSucceeded(AuthenticationResult result) {
1054         }
1055 
1056         /**
1057          * Called when a face is detected but not recognized.
1058          */
onAuthenticationFailed()1059         public void onAuthenticationFailed() {
1060         }
1061 
1062         /**
1063          * Called when a face image has been acquired, but wasn't processed yet.
1064          *
1065          * @param acquireInfo one of FACE_ACQUIRED_* constants
1066          * @hide
1067          */
onAuthenticationAcquired(int acquireInfo)1068         public void onAuthenticationAcquired(int acquireInfo) {
1069         }
1070     }
1071 
1072     /**
1073      * @hide
1074      */
1075     public interface FaceDetectionCallback {
onFaceDetected(int sensorId, int userId, boolean isStrongBiometric)1076         void onFaceDetected(int sensorId, int userId, boolean isStrongBiometric);
1077     }
1078 
1079     /**
1080      * Callback structure provided to {@link FaceManager#enroll(long,
1081      * EnrollmentCallback, CancellationSignal, int). Users of {@link #FaceAuthenticationManager()}
1082      * must provide an implementation of this to {@link FaceManager#enroll(long,
1083      * CancellationSignal, int, EnrollmentCallback) for listening to face enrollment events.
1084      *
1085      * @hide
1086      */
1087     public abstract static class EnrollmentCallback {
1088 
1089         /**
1090          * Called when an unrecoverable error has been encountered and the operation is complete.
1091          * No further callbacks will be made on this object.
1092          *
1093          * @param errMsgId  An integer identifying the error message
1094          * @param errString A human-readable error string that can be shown in UI
1095          */
onEnrollmentError(int errMsgId, CharSequence errString)1096         public void onEnrollmentError(int errMsgId, CharSequence errString) {
1097         }
1098 
1099         /**
1100          * Called when a recoverable error has been encountered during enrollment. The help
1101          * string is provided to give the user guidance for what went wrong, such as
1102          * "Image too dark, uncover light source" or what they need to do next, such as
1103          * "Rotate face up / down."
1104          *
1105          * @param helpMsgId  An integer identifying the error message
1106          * @param helpString A human-readable string that can be shown in UI
1107          */
onEnrollmentHelp(int helpMsgId, CharSequence helpString)1108         public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
1109         }
1110 
1111         /**
1112          * Called each time a single frame is captured during enrollment.
1113          *
1114          * <p>For older, non-AIDL implementations, only {@code helpCode} and {@code helpMessage} are
1115          * supported. Sensible default values will be provided for all other arguments.
1116          *
1117          * @param helpCode    An integer identifying the capture status for this frame.
1118          * @param helpMessage A human-readable help string that can be shown in UI.
1119          * @param cell        The cell captured during this frame of enrollment, if any.
1120          * @param stage       An integer representing the current stage of enrollment.
1121          * @param pan         The horizontal pan of the detected face. Values in the range [-1, 1]
1122          *                    indicate a good capture.
1123          * @param tilt        The vertical tilt of the detected face. Values in the range [-1, 1]
1124          *                    indicate a good capture.
1125          * @param distance    The distance of the detected face from the device. Values in
1126          *                    the range [-1, 1] indicate a good capture.
1127          */
onEnrollmentFrame( int helpCode, @Nullable CharSequence helpMessage, @Nullable FaceEnrollCell cell, @FaceEnrollStages.FaceEnrollStage int stage, float pan, float tilt, float distance)1128         public void onEnrollmentFrame(
1129                 int helpCode,
1130                 @Nullable CharSequence helpMessage,
1131                 @Nullable FaceEnrollCell cell,
1132                 @FaceEnrollStages.FaceEnrollStage int stage,
1133                 float pan,
1134                 float tilt,
1135                 float distance) {
1136             onEnrollmentHelp(helpCode, helpMessage);
1137         }
1138 
1139         /**
1140          * Called as each enrollment step progresses. Enrollment is considered complete when
1141          * remaining reaches 0. This function will not be called if enrollment fails. See
1142          * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)}
1143          *
1144          * @param remaining The number of remaining steps
1145          */
onEnrollmentProgress(int remaining)1146         public void onEnrollmentProgress(int remaining) {
1147         }
1148     }
1149 
1150     /**
1151      * Callback structure provided to {@link #remove}. Users of {@link FaceManager}
1152      * may
1153      * optionally provide an implementation of this to
1154      * {@link #remove(Face, int, RemovalCallback)} for listening to face template
1155      * removal events.
1156      *
1157      * @hide
1158      */
1159     public abstract static class RemovalCallback {
1160 
1161         /**
1162          * Called when the given face can't be removed.
1163          *
1164          * @param face      The face that the call attempted to remove
1165          * @param errMsgId  An associated error message id
1166          * @param errString An error message indicating why the face id can't be removed
1167          */
onRemovalError(Face face, int errMsgId, CharSequence errString)1168         public void onRemovalError(Face face, int errMsgId, CharSequence errString) {
1169         }
1170 
1171         /**
1172          * Called when a given face is successfully removed.
1173          *
1174          * @param face The face template that was removed.
1175          */
onRemovalSucceeded(@ullable Face face, int remaining)1176         public void onRemovalSucceeded(@Nullable Face face, int remaining) {
1177         }
1178     }
1179 
1180     /**
1181      * @hide
1182      */
1183     public abstract static class LockoutResetCallback {
1184 
1185         /**
1186          * Called when lockout period expired and clients are allowed to listen for face
1187          * authentication
1188          * again.
1189          */
onLockoutReset(int sensorId)1190         public void onLockoutReset(int sensorId) {
1191         }
1192     }
1193 
1194     /**
1195      * @hide
1196      */
1197     public abstract static class SetFeatureCallback {
onCompleted(boolean success, int feature)1198         public abstract void onCompleted(boolean success, int feature);
1199     }
1200 
1201     /**
1202      * @hide
1203      */
1204     public abstract static class GetFeatureCallback {
onCompleted(boolean success, int[] features, boolean[] featureState)1205         public abstract void onCompleted(boolean success, int[] features, boolean[] featureState);
1206     }
1207 
1208     /**
1209      * Callback structure provided to {@link #generateChallenge(int, int,
1210      * GenerateChallengeCallback)}.
1211      *
1212      * @hide
1213      */
1214     public interface GenerateChallengeCallback {
1215         /**
1216          * Invoked when a challenge has been generated.
1217          */
onGenerateChallengeResult(int sensorId, int userId, long challenge)1218         void onGenerateChallengeResult(int sensorId, int userId, long challenge);
1219     }
1220 
1221     private class OnEnrollCancelListener implements OnCancelListener {
1222         private final long mAuthRequestId;
1223 
OnEnrollCancelListener(long id)1224         private OnEnrollCancelListener(long id) {
1225             mAuthRequestId = id;
1226         }
1227 
1228         @Override
onCancel()1229         public void onCancel() {
1230             Slog.d(TAG, "Cancel face enrollment requested for: " + mAuthRequestId);
1231             cancelEnrollment(mAuthRequestId);
1232         }
1233     }
1234 
1235     private class OnAuthenticationCancelListener implements OnCancelListener {
1236         private final long mAuthRequestId;
1237 
OnAuthenticationCancelListener(long id)1238         OnAuthenticationCancelListener(long id) {
1239             mAuthRequestId = id;
1240         }
1241 
1242         @Override
onCancel()1243         public void onCancel() {
1244             Slog.d(TAG, "Cancel face authentication requested for: " + mAuthRequestId);
1245             cancelAuthentication(mAuthRequestId);
1246         }
1247     }
1248 
1249     private class OnFaceDetectionCancelListener implements OnCancelListener {
1250         private final long mAuthRequestId;
1251 
OnFaceDetectionCancelListener(long id)1252         OnFaceDetectionCancelListener(long id) {
1253             mAuthRequestId = id;
1254         }
1255 
1256         @Override
onCancel()1257         public void onCancel() {
1258             Slog.d(TAG, "Cancel face detect requested for: " + mAuthRequestId);
1259             cancelFaceDetect(mAuthRequestId);
1260         }
1261     }
1262 
1263     private class MyHandler extends Handler {
MyHandler(Context context)1264         private MyHandler(Context context) {
1265             super(context.getMainLooper());
1266         }
1267 
MyHandler(Looper looper)1268         private MyHandler(Looper looper) {
1269             super(looper);
1270         }
1271 
1272         @Override
handleMessage(android.os.Message msg)1273         public void handleMessage(android.os.Message msg) {
1274             Trace.beginSection("FaceManager#handleMessage: " + Integer.toString(msg.what));
1275             switch (msg.what) {
1276                 case MSG_ENROLL_RESULT:
1277                     sendEnrollResult((Face) msg.obj, msg.arg1 /* remaining */);
1278                     break;
1279                 case MSG_ACQUIRED:
1280                     sendAcquiredResult(msg.arg1 /* acquire info */, msg.arg2 /* vendorCode */);
1281                     break;
1282                 case MSG_AUTHENTICATION_SUCCEEDED:
1283                     sendAuthenticatedSucceeded((Face) msg.obj, msg.arg1 /* userId */,
1284                             msg.arg2 == 1 /* isStrongBiometric */);
1285                     break;
1286                 case MSG_AUTHENTICATION_FAILED:
1287                     sendAuthenticatedFailed();
1288                     break;
1289                 case MSG_ERROR:
1290                     sendErrorResult(msg.arg1 /* errMsgId */, msg.arg2 /* vendorCode */);
1291                     break;
1292                 case MSG_REMOVED:
1293                     sendRemovedResult((Face) msg.obj, msg.arg1 /* remaining */);
1294                     break;
1295                 case MSG_SET_FEATURE_COMPLETED:
1296                     sendSetFeatureCompleted((boolean) msg.obj /* success */,
1297                             msg.arg1 /* feature */);
1298                     break;
1299                 case MSG_GET_FEATURE_COMPLETED:
1300                     SomeArgs args = (SomeArgs) msg.obj;
1301                     sendGetFeatureCompleted((boolean) args.arg1 /* success */,
1302                             (int[]) args.arg2 /* features */,
1303                             (boolean[]) args.arg3 /* featureState */);
1304                     args.recycle();
1305                     break;
1306                 case MSG_CHALLENGE_GENERATED:
1307                     sendChallengeGenerated(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
1308                             (long) msg.obj /* challenge */);
1309                     break;
1310                 case MSG_FACE_DETECTED:
1311                     sendFaceDetected(msg.arg1 /* sensorId */, msg.arg2 /* userId */,
1312                             (boolean) msg.obj /* isStrongBiometric */);
1313                     break;
1314                 case MSG_AUTHENTICATION_FRAME:
1315                     sendAuthenticationFrame((FaceAuthenticationFrame) msg.obj /* frame */);
1316                     break;
1317                 case MSG_ENROLLMENT_FRAME:
1318                     sendEnrollmentFrame((FaceEnrollFrame) msg.obj /* frame */);
1319                     break;
1320                 default:
1321                     Slog.w(TAG, "Unknown message: " + msg.what);
1322             }
1323             Trace.endSection();
1324         }
1325     }
1326 
sendSetFeatureCompleted(boolean success, int feature)1327     private void sendSetFeatureCompleted(boolean success, int feature) {
1328         if (mSetFeatureCallback == null) {
1329             return;
1330         }
1331         mSetFeatureCallback.onCompleted(success, feature);
1332     }
1333 
sendGetFeatureCompleted(boolean success, int[] features, boolean[] featureState)1334     private void sendGetFeatureCompleted(boolean success, int[] features, boolean[] featureState) {
1335         if (mGetFeatureCallback == null) {
1336             return;
1337         }
1338         mGetFeatureCallback.onCompleted(success, features, featureState);
1339     }
1340 
sendChallengeGenerated(int sensorId, int userId, long challenge)1341     private void sendChallengeGenerated(int sensorId, int userId, long challenge) {
1342         if (mGenerateChallengeCallback == null) {
1343             return;
1344         }
1345         mGenerateChallengeCallback.onGenerateChallengeResult(sensorId, userId, challenge);
1346     }
1347 
sendFaceDetected(int sensorId, int userId, boolean isStrongBiometric)1348     private void sendFaceDetected(int sensorId, int userId, boolean isStrongBiometric) {
1349         if (mFaceDetectionCallback == null) {
1350             Slog.e(TAG, "sendFaceDetected, callback null");
1351             return;
1352         }
1353         mFaceDetectionCallback.onFaceDetected(sensorId, userId, isStrongBiometric);
1354     }
1355 
sendRemovedResult(Face face, int remaining)1356     private void sendRemovedResult(Face face, int remaining) {
1357         if (mRemovalCallback == null) {
1358             return;
1359         }
1360         mRemovalCallback.onRemovalSucceeded(face, remaining);
1361     }
1362 
sendErrorResult(int errMsgId, int vendorCode)1363     private void sendErrorResult(int errMsgId, int vendorCode) {
1364         // emulate HAL 2.1 behavior and send real errMsgId
1365         final int clientErrMsgId = errMsgId == FACE_ERROR_VENDOR
1366                 ? (vendorCode + FACE_ERROR_VENDOR_BASE) : errMsgId;
1367         if (mEnrollmentCallback != null) {
1368             mEnrollmentCallback.onEnrollmentError(clientErrMsgId,
1369                     getErrorString(mContext, errMsgId, vendorCode));
1370         } else if (mAuthenticationCallback != null) {
1371             mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
1372                     getErrorString(mContext, errMsgId, vendorCode));
1373         } else if (mRemovalCallback != null) {
1374             mRemovalCallback.onRemovalError(mRemovalFace, clientErrMsgId,
1375                     getErrorString(mContext, errMsgId, vendorCode));
1376         }
1377     }
1378 
sendEnrollResult(Face face, int remaining)1379     private void sendEnrollResult(Face face, int remaining) {
1380         if (mEnrollmentCallback != null) {
1381             mEnrollmentCallback.onEnrollmentProgress(remaining);
1382         }
1383     }
1384 
sendAuthenticatedSucceeded(Face face, int userId, boolean isStrongBiometric)1385     private void sendAuthenticatedSucceeded(Face face, int userId, boolean isStrongBiometric) {
1386         if (mAuthenticationCallback != null) {
1387             final AuthenticationResult result =
1388                     new AuthenticationResult(mCryptoObject, face, userId, isStrongBiometric);
1389             mAuthenticationCallback.onAuthenticationSucceeded(result);
1390         }
1391     }
1392 
sendAuthenticatedFailed()1393     private void sendAuthenticatedFailed() {
1394         if (mAuthenticationCallback != null) {
1395             mAuthenticationCallback.onAuthenticationFailed();
1396         }
1397     }
1398 
sendAcquiredResult(int acquireInfo, int vendorCode)1399     private void sendAcquiredResult(int acquireInfo, int vendorCode) {
1400         if (mAuthenticationCallback != null) {
1401             final FaceAuthenticationFrame frame = new FaceAuthenticationFrame(
1402                     new FaceDataFrame(acquireInfo, vendorCode));
1403             sendAuthenticationFrame(frame);
1404         } else if (mEnrollmentCallback != null) {
1405             final FaceEnrollFrame frame = new FaceEnrollFrame(
1406                     null /* cell */,
1407                     FaceEnrollStages.UNKNOWN,
1408                     new FaceDataFrame(acquireInfo, vendorCode));
1409             sendEnrollmentFrame(frame);
1410         }
1411     }
1412 
sendAuthenticationFrame(@ullable FaceAuthenticationFrame frame)1413     private void sendAuthenticationFrame(@Nullable FaceAuthenticationFrame frame) {
1414         if (frame == null) {
1415             Slog.w(TAG, "Received null authentication frame");
1416         } else if (mAuthenticationCallback != null) {
1417             // TODO(b/178414967): Send additional frame data to callback
1418             final int acquireInfo = frame.getData().getAcquiredInfo();
1419             final int vendorCode = frame.getData().getVendorCode();
1420             final int helpCode = getHelpCode(acquireInfo, vendorCode);
1421             final String helpMessage = getAuthHelpMessage(mContext, acquireInfo, vendorCode);
1422             mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
1423 
1424             // Ensure that only non-null help messages are sent.
1425             if (helpMessage != null) {
1426                 mAuthenticationCallback.onAuthenticationHelp(helpCode, helpMessage);
1427             }
1428         }
1429     }
1430 
sendEnrollmentFrame(@ullable FaceEnrollFrame frame)1431     private void sendEnrollmentFrame(@Nullable FaceEnrollFrame frame) {
1432         if (frame == null) {
1433             Slog.w(TAG, "Received null enrollment frame");
1434         } else if (mEnrollmentCallback != null) {
1435             final FaceDataFrame data = frame.getData();
1436             final int acquireInfo = data.getAcquiredInfo();
1437             final int vendorCode = data.getVendorCode();
1438             final int helpCode = getHelpCode(acquireInfo, vendorCode);
1439             final String helpMessage = getEnrollHelpMessage(mContext, acquireInfo, vendorCode);
1440             mEnrollmentCallback.onEnrollmentFrame(
1441                     helpCode,
1442                     helpMessage,
1443                     frame.getCell(),
1444                     frame.getStage(),
1445                     data.getPan(),
1446                     data.getTilt(),
1447                     data.getDistance());
1448         }
1449     }
1450 
getHelpCode(int acquireInfo, int vendorCode)1451     private static int getHelpCode(int acquireInfo, int vendorCode) {
1452         return acquireInfo == FACE_ACQUIRED_VENDOR
1453                 ? vendorCode + FACE_ACQUIRED_VENDOR_BASE
1454                 : acquireInfo;
1455     }
1456 
1457     /**
1458      * @hide
1459      */
1460     @Nullable
getAuthHelpMessage(Context context, int acquireInfo, int vendorCode)1461     public static String getAuthHelpMessage(Context context, int acquireInfo, int vendorCode) {
1462         switch (acquireInfo) {
1463             // No help message is needed for a good capture.
1464             case FACE_ACQUIRED_GOOD:
1465             case FACE_ACQUIRED_START:
1466                 return null;
1467 
1468             // Consolidate positional feedback to reduce noise during authentication.
1469             case FACE_ACQUIRED_NOT_DETECTED:
1470                 return context.getString(R.string.face_acquired_not_detected);
1471             case FACE_ACQUIRED_TOO_CLOSE:
1472                 return context.getString(R.string.face_acquired_too_close);
1473             case FACE_ACQUIRED_TOO_FAR:
1474                 return context.getString(R.string.face_acquired_too_far);
1475             case FACE_ACQUIRED_TOO_HIGH:
1476                 // TODO(b/181269243) Change back once error codes are fixed.
1477                 return context.getString(R.string.face_acquired_too_low);
1478             case FACE_ACQUIRED_TOO_LOW:
1479                 // TODO(b/181269243) Change back once error codes are fixed.
1480                 return context.getString(R.string.face_acquired_too_high);
1481             case FACE_ACQUIRED_TOO_RIGHT:
1482                 // TODO(b/181269243) Change back once error codes are fixed.
1483                 return context.getString(R.string.face_acquired_too_left);
1484             case FACE_ACQUIRED_TOO_LEFT:
1485                 // TODO(b/181269243) Change back once error codes are fixed.
1486                 return context.getString(R.string.face_acquired_too_right);
1487             case FACE_ACQUIRED_POOR_GAZE:
1488                 return context.getString(R.string.face_acquired_poor_gaze);
1489             case FACE_ACQUIRED_PAN_TOO_EXTREME:
1490                 return context.getString(R.string.face_acquired_pan_too_extreme);
1491             case FACE_ACQUIRED_TILT_TOO_EXTREME:
1492                 return context.getString(R.string.face_acquired_tilt_too_extreme);
1493             case FACE_ACQUIRED_ROLL_TOO_EXTREME:
1494                 return context.getString(R.string.face_acquired_roll_too_extreme);
1495             case FACE_ACQUIRED_INSUFFICIENT:
1496                 return context.getString(R.string.face_acquired_insufficient);
1497             case FACE_ACQUIRED_TOO_BRIGHT:
1498                 return context.getString(R.string.face_acquired_too_bright);
1499             case FACE_ACQUIRED_TOO_DARK:
1500                 return context.getString(R.string.face_acquired_too_dark);
1501             case FACE_ACQUIRED_TOO_MUCH_MOTION:
1502                 return context.getString(R.string.face_acquired_too_much_motion);
1503             case FACE_ACQUIRED_RECALIBRATE:
1504                 return context.getString(R.string.face_acquired_recalibrate);
1505             case FACE_ACQUIRED_TOO_DIFFERENT:
1506                 return context.getString(R.string.face_acquired_too_different);
1507             case FACE_ACQUIRED_TOO_SIMILAR:
1508                 return context.getString(R.string.face_acquired_too_similar);
1509             case FACE_ACQUIRED_FACE_OBSCURED:
1510                 return context.getString(R.string.face_acquired_obscured);
1511             case FACE_ACQUIRED_SENSOR_DIRTY:
1512                 return context.getString(R.string.face_acquired_sensor_dirty);
1513             case FACE_ACQUIRED_DARK_GLASSES_DETECTED:
1514                 return context.getString(R.string.face_acquired_dark_glasses_detected);
1515             case FACE_ACQUIRED_MOUTH_COVERING_DETECTED:
1516                 return context.getString(R.string.face_acquired_mouth_covering_detected);
1517 
1518             // Find and return the appropriate vendor-specific message.
1519             case FACE_ACQUIRED_VENDOR: {
1520                 String[] msgArray = context.getResources().getStringArray(
1521                         R.array.face_acquired_vendor);
1522                 if (vendorCode < msgArray.length) {
1523                     return msgArray[vendorCode];
1524                 }
1525             }
1526         }
1527 
1528         Slog.w(TAG, "Unknown authentication acquired message: " + acquireInfo + ", " + vendorCode);
1529         return null;
1530     }
1531 
1532     /**
1533      * @hide
1534      */
1535     @Nullable
getEnrollHelpMessage(Context context, int acquireInfo, int vendorCode)1536     public static String getEnrollHelpMessage(Context context, int acquireInfo, int vendorCode) {
1537         switch (acquireInfo) {
1538             case FACE_ACQUIRED_GOOD:
1539             case FACE_ACQUIRED_START:
1540                 return null;
1541             case FACE_ACQUIRED_INSUFFICIENT:
1542                 return context.getString(R.string.face_acquired_insufficient);
1543             case FACE_ACQUIRED_TOO_BRIGHT:
1544                 return context.getString(R.string.face_acquired_too_bright);
1545             case FACE_ACQUIRED_TOO_DARK:
1546                 return context.getString(R.string.face_acquired_too_dark);
1547             case FACE_ACQUIRED_TOO_CLOSE:
1548                 return context.getString(R.string.face_acquired_too_close);
1549             case FACE_ACQUIRED_TOO_FAR:
1550                 return context.getString(R.string.face_acquired_too_far);
1551             case FACE_ACQUIRED_TOO_HIGH:
1552                 // TODO(b/181269243): Change back once error codes are fixed.
1553                 return context.getString(R.string.face_acquired_too_low);
1554             case FACE_ACQUIRED_TOO_LOW:
1555                 // TODO(b/181269243) Change back once error codes are fixed.
1556                 return context.getString(R.string.face_acquired_too_high);
1557             case FACE_ACQUIRED_TOO_RIGHT:
1558                 // TODO(b/181269243) Change back once error codes are fixed.
1559                 return context.getString(R.string.face_acquired_too_left);
1560             case FACE_ACQUIRED_TOO_LEFT:
1561                 // TODO(b/181269243) Change back once error codes are fixed.
1562                 return context.getString(R.string.face_acquired_too_right);
1563             case FACE_ACQUIRED_POOR_GAZE:
1564                 return context.getString(R.string.face_acquired_poor_gaze);
1565             case FACE_ACQUIRED_NOT_DETECTED:
1566                 return context.getString(R.string.face_acquired_not_detected);
1567             case FACE_ACQUIRED_TOO_MUCH_MOTION:
1568                 return context.getString(R.string.face_acquired_too_much_motion);
1569             case FACE_ACQUIRED_RECALIBRATE:
1570                 return context.getString(R.string.face_acquired_recalibrate);
1571             case FACE_ACQUIRED_TOO_DIFFERENT:
1572                 return context.getString(R.string.face_acquired_too_different);
1573             case FACE_ACQUIRED_TOO_SIMILAR:
1574                 return context.getString(R.string.face_acquired_too_similar);
1575             case FACE_ACQUIRED_PAN_TOO_EXTREME:
1576                 return context.getString(R.string.face_acquired_pan_too_extreme);
1577             case FACE_ACQUIRED_TILT_TOO_EXTREME:
1578                 return context.getString(R.string.face_acquired_tilt_too_extreme);
1579             case FACE_ACQUIRED_ROLL_TOO_EXTREME:
1580                 return context.getString(R.string.face_acquired_roll_too_extreme);
1581             case FACE_ACQUIRED_FACE_OBSCURED:
1582                 return context.getString(R.string.face_acquired_obscured);
1583             case FACE_ACQUIRED_SENSOR_DIRTY:
1584                 return context.getString(R.string.face_acquired_sensor_dirty);
1585             case FACE_ACQUIRED_DARK_GLASSES_DETECTED:
1586                 return context.getString(R.string.face_acquired_dark_glasses_detected);
1587             case FACE_ACQUIRED_MOUTH_COVERING_DETECTED:
1588                 return context.getString(R.string.face_acquired_mouth_covering_detected);
1589             case FACE_ACQUIRED_VENDOR: {
1590                 String[] msgArray = context.getResources().getStringArray(
1591                         R.array.face_acquired_vendor);
1592                 if (vendorCode < msgArray.length) {
1593                     return msgArray[vendorCode];
1594                 }
1595             }
1596         }
1597         Slog.w(TAG, "Unknown enrollment acquired message: " + acquireInfo + ", " + vendorCode);
1598         return null;
1599     }
1600 }
1601