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