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