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                 boolean isStrongBiometric) {
99             mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, isStrongBiometric ? 1 : 0,
100                     face).sendToTarget();
101         }
102 
103         @Override // binder call
104         public void onAuthenticationFailed(long deviceId) {
105             mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
106         }
107 
108         @Override // binder call
109         public void onError(long deviceId, int error, int vendorCode) {
110             mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
111         }
112 
113         @Override // binder call
114         public void onRemoved(long deviceId, int faceId, int remaining) {
115             mHandler.obtainMessage(MSG_REMOVED, remaining, 0,
116                     new Face(null, faceId, deviceId)).sendToTarget();
117         }
118 
119         @Override
120         public void onEnumerated(long deviceId, int faceId, int remaining) {
121             // TODO: Finish. Low priority since it's not used.
122         }
123 
124         @Override
125         public void onFeatureSet(boolean success, int feature) {
126             mHandler.obtainMessage(MSG_SET_FEATURE_COMPLETED, feature, 0, success).sendToTarget();
127         }
128 
129         @Override
130         public void onFeatureGet(boolean success, int feature, boolean value) {
131             SomeArgs args = SomeArgs.obtain();
132             args.arg1 = success;
133             args.argi1 = feature;
134             args.arg2 = value;
135             mHandler.obtainMessage(MSG_GET_FEATURE_COMPLETED, args).sendToTarget();
136         }
137     };
138 
139     /**
140      * @hide
141      */
FaceManager(Context context, IFaceService service)142     public FaceManager(Context context, IFaceService service) {
143         mContext = context;
144         mService = service;
145         if (mService == null) {
146             Slog.v(TAG, "FaceAuthenticationManagerService was null");
147         }
148         mHandler = new MyHandler(context);
149     }
150 
151     /**
152      * Request authentication of a crypto object. This call operates the face recognition hardware
153      * and starts capturing images. It terminates when
154      * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
155      * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at
156      * which point the object is no longer valid. The operation can be canceled by using the
157      * provided cancel object.
158      *
159      * @param crypto   object associated with the call or null if none required.
160      * @param cancel   an object that can be used to cancel authentication
161      * @param flags    optional flags; should be 0
162      * @param callback an object to receive authentication events
163      * @param handler  an optional handler to handle callback events
164      * @throws IllegalArgumentException if the crypto operation is not supported or is not backed
165      *                                  by
166      *                                  <a href="{@docRoot}training/articles/keystore.html">Android
167      *                                  Keystore facility</a>.
168      * @throws IllegalStateException    if the crypto primitive is not initialized.
169      * @hide
170      */
171     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
authenticate(@ullable CryptoObject crypto, @Nullable CancellationSignal cancel, int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler)172     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
173             int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
174         authenticate(crypto, cancel, flags, callback, handler, mContext.getUserId());
175     }
176 
177     /**
178      * Use the provided handler thread for events.
179      */
useHandler(Handler handler)180     private void useHandler(Handler handler) {
181         if (handler != null) {
182             mHandler = new MyHandler(handler.getLooper());
183         } else if (mHandler.getLooper() != mContext.getMainLooper()) {
184             mHandler = new MyHandler(mContext.getMainLooper());
185         }
186     }
187 
188     /**
189      * Request authentication of a crypto object. This call operates the face recognition hardware
190      * and starts capturing images. It terminates when
191      * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or
192      * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at
193      * which point the object is no longer valid. The operation can be canceled by using the
194      * provided cancel object.
195      *
196      * @param crypto   object associated with the call or null if none required.
197      * @param cancel   an object that can be used to cancel authentication
198      * @param flags    optional flags; should be 0
199      * @param callback an object to receive authentication events
200      * @param handler  an optional handler to handle callback events
201      * @param userId   userId to authenticate for
202      * @throws IllegalArgumentException if the crypto operation is not supported or is not backed
203      *                                  by
204      *                                  <a href="{@docRoot}training/articles/keystore.html">Android
205      *                                  Keystore facility</a>.
206      * @throws IllegalStateException    if the crypto primitive is not initialized.
207      * @hide
208      */
authenticate(@ullable CryptoObject crypto, @Nullable CancellationSignal cancel, int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler, int userId)209     public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
210             int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler,
211             int userId) {
212         if (callback == null) {
213             throw new IllegalArgumentException("Must supply an authentication callback");
214         }
215 
216         if (cancel != null) {
217             if (cancel.isCanceled()) {
218                 Log.w(TAG, "authentication already canceled");
219                 return;
220             } else {
221                 cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
222             }
223         }
224 
225         if (mService != null) {
226             try {
227                 useHandler(handler);
228                 mAuthenticationCallback = callback;
229                 mCryptoObject = crypto;
230                 long sessionId = crypto != null ? crypto.getOpId() : 0;
231                 Trace.beginSection("FaceManager#authenticate");
232                 mService.authenticate(mToken, sessionId, userId, mServiceReceiver,
233                         flags, mContext.getOpPackageName());
234             } catch (RemoteException e) {
235                 Log.w(TAG, "Remote exception while authenticating: ", e);
236                 if (callback != null) {
237                     // Though this may not be a hardware issue, it will cause apps to give up or
238                     // try again later.
239                     callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
240                             getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
241                                     0 /* vendorCode */));
242                 }
243             } finally {
244                 Trace.endSection();
245             }
246         }
247     }
248 
249     /**
250      * Request face authentication enrollment. This call operates the face authentication hardware
251      * and starts capturing images. Progress will be indicated by callbacks to the
252      * {@link EnrollmentCallback} object. It terminates when
253      * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or
254      * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at
255      * which point the object is no longer valid. The operation can be canceled by using the
256      * provided cancel object.
257      *
258      * @param token    a unique token provided by a recent creation or verification of device
259      *                 credentials (e.g. pin, pattern or password).
260      * @param cancel   an object that can be used to cancel enrollment
261      * @param flags    optional flags
262      * @param userId   the user to whom this face will belong to
263      * @param callback an object to receive enrollment events
264      * @hide
265      */
266     @RequiresPermission(MANAGE_BIOMETRIC)
enroll(int userId, byte[] token, CancellationSignal cancel, EnrollmentCallback callback, int[] disabledFeatures)267     public void enroll(int userId, byte[] token, CancellationSignal cancel,
268             EnrollmentCallback callback, int[] disabledFeatures) {
269         if (callback == null) {
270             throw new IllegalArgumentException("Must supply an enrollment callback");
271         }
272 
273         if (cancel != null) {
274             if (cancel.isCanceled()) {
275                 Log.w(TAG, "enrollment already canceled");
276                 return;
277             } else {
278                 cancel.setOnCancelListener(new OnEnrollCancelListener());
279             }
280         }
281 
282         if (mService != null) {
283             try {
284                 mEnrollmentCallback = callback;
285                 Trace.beginSection("FaceManager#enroll");
286                 mService.enroll(userId, mToken, token, mServiceReceiver,
287                         mContext.getOpPackageName(), disabledFeatures);
288             } catch (RemoteException e) {
289                 Log.w(TAG, "Remote exception in enroll: ", e);
290                 if (callback != null) {
291                     // Though this may not be a hardware issue, it will cause apps to give up or
292                     // try again later.
293                     callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE,
294                             getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
295                                 0 /* vendorCode */));
296                 }
297             } finally {
298                 Trace.endSection();
299             }
300         }
301     }
302 
303     /**
304      * Requests an auth token to tie sensitive operations to the confirmation of
305      * existing device credentials (e.g. pin/pattern/password).
306      *
307      * @hide
308      */
309     @RequiresPermission(MANAGE_BIOMETRIC)
generateChallenge()310     public long generateChallenge() {
311         long result = 0;
312         if (mService != null) {
313             try {
314                 result = mService.generateChallenge(mToken);
315             } catch (RemoteException e) {
316                 throw e.rethrowFromSystemServer();
317             }
318         }
319         return result;
320     }
321 
322     /**
323      * Invalidates the current auth token.
324      *
325      * @hide
326      */
327     @RequiresPermission(MANAGE_BIOMETRIC)
revokeChallenge()328     public int revokeChallenge() {
329         int result = 0;
330         if (mService != null) {
331             try {
332                 result = mService.revokeChallenge(mToken);
333             } catch (RemoteException e) {
334                 throw e.rethrowFromSystemServer();
335             }
336         }
337         return result;
338     }
339 
340     /**
341      * @hide
342      */
343     @RequiresPermission(MANAGE_BIOMETRIC)
setFeature(int userId, int feature, boolean enabled, byte[] token, SetFeatureCallback callback)344     public void setFeature(int userId, int feature, boolean enabled, byte[] token,
345             SetFeatureCallback callback) {
346         if (mService != null) {
347             try {
348                 mSetFeatureCallback = callback;
349                 mService.setFeature(userId, feature, enabled, token, mServiceReceiver,
350                         mContext.getOpPackageName());
351             } catch (RemoteException e) {
352                 throw e.rethrowFromSystemServer();
353             }
354         }
355     }
356 
357     /**
358      * @hide
359      */
360     @RequiresPermission(MANAGE_BIOMETRIC)
getFeature(int userId, int feature, GetFeatureCallback callback)361     public void getFeature(int userId, int feature, GetFeatureCallback callback) {
362         if (mService != null) {
363             try {
364                 mGetFeatureCallback = callback;
365                 mService.getFeature(userId, feature, mServiceReceiver, mContext.getOpPackageName());
366             } catch (RemoteException e) {
367                 throw e.rethrowFromSystemServer();
368             }
369         }
370     }
371 
372     /**
373      * Pokes the the driver to have it start looking for faces again.
374      * @hide
375      */
376     @RequiresPermission(MANAGE_BIOMETRIC)
userActivity()377     public void userActivity() {
378         if (mService != null) {
379             try {
380                 mService.userActivity();
381             } catch (RemoteException e) {
382                 throw e.rethrowFromSystemServer();
383             }
384         }
385     }
386 
387     /**
388      * Sets the active user. This is meant to be used to select the current profile for enrollment
389      * to allow separate enrolled faces for a work profile
390      *
391      * @hide
392      */
393     @RequiresPermission(MANAGE_BIOMETRIC)
394     @Override
setActiveUser(int userId)395     public void setActiveUser(int userId) {
396         if (mService != null) {
397             try {
398                 mService.setActiveUser(userId);
399             } catch (RemoteException e) {
400                 throw e.rethrowFromSystemServer();
401             }
402         }
403     }
404 
405     /**
406      * Remove given face template from face hardware and/or protected storage.
407      *
408      * @param face     the face item to remove
409      * @param userId   the user who this face belongs to
410      * @param callback an optional callback to verify that face templates have been
411      *                 successfully removed. May be null if no callback is required.
412      * @hide
413      */
414     @RequiresPermission(MANAGE_BIOMETRIC)
remove(Face face, int userId, RemovalCallback callback)415     public void remove(Face face, int userId, RemovalCallback callback) {
416         if (mService != null) {
417             try {
418                 mRemovalCallback = callback;
419                 mRemovalFace = face;
420                 mService.remove(mToken, face.getBiometricId(), userId, mServiceReceiver,
421                         mContext.getOpPackageName());
422             } catch (RemoteException e) {
423                 Log.w(TAG, "Remote exception in remove: ", e);
424                 if (callback != null) {
425                     callback.onRemovalError(face, FACE_ERROR_HW_UNAVAILABLE,
426                             getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE,
427                                 0 /* vendorCode */));
428                 }
429             }
430         }
431     }
432 
433     /**
434      * Obtain the enrolled face template.
435      *
436      * @return the current face item
437      * @hide
438      */
439     @RequiresPermission(MANAGE_BIOMETRIC)
getEnrolledFaces(int userId)440     public List<Face> getEnrolledFaces(int userId) {
441         if (mService != null) {
442             try {
443                 return mService.getEnrolledFaces(userId, mContext.getOpPackageName());
444             } catch (RemoteException e) {
445                 throw e.rethrowFromSystemServer();
446             }
447         }
448         return null;
449     }
450 
451     /**
452      * Obtain the enrolled face template.
453      *
454      * @return the current face item
455      * @hide
456      */
457     @RequiresPermission(MANAGE_BIOMETRIC)
getEnrolledFaces()458     public List<Face> getEnrolledFaces() {
459         return getEnrolledFaces(UserHandle.myUserId());
460     }
461 
462     /**
463      * Determine if there is a face enrolled.
464      *
465      * @return true if a face is enrolled, false otherwise
466      */
467     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
468     @Override
hasEnrolledTemplates()469     public boolean hasEnrolledTemplates() {
470         if (mService != null) {
471             try {
472                 return mService.hasEnrolledFaces(
473                         UserHandle.myUserId(), mContext.getOpPackageName());
474             } catch (RemoteException e) {
475                 throw e.rethrowFromSystemServer();
476             }
477         }
478         return false;
479     }
480 
481     /**
482      * @hide
483      */
484     @RequiresPermission(allOf = {
485             USE_BIOMETRIC_INTERNAL,
486             INTERACT_ACROSS_USERS})
487     @Override
hasEnrolledTemplates(int userId)488     public boolean hasEnrolledTemplates(int userId) {
489         if (mService != null) {
490             try {
491                 return mService.hasEnrolledFaces(userId, mContext.getOpPackageName());
492             } catch (RemoteException e) {
493                 throw e.rethrowFromSystemServer();
494             }
495         }
496         return false;
497     }
498 
499     /**
500      * Determine if face authentication sensor hardware is present and functional.
501      *
502      * @return true if hardware is present and functional, false otherwise.
503      */
504     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
505     @Override
isHardwareDetected()506     public boolean isHardwareDetected() {
507         if (mService != null) {
508             try {
509                 return mService.isHardwareDetected(mContext.getOpPackageName());
510             } catch (RemoteException e) {
511                 throw e.rethrowFromSystemServer();
512             }
513         } else {
514             Log.w(TAG, "isFaceHardwareDetected(): Service not connected!");
515         }
516         return false;
517     }
518 
519     /**
520      * @hide
521      */
522     @RequiresPermission(USE_BIOMETRIC_INTERNAL)
addLockoutResetCallback(final LockoutResetCallback callback)523     public void addLockoutResetCallback(final LockoutResetCallback callback) {
524         if (mService != null) {
525             try {
526                 final PowerManager powerManager = mContext.getSystemService(PowerManager.class);
527                 mService.addLockoutResetCallback(
528                         new IBiometricServiceLockoutResetCallback.Stub() {
529 
530                             @Override
531                             public void onLockoutReset(long deviceId,
532                                     IRemoteCallback serverCallback)
533                                     throws RemoteException {
534                                 try {
535                                     final PowerManager.WakeLock wakeLock = powerManager.newWakeLock(
536                                             PowerManager.PARTIAL_WAKE_LOCK,
537                                             "faceLockoutResetCallback");
538                                     wakeLock.acquire();
539                                     mHandler.post(() -> {
540                                         try {
541                                             callback.onLockoutReset();
542                                         } finally {
543                                             wakeLock.release();
544                                         }
545                                     });
546                                 } finally {
547                                     serverCallback.sendResult(null /* data */);
548                                 }
549                             }
550                         });
551             } catch (RemoteException e) {
552                 throw e.rethrowFromSystemServer();
553             }
554         } else {
555             Log.w(TAG, "addLockoutResetCallback(): Service not connected!");
556         }
557     }
558 
getCurrentUserId()559     private int getCurrentUserId() {
560         try {
561             return ActivityManager.getService().getCurrentUser().id;
562         } catch (RemoteException e) {
563             throw e.rethrowFromSystemServer();
564         }
565     }
566 
cancelEnrollment()567     private void cancelEnrollment() {
568         if (mService != null) {
569             try {
570                 mService.cancelEnrollment(mToken);
571             } catch (RemoteException e) {
572                 throw e.rethrowFromSystemServer();
573             }
574         }
575     }
576 
cancelAuthentication(CryptoObject cryptoObject)577     private void cancelAuthentication(CryptoObject cryptoObject) {
578         if (mService != null) {
579             try {
580                 mService.cancelAuthentication(mToken, mContext.getOpPackageName());
581             } catch (RemoteException e) {
582                 throw e.rethrowFromSystemServer();
583             }
584         }
585     }
586 
587     /**
588      * @hide
589      */
getErrorString(Context context, int errMsg, int vendorCode)590     public static String getErrorString(Context context, int errMsg, int vendorCode) {
591         switch (errMsg) {
592             case FACE_ERROR_HW_UNAVAILABLE:
593                 return context.getString(
594                         com.android.internal.R.string.face_error_hw_not_available);
595             case FACE_ERROR_UNABLE_TO_PROCESS:
596                 return context.getString(
597                         com.android.internal.R.string.face_error_unable_to_process);
598             case FACE_ERROR_TIMEOUT:
599                 return context.getString(com.android.internal.R.string.face_error_timeout);
600             case FACE_ERROR_NO_SPACE:
601                 return context.getString(com.android.internal.R.string.face_error_no_space);
602             case FACE_ERROR_CANCELED:
603                 return context.getString(com.android.internal.R.string.face_error_canceled);
604             case FACE_ERROR_LOCKOUT:
605                 return context.getString(com.android.internal.R.string.face_error_lockout);
606             case FACE_ERROR_LOCKOUT_PERMANENT:
607                 return context.getString(
608                         com.android.internal.R.string.face_error_lockout_permanent);
609             case FACE_ERROR_USER_CANCELED:
610                 return context.getString(com.android.internal.R.string.face_error_user_canceled);
611             case FACE_ERROR_NOT_ENROLLED:
612                 return context.getString(com.android.internal.R.string.face_error_not_enrolled);
613             case FACE_ERROR_HW_NOT_PRESENT:
614                 return context.getString(com.android.internal.R.string.face_error_hw_not_present);
615             case BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED:
616                 return context.getString(
617                         com.android.internal.R.string.face_error_security_update_required);
618             case FACE_ERROR_VENDOR: {
619                 String[] msgArray = context.getResources().getStringArray(
620                         com.android.internal.R.array.face_error_vendor);
621                 if (vendorCode < msgArray.length) {
622                     return msgArray[vendorCode];
623                 }
624             }
625         }
626         Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode);
627         return "";
628     }
629 
630     /**
631      * @hide
632      */
getAcquiredString(Context context, int acquireInfo, int vendorCode)633     public static String getAcquiredString(Context context, int acquireInfo, int vendorCode) {
634         switch (acquireInfo) {
635             case FACE_ACQUIRED_GOOD:
636                 return null;
637             case FACE_ACQUIRED_INSUFFICIENT:
638                 return context.getString(R.string.face_acquired_insufficient);
639             case FACE_ACQUIRED_TOO_BRIGHT:
640                 return context.getString(R.string.face_acquired_too_bright);
641             case FACE_ACQUIRED_TOO_DARK:
642                 return context.getString(R.string.face_acquired_too_dark);
643             case FACE_ACQUIRED_TOO_CLOSE:
644                 return context.getString(R.string.face_acquired_too_close);
645             case FACE_ACQUIRED_TOO_FAR:
646                 return context.getString(R.string.face_acquired_too_far);
647             case FACE_ACQUIRED_TOO_HIGH:
648                 return context.getString(R.string.face_acquired_too_high);
649             case FACE_ACQUIRED_TOO_LOW:
650                 return context.getString(R.string.face_acquired_too_low);
651             case FACE_ACQUIRED_TOO_RIGHT:
652                 return context.getString(R.string.face_acquired_too_right);
653             case FACE_ACQUIRED_TOO_LEFT:
654                 return context.getString(R.string.face_acquired_too_left);
655             case FACE_ACQUIRED_POOR_GAZE:
656                 return context.getString(R.string.face_acquired_poor_gaze);
657             case FACE_ACQUIRED_NOT_DETECTED:
658                 return context.getString(R.string.face_acquired_not_detected);
659             case FACE_ACQUIRED_TOO_MUCH_MOTION:
660                 return context.getString(R.string.face_acquired_too_much_motion);
661             case FACE_ACQUIRED_RECALIBRATE:
662                 return context.getString(R.string.face_acquired_recalibrate);
663             case FACE_ACQUIRED_TOO_DIFFERENT:
664                 return context.getString(R.string.face_acquired_too_different);
665             case FACE_ACQUIRED_TOO_SIMILAR:
666                 return context.getString(R.string.face_acquired_too_similar);
667             case FACE_ACQUIRED_PAN_TOO_EXTREME:
668                 return context.getString(R.string.face_acquired_pan_too_extreme);
669             case FACE_ACQUIRED_TILT_TOO_EXTREME:
670                 return context.getString(R.string.face_acquired_tilt_too_extreme);
671             case FACE_ACQUIRED_ROLL_TOO_EXTREME:
672                 return context.getString(R.string.face_acquired_roll_too_extreme);
673             case FACE_ACQUIRED_FACE_OBSCURED:
674                 return context.getString(R.string.face_acquired_obscured);
675             case FACE_ACQUIRED_START:
676                 return null;
677             case FACE_ACQUIRED_SENSOR_DIRTY:
678                 return context.getString(R.string.face_acquired_sensor_dirty);
679             case FACE_ACQUIRED_VENDOR: {
680                 String[] msgArray = context.getResources().getStringArray(
681                         R.array.face_acquired_vendor);
682                 if (vendorCode < msgArray.length) {
683                     return msgArray[vendorCode];
684                 }
685             }
686         }
687         Slog.w(TAG, "Invalid acquired message: " + acquireInfo + ", " + vendorCode);
688         return null;
689     }
690 
691     /**
692      * Used so BiometricPrompt can map the face ones onto existing public constants.
693      * @hide
694      */
getMappedAcquiredInfo(int acquireInfo, int vendorCode)695     public static int getMappedAcquiredInfo(int acquireInfo, int vendorCode) {
696         switch (acquireInfo) {
697             case FACE_ACQUIRED_GOOD:
698                 return BiometricConstants.BIOMETRIC_ACQUIRED_GOOD;
699             case FACE_ACQUIRED_INSUFFICIENT:
700             case FACE_ACQUIRED_TOO_BRIGHT:
701             case FACE_ACQUIRED_TOO_DARK:
702                 return BiometricConstants.BIOMETRIC_ACQUIRED_INSUFFICIENT;
703             case FACE_ACQUIRED_TOO_CLOSE:
704             case FACE_ACQUIRED_TOO_FAR:
705             case FACE_ACQUIRED_TOO_HIGH:
706             case FACE_ACQUIRED_TOO_LOW:
707             case FACE_ACQUIRED_TOO_RIGHT:
708             case FACE_ACQUIRED_TOO_LEFT:
709                 return BiometricConstants.BIOMETRIC_ACQUIRED_PARTIAL;
710             case FACE_ACQUIRED_POOR_GAZE:
711             case FACE_ACQUIRED_NOT_DETECTED:
712             case FACE_ACQUIRED_TOO_MUCH_MOTION:
713             case FACE_ACQUIRED_RECALIBRATE:
714                 return BiometricConstants.BIOMETRIC_ACQUIRED_INSUFFICIENT;
715             case FACE_ACQUIRED_VENDOR:
716                 return BiometricConstants.BIOMETRIC_ACQUIRED_VENDOR_BASE + vendorCode;
717             default:
718                 return BiometricConstants.BIOMETRIC_ACQUIRED_GOOD;
719         }
720     }
721 
722     /**
723      * Container for callback data from {@link FaceManager#authenticate(CryptoObject,
724      * CancellationSignal, int, AuthenticationCallback, Handler)}.
725      */
726     public static class AuthenticationResult {
727         private Face mFace;
728         private CryptoObject mCryptoObject;
729         private int mUserId;
730         private boolean mIsStrongBiometric;
731 
732         /**
733          * Authentication result
734          *
735          * @param crypto the crypto object
736          * @param face   the recognized face data, if allowed.
737          * @hide
738          */
AuthenticationResult(CryptoObject crypto, Face face, int userId, boolean isStrongBiometric)739         public AuthenticationResult(CryptoObject crypto, Face face, int userId,
740                 boolean isStrongBiometric) {
741             mCryptoObject = crypto;
742             mFace = face;
743             mUserId = userId;
744             mIsStrongBiometric = isStrongBiometric;
745         }
746 
747         /**
748          * Obtain the crypto object associated with this transaction
749          *
750          * @return crypto object provided to {@link FaceManager#authenticate
751          * (CryptoObject,
752          * CancellationSignal, int, AuthenticationCallback, Handler)}.
753          */
getCryptoObject()754         public CryptoObject getCryptoObject() {
755             return mCryptoObject;
756         }
757 
758         /**
759          * Obtain the Face associated with this operation. Applications are strongly
760          * discouraged from associating specific faces with specific applications or operations.
761          *
762          * @hide
763          */
getFace()764         public Face getFace() {
765             return mFace;
766         }
767 
768         /**
769          * Obtain the userId for which this face was authenticated.
770          *
771          * @hide
772          */
getUserId()773         public int getUserId() {
774             return mUserId;
775         }
776 
777         /**
778          * Check whether the strength of the face modality associated with this operation is strong
779          * (i.e. not weak or convenience).
780          *
781          * @hide
782          */
isStrongBiometric()783         public boolean isStrongBiometric() {
784             return mIsStrongBiometric;
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                             msg.arg2 == 1 /* isStrongBiometric */);
987                     break;
988                 case MSG_AUTHENTICATION_FAILED:
989                     sendAuthenticatedFailed();
990                     break;
991                 case MSG_ERROR:
992                     sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */,
993                             msg.arg2 /* vendorCode */);
994                     break;
995                 case MSG_REMOVED:
996                     sendRemovedResult((Face) msg.obj, msg.arg1 /* remaining */);
997                     break;
998                 case MSG_SET_FEATURE_COMPLETED:
999                     sendSetFeatureCompleted((boolean) msg.obj /* success */,
1000                             msg.arg1 /* feature */);
1001                     break;
1002                 case MSG_GET_FEATURE_COMPLETED:
1003                     SomeArgs args = (SomeArgs) msg.obj;
1004                     sendGetFeatureCompleted((boolean) args.arg1 /* success */,
1005                             args.argi1 /* feature */,
1006                             (boolean) args.arg2 /* value */);
1007                     args.recycle();
1008                     break;
1009                 default:
1010                     Log.w(TAG, "Unknown message: " + msg.what);
1011             }
1012             Trace.endSection();
1013         }
1014     }
1015 
sendSetFeatureCompleted(boolean success, int feature)1016     private void sendSetFeatureCompleted(boolean success, int feature) {
1017         if (mSetFeatureCallback == null) {
1018             return;
1019         }
1020         mSetFeatureCallback.onCompleted(success, feature);
1021     }
1022 
sendGetFeatureCompleted(boolean success, int feature, boolean value)1023     private void sendGetFeatureCompleted(boolean success, int feature, boolean value) {
1024         if (mGetFeatureCallback == null) {
1025             return;
1026         }
1027         mGetFeatureCallback.onCompleted(success, feature, value);
1028     }
1029 
sendRemovedResult(Face face, int remaining)1030     private void sendRemovedResult(Face face, int remaining) {
1031         if (mRemovalCallback == null) {
1032             return;
1033         }
1034         if (face == null) {
1035             Log.e(TAG, "Received MSG_REMOVED, but face is null");
1036             return;
1037         }
1038         mRemovalCallback.onRemovalSucceeded(face, remaining);
1039     }
1040 
sendErrorResult(long deviceId, int errMsgId, int vendorCode)1041     private void sendErrorResult(long deviceId, int errMsgId, int vendorCode) {
1042         // emulate HAL 2.1 behavior and send real errMsgId
1043         final int clientErrMsgId = errMsgId == FACE_ERROR_VENDOR
1044                 ? (vendorCode + FACE_ERROR_VENDOR_BASE) : errMsgId;
1045         if (mEnrollmentCallback != null) {
1046             mEnrollmentCallback.onEnrollmentError(clientErrMsgId,
1047                     getErrorString(mContext, errMsgId, vendorCode));
1048         } else if (mAuthenticationCallback != null) {
1049             mAuthenticationCallback.onAuthenticationError(clientErrMsgId,
1050                     getErrorString(mContext, errMsgId, vendorCode));
1051         } else if (mRemovalCallback != null) {
1052             mRemovalCallback.onRemovalError(mRemovalFace, clientErrMsgId,
1053                     getErrorString(mContext, errMsgId, vendorCode));
1054         }
1055     }
1056 
sendEnrollResult(Face face, int remaining)1057     private void sendEnrollResult(Face face, int remaining) {
1058         if (mEnrollmentCallback != null) {
1059             mEnrollmentCallback.onEnrollmentProgress(remaining);
1060         }
1061     }
1062 
sendAuthenticatedSucceeded(Face face, int userId, boolean isStrongBiometric)1063     private void sendAuthenticatedSucceeded(Face face, int userId, boolean isStrongBiometric) {
1064         if (mAuthenticationCallback != null) {
1065             final AuthenticationResult result =
1066                     new AuthenticationResult(mCryptoObject, face, userId, isStrongBiometric);
1067             mAuthenticationCallback.onAuthenticationSucceeded(result);
1068         }
1069     }
1070 
sendAuthenticatedFailed()1071     private void sendAuthenticatedFailed() {
1072         if (mAuthenticationCallback != null) {
1073             mAuthenticationCallback.onAuthenticationFailed();
1074         }
1075     }
1076 
sendAcquiredResult(long deviceId, int acquireInfo, int vendorCode)1077     private void sendAcquiredResult(long deviceId, int acquireInfo, int vendorCode) {
1078         if (mAuthenticationCallback != null) {
1079             mAuthenticationCallback.onAuthenticationAcquired(acquireInfo);
1080         }
1081         final String msg = getAcquiredString(mContext, acquireInfo, vendorCode);
1082         final int clientInfo = acquireInfo == FACE_ACQUIRED_VENDOR
1083                 ? (vendorCode + FACE_ACQUIRED_VENDOR_BASE) : acquireInfo;
1084         if (mEnrollmentCallback != null) {
1085             mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg);
1086         } else if (mAuthenticationCallback != null && msg != null) {
1087             mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg);
1088         }
1089     }
1090 }
1091