• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hardware.face;
18 
19 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
20 import static android.Manifest.permission.MANAGE_BIOMETRIC;
21 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
22 
23 import android.annotation.NonNull;
24 import android.annotation.Nullable;
25 import android.annotation.RequiresPermission;
26 import android.annotation.SystemService;
27 import android.app.ActivityManager;
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.Log;
46 import android.util.Slog;
47 
48 import com.android.internal.R;
49 import com.android.internal.os.SomeArgs;
50 
51 import java.util.List;
52 
53 /**
54  * A class that coordinates access to the face authentication hardware.
55  * @hide
56  */
57 @SystemService(Context.FACE_SERVICE)
58 public class FaceManager implements BiometricAuthenticator, BiometricFaceConstants {
59 
60     private static final String TAG = "FaceManager";
61     private static final boolean DEBUG = true;
62     private static final int MSG_ENROLL_RESULT = 100;
63     private static final int MSG_ACQUIRED = 101;
64     private static final int MSG_AUTHENTICATION_SUCCEEDED = 102;
65     private static final int MSG_AUTHENTICATION_FAILED = 103;
66     private static final int MSG_ERROR = 104;
67     private static final int MSG_REMOVED = 105;
68     private static final int MSG_GET_FEATURE_COMPLETED = 106;
69     private static final int MSG_SET_FEATURE_COMPLETED = 107;
70 
71     private IFaceService mService;
72     private final Context mContext;
73     private IBinder mToken = new Binder();
74     private AuthenticationCallback mAuthenticationCallback;
75     private EnrollmentCallback mEnrollmentCallback;
76     private RemovalCallback mRemovalCallback;
77     private SetFeatureCallback mSetFeatureCallback;
78     private GetFeatureCallback mGetFeatureCallback;
79     private CryptoObject mCryptoObject;
80     private Face mRemovalFace;
81     private Handler mHandler;
82 
83     private IFaceServiceReceiver mServiceReceiver = new IFaceServiceReceiver.Stub() {
84 
85         @Override // binder call
86         public void onEnrollResult(long deviceId, int faceId, int remaining) {
87             mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0,
88                     new Face(null, faceId, deviceId)).sendToTarget();
89         }
90 
91         @Override // binder call
92         public void onAcquired(long deviceId, int acquireInfo, int vendorCode) {
93             mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode, deviceId).sendToTarget();
94         }
95 
96         @Override // binder call
97         public void onAuthenticationSucceeded(long deviceId, Face face, int userId) {
98             mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, face).sendToTarget();
99         }
100 
101         @Override // binder call
102         public void onAuthenticationFailed(long deviceId) {
103             mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
104         }
105 
106         @Override // binder call
107         public void onError(long deviceId, int error, int vendorCode) {
108             mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
109         }
110 
111         @Override // binder call
112         public void onRemoved(long deviceId, int faceId, int remaining) {
113             mHandler.obtainMessage(MSG_REMOVED, remaining, 0,
114                     new Face(null, faceId, deviceId)).sendToTarget();
115         }
116 
117         @Override
118         public void onEnumerated(long deviceId, int faceId, int remaining) {
119             // TODO: Finish. Low priority since it's not used.
120         }
121 
122         @Override
123         public void onFeatureSet(boolean success, int feature) {
124             mHandler.obtainMessage(MSG_SET_FEATURE_COMPLETED, feature, 0, success).sendToTarget();
125         }
126 
127         @Override
128         public void onFeatureGet(boolean success, int feature, boolean value) {
129             SomeArgs args = SomeArgs.obtain();
130             args.arg1 = success;
131             args.argi1 = feature;
132             args.arg2 = value;
133             mHandler.obtainMessage(MSG_GET_FEATURE_COMPLETED, args).sendToTarget();
134         }
135     };
136 
137     /**
138      * @hide
139      */
FaceManager(Context context, IFaceService service)140     public FaceManager(Context context, IFaceService service) {
141         mContext = context;
142         mService = service;
143         if (mService == null) {
144             Slog.v(TAG, "FaceAuthenticationManagerService was null");
145         }
146         mHandler = new MyHandler(context);
147     }
148 
149     /**
150      * Request authentication of a crypto object. This call operates the face recognition hardware
151      * and starts capturing images. It terminates when
152      * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
153      * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at
154      * which point the object is no longer valid. The operation can be canceled by using the
155      * provided cancel object.
156      *
157      * @param crypto   object associated with the call or null if none required.
158      * @param cancel   an object that can be used to cancel authentication
159      * @param flags    optional flags; should be 0
160      * @param callback an object to receive authentication events
161      * @param handler  an optional handler to handle callback events
162      * @throws IllegalArgumentException if the crypto operation is not supported or is not backed
163      *                                  by
164      *                                  <a href="{@docRoot}training/articles/keystore.html">Android
165      *                                  Keystore facility</a>.
166      * @throws IllegalStateException    if the crypto primitive is not initialized.
167      * @hide
168      */
169     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
authenticate(@ullable CryptoObject crypto, @Nullable CancellationSignal cancel, int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler)170     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
171             int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
172         authenticate(crypto, cancel, flags, callback, handler, mContext.getUserId());
173     }
174 
175     /**
176      * Use the provided handler thread for events.
177      */
useHandler(Handler handler)178     private void useHandler(Handler handler) {
179         if (handler != null) {
180             mHandler = new MyHandler(handler.getLooper());
181         } else if (mHandler.getLooper() != mContext.getMainLooper()) {
182             mHandler = new MyHandler(mContext.getMainLooper());
183         }
184     }
185 
186     /**
187      * Request authentication of a crypto object. This call operates the face recognition hardware
188      * and starts capturing images. It terminates when
189      * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
190      * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at
191      * which point the object is no longer valid. The operation can be canceled by using the
192      * provided cancel object.
193      *
194      * @param crypto   object associated with the call or null if none required.
195      * @param cancel   an object that can be used to cancel authentication
196      * @param flags    optional flags; should be 0
197      * @param callback an object to receive authentication events
198      * @param handler  an optional handler to handle callback events
199      * @param userId   userId to authenticate for
200      * @throws IllegalArgumentException if the crypto operation is not supported or is not backed
201      *                                  by
202      *                                  <a href="{@docRoot}training/articles/keystore.html">Android
203      *                                  Keystore facility</a>.
204      * @throws IllegalStateException    if the crypto primitive is not initialized.
205      * @hide
206      */
authenticate(@ullable CryptoObject crypto, @Nullable CancellationSignal cancel, int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler, int userId)207     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
208             int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler,
209             int userId) {
210         if (callback == null) {
211             throw new IllegalArgumentException("Must supply an authentication callback");
212         }
213 
214         if (cancel != null) {
215             if (cancel.isCanceled()) {
216                 Log.w(TAG, "authentication already canceled");
217                 return;
218             } else {
219                 cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
220             }
221         }
222 
223         if (mService != null) {
224             try {
225                 useHandler(handler);
226                 mAuthenticationCallback = callback;
227                 mCryptoObject = crypto;
228                 long sessionId = crypto != null ? crypto.getOpId() : 0;
229                 Trace.beginSection("FaceManager#authenticate");
230                 mService.authenticate(mToken, sessionId, userId, mServiceReceiver,
231                         flags, mContext.getOpPackageName());
232             } catch (RemoteException e) {
233                 Log.w(TAG, "Remote exception while authenticating: ", e);
234                 if (callback != null) {
235                     // Though this may not be a hardware issue, it will cause apps to give up or
236                     // try again later.
237                     callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
238                             getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
239                                     0 /* vendorCode */));
240                 }
241             } finally {
242                 Trace.endSection();
243             }
244         }
245     }
246 
247     /**
248      * Request face authentication enrollment. This call operates the face authentication hardware
249      * and starts capturing images. Progress will be indicated by callbacks to the
250      * {@link EnrollmentCallback} object. It terminates when
251      * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or
252      * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at
253      * which point the object is no longer valid. The operation can be canceled by using the
254      * provided cancel object.
255      *
256      * @param token    a unique token provided by a recent creation or verification of device
257      *                 credentials (e.g. pin, pattern or password).
258      * @param cancel   an object that can be used to cancel enrollment
259      * @param flags    optional flags
260      * @param userId   the user to whom this face will belong to
261      * @param callback an object to receive enrollment events
262      * @hide
263      */
264     @RequiresPermission(MANAGE_BIOMETRIC)
enroll(byte[] token, CancellationSignal cancel, EnrollmentCallback callback, int[] disabledFeatures)265     public void enroll(byte[] token, CancellationSignal cancel,
266             EnrollmentCallback callback, int[] disabledFeatures) {
267         if (callback == null) {
268             throw new IllegalArgumentException("Must supply an enrollment callback");
269         }
270 
271         if (cancel != null) {
272             if (cancel.isCanceled()) {
273                 Log.w(TAG, "enrollment already canceled");
274                 return;
275             } else {
276                 cancel.setOnCancelListener(new OnEnrollCancelListener());
277             }
278         }
279 
280         if (mService != null) {
281             try {
282                 mEnrollmentCallback = callback;
283                 Trace.beginSection("FaceManager#enroll");
284                 mService.enroll(mToken, token, mServiceReceiver,
285                         mContext.getOpPackageName(), disabledFeatures);
286             } catch (RemoteException e) {
287                 Log.w(TAG, "Remote exception in enroll: ", e);
288                 if (callback != null) {
289                     // Though this may not be a hardware issue, it will cause apps to give up or
290                     // try again later.
291                     callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
292                             getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
293                                 0 /* vendorCode */));
294                 }
295             } finally {
296                 Trace.endSection();
297             }
298         }
299     }
300 
301     /**
302      * Requests an auth token to tie sensitive operations to the confirmation of
303      * existing device credentials (e.g. pin/pattern/password).
304      *
305      * @hide
306      */
307     @RequiresPermission(MANAGE_BIOMETRIC)
generateChallenge()308     public long generateChallenge() {
309         long result = 0;
310         if (mService != null) {
311             try {
312                 result = mService.generateChallenge(mToken);
313             } catch (RemoteException e) {
314                 throw e.rethrowFromSystemServer();
315             }
316         }
317         return result;
318     }
319 
320     /**
321      * Invalidates the current auth token.
322      *
323      * @hide
324      */
325     @RequiresPermission(MANAGE_BIOMETRIC)
revokeChallenge()326     public int revokeChallenge() {
327         int result = 0;
328         if (mService != null) {
329             try {
330                 result = mService.revokeChallenge(mToken);
331             } catch (RemoteException e) {
332                 throw e.rethrowFromSystemServer();
333             }
334         }
335         return result;
336     }
337 
338     /**
339      * @hide
340      */
341     @RequiresPermission(MANAGE_BIOMETRIC)
setFeature(int feature, boolean enabled, byte[] token, SetFeatureCallback callback)342     public void setFeature(int feature, boolean enabled, byte[] token,
343             SetFeatureCallback callback) {
344         if (mService != null) {
345             try {
346                 mSetFeatureCallback = callback;
347                 mService.setFeature(feature, enabled, token, mServiceReceiver);
348             } catch (RemoteException e) {
349                 throw e.rethrowFromSystemServer();
350             }
351         }
352     }
353 
354     /**
355      * @hide
356      */
357     @RequiresPermission(MANAGE_BIOMETRIC)
getFeature(int feature, GetFeatureCallback callback)358     public void getFeature(int feature, GetFeatureCallback callback) {
359         if (mService != null) {
360             try {
361                 mGetFeatureCallback = callback;
362                 mService.getFeature(feature, mServiceReceiver);
363             } catch (RemoteException e) {
364                 throw e.rethrowFromSystemServer();
365             }
366         }
367     }
368 
369     /**
370      * Pokes the the driver to have it start looking for faces again.
371      * @hide
372      */
373     @RequiresPermission(MANAGE_BIOMETRIC)
userActivity()374     public void userActivity() {
375         if (mService != null) {
376             try {
377                 mService.userActivity();
378             } catch (RemoteException e) {
379                 throw e.rethrowFromSystemServer();
380             }
381         }
382     }
383 
384     /**
385      * Sets the active user. This is meant to be used to select the current profile for enrollment
386      * to allow separate enrolled faces for a work profile
387      *
388      * @hide
389      */
390     @RequiresPermission(MANAGE_BIOMETRIC)
391     @Override
setActiveUser(int userId)392     public void setActiveUser(int userId) {
393         if (mService != null) {
394             try {
395                 mService.setActiveUser(userId);
396             } catch (RemoteException e) {
397                 throw e.rethrowFromSystemServer();
398             }
399         }
400     }
401 
402     /**
403      * Remove given face template from face hardware and/or protected storage.
404      *
405      * @param face     the face item to remove
406      * @param userId   the user who this face belongs to
407      * @param callback an optional callback to verify that face templates have been
408      *                 successfully removed. May be null if no callback is required.
409      * @hide
410      */
411     @RequiresPermission(MANAGE_BIOMETRIC)
remove(Face face, int userId, RemovalCallback callback)412     public void remove(Face face, int userId, RemovalCallback callback) {
413         if (mService != null) {
414             try {
415                 mRemovalCallback = callback;
416                 mRemovalFace = face;
417                 mService.remove(mToken, face.getBiometricId(), userId, mServiceReceiver);
418             } catch (RemoteException e) {
419                 Log.w(TAG, "Remote exception in remove: ", e);
420                 if (callback != null) {
421                     callback.onRemovalError(face, FACE_ERROR_HW_UNAVAILABLE,
422                             getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
423                                 0 /* vendorCode */));
424                 }
425             }
426         }
427     }
428 
429     /**
430      * Obtain the enrolled face template.
431      *
432      * @return the current face item
433      * @hide
434      */
435     @RequiresPermission(MANAGE_BIOMETRIC)
getEnrolledFaces(int userId)436     public List<Face> getEnrolledFaces(int userId) {
437         if (mService != null) {
438             try {
439                 return mService.getEnrolledFaces(userId, mContext.getOpPackageName());
440             } catch (RemoteException e) {
441                 throw e.rethrowFromSystemServer();
442             }
443         }
444         return null;
445     }
446 
447     /**
448      * Obtain the enrolled face template.
449      *
450      * @return the current face item
451      * @hide
452      */
453     @RequiresPermission(MANAGE_BIOMETRIC)
getEnrolledFaces()454     public List<Face> getEnrolledFaces() {
455         return getEnrolledFaces(UserHandle.myUserId());
456     }
457 
458     /**
459      * Determine if there is a face enrolled.
460      *
461      * @return true if a face is enrolled, false otherwise
462      */
463     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
464     @Override
hasEnrolledTemplates()465     public boolean hasEnrolledTemplates() {
466         if (mService != null) {
467             try {
468                 return mService.hasEnrolledFaces(
469                         UserHandle.myUserId(), mContext.getOpPackageName());
470             } catch (RemoteException e) {
471                 throw e.rethrowFromSystemServer();
472             }
473         }
474         return false;
475     }
476 
477     /**
478      * @hide
479      */
480     @RequiresPermission(allOf = {
481             USE_BIOMETRIC_INTERNAL,
482             INTERACT_ACROSS_USERS})
483     @Override
hasEnrolledTemplates(int userId)484     public boolean hasEnrolledTemplates(int userId) {
485         if (mService != null) {
486             try {
487                 return mService.hasEnrolledFaces(userId, mContext.getOpPackageName());
488             } catch (RemoteException e) {
489                 throw e.rethrowFromSystemServer();
490             }
491         }
492         return false;
493     }
494 
495     /**
496      * Determine if face authentication sensor hardware is present and functional.
497      *
498      * @return true if hardware is present and functional, false otherwise.
499      */
500     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
501     @Override
isHardwareDetected()502     public boolean isHardwareDetected() {
503         if (mService != null) {
504             try {
505                 long deviceId = 0; /* TODO: plumb hardware id to FPMS */
506                 return mService.isHardwareDetected(deviceId, mContext.getOpPackageName());
507             } catch (RemoteException e) {
508                 throw e.rethrowFromSystemServer();
509             }
510         } else {
511             Log.w(TAG, "isFaceHardwareDetected(): Service not connected!");
512         }
513         return false;
514     }
515 
516     /**
517      * Retrieves the authenticator token for binding keys to the lifecycle
518      * of the calling user's face. Used only by internal clients.
519      *
520      * @hide
521      */
getAuthenticatorId()522     public long getAuthenticatorId() {
523         if (mService != null) {
524             try {
525                 return mService.getAuthenticatorId(mContext.getOpPackageName());
526             } catch (RemoteException e) {
527                 throw e.rethrowFromSystemServer();
528             }
529         } else {
530             Log.w(TAG, "getAuthenticatorId(): Service not connected!");
531         }
532         return 0;
533     }
534 
535     /**
536      * @hide
537      */
538     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
addLockoutResetCallback(final LockoutResetCallback callback)539     public void addLockoutResetCallback(final LockoutResetCallback callback) {
540         if (mService != null) {
541             try {
542                 final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
543                 mService.addLockoutResetCallback(
544                         new IBiometricServiceLockoutResetCallback.Stub() {
545 
546                             @Override
547                             public void onLockoutReset(long deviceId,
548                                     IRemoteCallback serverCallback)
549                                     throws RemoteException {
550                                 try {
551                                     final PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
552                                             PowerManager.PARTIAL_WAKE_LOCK,
553                                             "faceLockoutResetCallback");
554                                     wakeLock.acquire();
555                                     mHandler.post(() -> {
556                                         try {
557                                             callback.onLockoutReset();
558                                         } finally {
559                                             wakeLock.release();
560                                         }
561                                     });
562                                 } finally {
563                                     serverCallback.sendResult(null /* data */);
564                                 }
565                             }
566                         });
567             } catch (RemoteException e) {
568                 throw e.rethrowFromSystemServer();
569             }
570         } else {
571             Log.w(TAG, "addLockoutResetCallback(): Service not connected!");
572         }
573     }
574 
getCurrentUserId()575     private int getCurrentUserId() {
576         try {
577             return ActivityManager.getService().getCurrentUser().id;
578         } catch (RemoteException e) {
579             throw e.rethrowFromSystemServer();
580         }
581     }
582 
cancelEnrollment()583     private void cancelEnrollment() {
584         if (mService != null) {
585             try {
586                 mService.cancelEnrollment(mToken);
587             } catch (RemoteException e) {
588                 throw e.rethrowFromSystemServer();
589             }
590         }
591     }
592 
cancelAuthentication(CryptoObject cryptoObject)593     private void cancelAuthentication(CryptoObject cryptoObject) {
594         if (mService != null) {
595             try {
596                 mService.cancelAuthentication(mToken, mContext.getOpPackageName());
597             } catch (RemoteException e) {
598                 throw e.rethrowFromSystemServer();
599             }
600         }
601     }
602 
603     /**
604      * @hide
605      */
getErrorString(Context context, int errMsg, int vendorCode)606     public static String getErrorString(Context context, int errMsg, int vendorCode) {
607         switch (errMsg) {
608             case FACE_ERROR_HW_UNAVAILABLE:
609                 return context.getString(
610                         com.android.internal.R.string.face_error_hw_not_available);
611             case FACE_ERROR_UNABLE_TO_PROCESS:
612                 return context.getString(
613                         com.android.internal.R.string.face_error_unable_to_process);
614             case FACE_ERROR_TIMEOUT:
615                 return context.getString(com.android.internal.R.string.face_error_timeout);
616             case FACE_ERROR_NO_SPACE:
617                 return context.getString(com.android.internal.R.string.face_error_no_space);
618             case FACE_ERROR_CANCELED:
619                 return context.getString(com.android.internal.R.string.face_error_canceled);
620             case FACE_ERROR_LOCKOUT:
621                 return context.getString(com.android.internal.R.string.face_error_lockout);
622             case FACE_ERROR_LOCKOUT_PERMANENT:
623                 return context.getString(
624                         com.android.internal.R.string.face_error_lockout_permanent);
625             case FACE_ERROR_USER_CANCELED:
626                 return context.getString(com.android.internal.R.string.face_error_user_canceled);
627             case FACE_ERROR_NOT_ENROLLED:
628                 return context.getString(com.android.internal.R.string.face_error_not_enrolled);
629             case FACE_ERROR_HW_NOT_PRESENT:
630                 return context.getString(com.android.internal.R.string.face_error_hw_not_present);
631             case FACE_ERROR_VENDOR: {
632                 String[] msgArray = context.getResources().getStringArray(
633                         com.android.internal.R.array.face_error_vendor);
634                 if (vendorCode < msgArray.length) {
635                     return msgArray[vendorCode];
636                 }
637             }
638         }
639         Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode);
640         return null;
641     }
642 
643     /**
644      * @hide
645      */
getAcquiredString(Context context, int acquireInfo, int vendorCode)646     public static String getAcquiredString(Context context, int acquireInfo, int vendorCode) {
647         switch (acquireInfo) {
648             case FACE_ACQUIRED_GOOD:
649                 return null;
650             case FACE_ACQUIRED_INSUFFICIENT:
651                 return context.getString(R.string.face_acquired_insufficient);
652             case FACE_ACQUIRED_TOO_BRIGHT:
653                 return context.getString(R.string.face_acquired_too_bright);
654             case FACE_ACQUIRED_TOO_DARK:
655                 return context.getString(R.string.face_acquired_too_dark);
656             case FACE_ACQUIRED_TOO_CLOSE:
657                 return context.getString(R.string.face_acquired_too_close);
658             case FACE_ACQUIRED_TOO_FAR:
659                 return context.getString(R.string.face_acquired_too_far);
660             case FACE_ACQUIRED_TOO_HIGH:
661                 return context.getString(R.string.face_acquired_too_high);
662             case FACE_ACQUIRED_TOO_LOW:
663                 return context.getString(R.string.face_acquired_too_low);
664             case FACE_ACQUIRED_TOO_RIGHT:
665                 return context.getString(R.string.face_acquired_too_right);
666             case FACE_ACQUIRED_TOO_LEFT:
667                 return context.getString(R.string.face_acquired_too_left);
668             case FACE_ACQUIRED_POOR_GAZE:
669                 return context.getString(R.string.face_acquired_poor_gaze);
670             case FACE_ACQUIRED_NOT_DETECTED:
671                 return context.getString(R.string.face_acquired_not_detected);
672             case FACE_ACQUIRED_TOO_MUCH_MOTION:
673                 return context.getString(R.string.face_acquired_too_much_motion);
674             case FACE_ACQUIRED_RECALIBRATE:
675                 return context.getString(R.string.face_acquired_recalibrate);
676             case FACE_ACQUIRED_TOO_DIFFERENT:
677                 return context.getString(R.string.face_acquired_too_different);
678             case FACE_ACQUIRED_TOO_SIMILAR:
679                 return context.getString(R.string.face_acquired_too_similar);
680             case FACE_ACQUIRED_PAN_TOO_EXTREME:
681                 return context.getString(R.string.face_acquired_pan_too_extreme);
682             case FACE_ACQUIRED_TILT_TOO_EXTREME:
683                 return context.getString(R.string.face_acquired_tilt_too_extreme);
684             case FACE_ACQUIRED_ROLL_TOO_EXTREME:
685                 return context.getString(R.string.face_acquired_roll_too_extreme);
686             case FACE_ACQUIRED_FACE_OBSCURED:
687                 return context.getString(R.string.face_acquired_obscured);
688             case FACE_ACQUIRED_START:
689                 return null;
690             case FACE_ACQUIRED_SENSOR_DIRTY:
691                 return context.getString(R.string.face_acquired_sensor_dirty);
692             case FACE_ACQUIRED_VENDOR: {
693                 String[] msgArray = context.getResources().getStringArray(
694                         R.array.face_acquired_vendor);
695                 if (vendorCode < msgArray.length) {
696                     return msgArray[vendorCode];
697                 }
698             }
699         }
700         Slog.w(TAG, "Invalid acquired message: " + acquireInfo + ", " + vendorCode);
701         return null;
702     }
703 
704     /**
705      * Used so BiometricPrompt can map the face ones onto existing public constants.
706      * @hide
707      */
getMappedAcquiredInfo(int acquireInfo, int vendorCode)708     public static int getMappedAcquiredInfo(int acquireInfo, int vendorCode) {
709         switch (acquireInfo) {
710             case FACE_ACQUIRED_GOOD:
711                 return BiometricConstants.BIOMETRIC_ACQUIRED_GOOD;
712             case FACE_ACQUIRED_INSUFFICIENT:
713             case FACE_ACQUIRED_TOO_BRIGHT:
714             case FACE_ACQUIRED_TOO_DARK:
715                 return BiometricConstants.BIOMETRIC_ACQUIRED_INSUFFICIENT;
716             case FACE_ACQUIRED_TOO_CLOSE:
717             case FACE_ACQUIRED_TOO_FAR:
718             case FACE_ACQUIRED_TOO_HIGH:
719             case FACE_ACQUIRED_TOO_LOW:
720             case FACE_ACQUIRED_TOO_RIGHT:
721             case FACE_ACQUIRED_TOO_LEFT:
722                 return BiometricConstants.BIOMETRIC_ACQUIRED_PARTIAL;
723             case FACE_ACQUIRED_POOR_GAZE:
724             case FACE_ACQUIRED_NOT_DETECTED:
725             case FACE_ACQUIRED_TOO_MUCH_MOTION:
726             case FACE_ACQUIRED_RECALIBRATE:
727                 return BiometricConstants.BIOMETRIC_ACQUIRED_INSUFFICIENT;
728             case FACE_ACQUIRED_VENDOR:
729                 return BiometricConstants.BIOMETRIC_ACQUIRED_VENDOR_BASE + vendorCode;
730             default:
731                 return BiometricConstants.BIOMETRIC_ACQUIRED_GOOD;
732         }
733     }
734 
735     /**
736      * Container for callback data from {@link FaceManager#authenticate(CryptoObject,
737      * CancellationSignal, int, AuthenticationCallback, Handler)}.
738      */
739     public static class AuthenticationResult {
740         private Face mFace;
741         private CryptoObject mCryptoObject;
742         private int mUserId;
743 
744         /**
745          * Authentication result
746          *
747          * @param crypto the crypto object
748          * @param face   the recognized face data, if allowed.
749          * @hide
750          */
AuthenticationResult(CryptoObject crypto, Face face, int userId)751         public AuthenticationResult(CryptoObject crypto, Face face, int userId) {
752             mCryptoObject = crypto;
753             mFace = face;
754             mUserId = userId;
755         }
756 
757         /**
758          * Obtain the crypto object associated with this transaction
759          *
760          * @return crypto object provided to {@link FaceManager#authenticate
761          * (CryptoObject,
762          * CancellationSignal, int, AuthenticationCallback, Handler)}.
763          */
getCryptoObject()764         public CryptoObject getCryptoObject() {
765             return mCryptoObject;
766         }
767 
768         /**
769          * Obtain the Face associated with this operation. Applications are strongly
770          * discouraged from associating specific faces with specific applications or operations.
771          *
772          * @hide
773          */
getFace()774         public Face getFace() {
775             return mFace;
776         }
777 
778         /**
779          * Obtain the userId for which this face was authenticated.
780          *
781          * @hide
782          */
getUserId()783         public int getUserId() {
784             return mUserId;
785         }
786     }
787 
788     /**
789      * Callback structure provided to {@link FaceManager#authenticate(CryptoObject,
790      * CancellationSignal, int, AuthenticationCallback, Handler)}. Users of {@link
791      * FaceManager#authenticate(CryptoObject, CancellationSignal,
792      * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening
793      * to face events.
794      */
795     public abstract static class AuthenticationCallback
796             extends BiometricAuthenticator.AuthenticationCallback {
797 
798         /**
799          * Called when an unrecoverable error has been encountered and the operation is complete.
800          * No further callbacks will be made on this object.
801          *
802          * @param errorCode An integer identifying the error message
803          * @param errString A human-readable error string that can be shown in UI
804          */
onAuthenticationError(int errorCode, CharSequence errString)805         public void onAuthenticationError(int errorCode, CharSequence errString) {
806         }
807 
808         /**
809          * Called when a recoverable error has been encountered during authentication. The help
810          * string is provided to give the user guidance for what went wrong, such as
811          * "Sensor dirty, please clean it."
812          *
813          * @param helpCode   An integer identifying the error message
814          * @param helpString A human-readable string that can be shown in UI
815          */
onAuthenticationHelp(int helpCode, CharSequence helpString)816         public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
817         }
818 
819         /**
820          * Called when a face is recognized.
821          *
822          * @param result An object containing authentication-related data
823          */
onAuthenticationSucceeded(AuthenticationResult result)824         public void onAuthenticationSucceeded(AuthenticationResult result) {
825         }
826 
827         /**
828          * Called when a face is detected but not recognized.
829          */
onAuthenticationFailed()830         public void onAuthenticationFailed() {
831         }
832 
833         /**
834          * Called when a face image has been acquired, but wasn't processed yet.
835          *
836          * @param acquireInfo one of FACE_ACQUIRED_* constants
837          * @hide
838          */
onAuthenticationAcquired(int acquireInfo)839         public void onAuthenticationAcquired(int acquireInfo) {
840         }
841     }
842 
843     /**
844      * Callback structure provided to {@link FaceManager#enroll(long,
845      * EnrollmentCallback, CancellationSignal, int). Users of {@link #FaceAuthenticationManager()}
846      * must provide an implementation of this to {@link FaceManager#enroll(long,
847      * CancellationSignal, int, EnrollmentCallback) for listening to face enrollment events.
848      *
849      * @hide
850      */
851     public abstract static class EnrollmentCallback {
852 
853         /**
854          * Called when an unrecoverable error has been encountered and the operation is complete.
855          * No further callbacks will be made on this object.
856          *
857          * @param errMsgId  An integer identifying the error message
858          * @param errString A human-readable error string that can be shown in UI
859          */
onEnrollmentError(int errMsgId, CharSequence errString)860         public void onEnrollmentError(int errMsgId, CharSequence errString) {
861         }
862 
863         /**
864          * Called when a recoverable error has been encountered during enrollment. The help
865          * string is provided to give the user guidance for what went wrong, such as
866          * "Image too dark, uncover light source" or what they need to do next, such as
867          * "Rotate face up / down."
868          *
869          * @param helpMsgId  An integer identifying the error message
870          * @param helpString A human-readable string that can be shown in UI
871          */
onEnrollmentHelp(int helpMsgId, CharSequence helpString)872         public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
873         }
874 
875         /**
876          * Called as each enrollment step progresses. Enrollment is considered complete when
877          * remaining reaches 0. This function will not be called if enrollment fails. See
878          * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)}
879          *
880          * @param remaining The number of remaining steps
881          */
onEnrollmentProgress(int remaining)882         public void onEnrollmentProgress(int remaining) {
883         }
884     }
885 
886     /**
887      * Callback structure provided to {@link #remove}. Users of {@link FaceManager}
888      * may
889      * optionally provide an implementation of this to
890      * {@link #remove(Face, int, RemovalCallback)} for listening to face template
891      * removal events.
892      *
893      * @hide
894      */
895     public abstract static class RemovalCallback {
896 
897         /**
898          * Called when the given face can't be removed.
899          *
900          * @param face      The face that the call attempted to remove
901          * @param errMsgId  An associated error message id
902          * @param errString An error message indicating why the face id can't be removed
903          */
onRemovalError(Face face, int errMsgId, CharSequence errString)904         public void onRemovalError(Face face, int errMsgId, CharSequence errString) {
905         }
906 
907         /**
908          * Called when a given face is successfully removed.
909          *
910          * @param face The face template that was removed.
911          */
onRemovalSucceeded(Face face, int remaining)912         public void onRemovalSucceeded(Face face, int remaining) {
913         }
914     }
915 
916     /**
917      * @hide
918      */
919     public abstract static class LockoutResetCallback {
920 
921         /**
922          * Called when lockout period expired and clients are allowed to listen for face
923          * authentication
924          * again.
925          */
onLockoutReset()926         public void onLockoutReset() {
927         }
928     }
929 
930     /**
931      * @hide
932      */
933     public abstract static class SetFeatureCallback {
onCompleted(boolean success, int feature)934         public abstract void onCompleted(boolean success, int feature);
935     }
936 
937     /**
938      * @hide
939      */
940     public abstract static class GetFeatureCallback {
onCompleted(boolean success, int feature, boolean value)941         public abstract void onCompleted(boolean success, int feature, boolean value);
942     }
943 
944     private class OnEnrollCancelListener implements OnCancelListener {
945         @Override
onCancel()946         public void onCancel() {
947             cancelEnrollment();
948         }
949     }
950 
951     private class OnAuthenticationCancelListener implements OnCancelListener {
952         private CryptoObject mCrypto;
953 
OnAuthenticationCancelListener(CryptoObject crypto)954         OnAuthenticationCancelListener(CryptoObject crypto) {
955             mCrypto = crypto;
956         }
957 
958         @Override
onCancel()959         public void onCancel() {
960             cancelAuthentication(mCrypto);
961         }
962     }
963 
964     private class MyHandler extends Handler {
MyHandler(Context context)965         private MyHandler(Context context) {
966             super(context.getMainLooper());
967         }
968 
MyHandler(Looper looper)969         private MyHandler(Looper looper) {
970             super(looper);
971         }
972 
973         @Override
handleMessage(android.os.Message msg)974         public void handleMessage(android.os.Message msg) {
975             Trace.beginSection("FaceManager#handleMessage: " + Integer.toString(msg.what));
976             switch (msg.what) {
977                 case MSG_ENROLL_RESULT:
978                     sendEnrollResult((Face) msg.obj, msg.arg1 /* remaining */);
979                     break;
980                 case MSG_ACQUIRED:
981                     sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */,
982                             msg.arg2 /* vendorCode */);
983                     break;
984                 case MSG_AUTHENTICATION_SUCCEEDED:
985                     sendAuthenticatedSucceeded((Face) msg.obj, msg.arg1 /* userId */);
986                     break;
987                 case MSG_AUTHENTICATION_FAILED:
988                     sendAuthenticatedFailed();
989                     break;
990                 case MSG_ERROR:
991                     sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */,
992                             msg.arg2 /* vendorCode */);
993                     break;
994                 case MSG_REMOVED:
995                     sendRemovedResult((Face) msg.obj, msg.arg1 /* remaining */);
996                     break;
997                 case MSG_SET_FEATURE_COMPLETED:
998                     sendSetFeatureCompleted((boolean) msg.obj /* success */,
999                             msg.arg1 /* feature */);
1000                     break;
1001                 case MSG_GET_FEATURE_COMPLETED:
1002                     SomeArgs args = (SomeArgs) msg.obj;
1003                     sendGetFeatureCompleted((boolean) args.arg1 /* success */,
1004                             args.argi1 /* feature */,
1005                             (boolean) args.arg2 /* value */);
1006                     args.recycle();
1007                     break;
1008                 default:
1009                     Log.w(TAG, "Unknown message: " + msg.what);
1010             }
1011             Trace.endSection();
1012         }
1013     }
1014 
sendSetFeatureCompleted(boolean success, int feature)1015     private void sendSetFeatureCompleted(boolean success, int feature) {
1016         if (mSetFeatureCallback == null) {
1017             return;
1018         }
1019         mSetFeatureCallback.onCompleted(success, feature);
1020     }
1021 
sendGetFeatureCompleted(boolean success, int feature, boolean value)1022     private void sendGetFeatureCompleted(boolean success, int feature, boolean value) {
1023         if (mGetFeatureCallback == null) {
1024             return;
1025         }
1026         mGetFeatureCallback.onCompleted(success, feature, value);
1027     }
1028 
sendRemovedResult(Face face, int remaining)1029     private void sendRemovedResult(Face face, int remaining) {
1030         if (mRemovalCallback == null) {
1031             return;
1032         }
1033         if (face == null) {
1034             Log.e(TAG, "Received MSG_REMOVED, but face is null");
1035             return;
1036         }
1037         mRemovalCallback.onRemovalSucceeded(face, remaining);
1038     }
1039 
sendErrorResult(long deviceId, int errMsgId, int vendorCode)1040     private void sendErrorResult(long deviceId, int errMsgId, int vendorCode) {
1041         // emulate HAL 2.1 behavior and send real errMsgId
1042         final int clientErrMsgId = errMsgId == FACE_ERROR_VENDOR
1043                 ? (vendorCode + FACE_ERROR_VENDOR_BASE) : errMsgId;
1044         if (mEnrollmentCallback != null) {
1045             mEnrollmentCallback.onEnrollmentError(clientErrMsgId,
1046                     getErrorString(mContext, errMsgId, vendorCode));
1047         } else if (mAuthenticationCallback != null) {
1048             mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
1049                     getErrorString(mContext, errMsgId, vendorCode));
1050         } else if (mRemovalCallback != null) {
1051             mRemovalCallback.onRemovalError(mRemovalFace, clientErrMsgId,
1052                     getErrorString(mContext, errMsgId, vendorCode));
1053         }
1054     }
1055 
sendEnrollResult(Face face, int remaining)1056     private void sendEnrollResult(Face face, int remaining) {
1057         if (mEnrollmentCallback != null) {
1058             mEnrollmentCallback.onEnrollmentProgress(remaining);
1059         }
1060     }
1061 
sendAuthenticatedSucceeded(Face face, int userId)1062     private void sendAuthenticatedSucceeded(Face face, int userId) {
1063         if (mAuthenticationCallback != null) {
1064             final AuthenticationResult result =
1065                     new AuthenticationResult(mCryptoObject, face, userId);
1066             mAuthenticationCallback.onAuthenticationSucceeded(result);
1067         }
1068     }
1069 
sendAuthenticatedFailed()1070     private void sendAuthenticatedFailed() {
1071         if (mAuthenticationCallback != null) {
1072             mAuthenticationCallback.onAuthenticationFailed();
1073         }
1074     }
1075 
sendAcquiredResult(long deviceId, int acquireInfo, int vendorCode)1076     private void sendAcquiredResult(long deviceId, int acquireInfo, int vendorCode) {
1077         if (mAuthenticationCallback != null) {
1078             mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
1079         }
1080         final String msg = getAcquiredString(mContext, acquireInfo, vendorCode);
1081         final int clientInfo = acquireInfo == FACE_ACQUIRED_VENDOR
1082                 ? (vendorCode + FACE_ACQUIRED_VENDOR_BASE) : acquireInfo;
1083         if (mEnrollmentCallback != null) {
1084             mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg);
1085         } else if (mAuthenticationCallback != null && msg != null) {
1086             mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
1087         }
1088     }
1089 }
1090