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