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