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(int userId, byte[] token, CancellationSignal cancel, EnrollmentCallback callback, int[] disabledFeatures)265 public void enroll(int userId, 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(userId, 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 userId, int feature, boolean enabled, byte[] token, SetFeatureCallback callback)342 public void setFeature(int userId, int feature, boolean enabled, byte[] token, 343 SetFeatureCallback callback) { 344 if (mService != null) { 345 try { 346 mSetFeatureCallback = callback; 347 mService.setFeature(userId, feature, enabled, token, mServiceReceiver, 348 mContext.getOpPackageName()); 349 } catch (RemoteException e) { 350 throw e.rethrowFromSystemServer(); 351 } 352 } 353 } 354 355 /** 356 * @hide 357 */ 358 @RequiresPermission(MANAGE_BIOMETRIC) getFeature(int userId, int feature, GetFeatureCallback callback)359 public void getFeature(int userId, int feature, GetFeatureCallback callback) { 360 if (mService != null) { 361 try { 362 mGetFeatureCallback = callback; 363 mService.getFeature(userId, feature, mServiceReceiver, mContext.getOpPackageName()); 364 } catch (RemoteException e) { 365 throw e.rethrowFromSystemServer(); 366 } 367 } 368 } 369 370 /** 371 * Pokes the the driver to have it start looking for faces again. 372 * @hide 373 */ 374 @RequiresPermission(MANAGE_BIOMETRIC) userActivity()375 public void userActivity() { 376 if (mService != null) { 377 try { 378 mService.userActivity(); 379 } catch (RemoteException e) { 380 throw e.rethrowFromSystemServer(); 381 } 382 } 383 } 384 385 /** 386 * Sets the active user. This is meant to be used to select the current profile for enrollment 387 * to allow separate enrolled faces for a work profile 388 * 389 * @hide 390 */ 391 @RequiresPermission(MANAGE_BIOMETRIC) 392 @Override setActiveUser(int userId)393 public void setActiveUser(int userId) { 394 if (mService != null) { 395 try { 396 mService.setActiveUser(userId); 397 } catch (RemoteException e) { 398 throw e.rethrowFromSystemServer(); 399 } 400 } 401 } 402 403 /** 404 * Remove given face template from face hardware and/or protected storage. 405 * 406 * @param face the face item to remove 407 * @param userId the user who this face belongs to 408 * @param callback an optional callback to verify that face templates have been 409 * successfully removed. May be null if no callback is required. 410 * @hide 411 */ 412 @RequiresPermission(MANAGE_BIOMETRIC) remove(Face face, int userId, RemovalCallback callback)413 public void remove(Face face, int userId, RemovalCallback callback) { 414 if (mService != null) { 415 try { 416 mRemovalCallback = callback; 417 mRemovalFace = face; 418 mService.remove(mToken, face.getBiometricId(), userId, mServiceReceiver, 419 mContext.getOpPackageName()); 420 } catch (RemoteException e) { 421 Log.w(TAG, "Remote exception in remove: ", e); 422 if (callback != null) { 423 callback.onRemovalError(face, FACE_ERROR_HW_UNAVAILABLE, 424 getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE, 425 0 /* vendorCode */)); 426 } 427 } 428 } 429 } 430 431 /** 432 * Obtain the enrolled face template. 433 * 434 * @return the current face item 435 * @hide 436 */ 437 @RequiresPermission(MANAGE_BIOMETRIC) getEnrolledFaces(int userId)438 public List<Face> getEnrolledFaces(int userId) { 439 if (mService != null) { 440 try { 441 return mService.getEnrolledFaces(userId, mContext.getOpPackageName()); 442 } catch (RemoteException e) { 443 throw e.rethrowFromSystemServer(); 444 } 445 } 446 return null; 447 } 448 449 /** 450 * Obtain the enrolled face template. 451 * 452 * @return the current face item 453 * @hide 454 */ 455 @RequiresPermission(MANAGE_BIOMETRIC) getEnrolledFaces()456 public List<Face> getEnrolledFaces() { 457 return getEnrolledFaces(UserHandle.myUserId()); 458 } 459 460 /** 461 * Determine if there is a face enrolled. 462 * 463 * @return true if a face is enrolled, false otherwise 464 */ 465 @RequiresPermission(USE_BIOMETRIC_INTERNAL) 466 @Override hasEnrolledTemplates()467 public boolean hasEnrolledTemplates() { 468 if (mService != null) { 469 try { 470 return mService.hasEnrolledFaces( 471 UserHandle.myUserId(), mContext.getOpPackageName()); 472 } catch (RemoteException e) { 473 throw e.rethrowFromSystemServer(); 474 } 475 } 476 return false; 477 } 478 479 /** 480 * @hide 481 */ 482 @RequiresPermission(allOf = { 483 USE_BIOMETRIC_INTERNAL, 484 INTERACT_ACROSS_USERS}) 485 @Override hasEnrolledTemplates(int userId)486 public boolean hasEnrolledTemplates(int userId) { 487 if (mService != null) { 488 try { 489 return mService.hasEnrolledFaces(userId, mContext.getOpPackageName()); 490 } catch (RemoteException e) { 491 throw e.rethrowFromSystemServer(); 492 } 493 } 494 return false; 495 } 496 497 /** 498 * Determine if face authentication sensor hardware is present and functional. 499 * 500 * @return true if hardware is present and functional, false otherwise. 501 */ 502 @RequiresPermission(USE_BIOMETRIC_INTERNAL) 503 @Override isHardwareDetected()504 public boolean isHardwareDetected() { 505 if (mService != null) { 506 try { 507 long deviceId = 0; /* TODO: plumb hardware id to FPMS */ 508 return mService.isHardwareDetected(deviceId, mContext.getOpPackageName()); 509 } catch (RemoteException e) { 510 throw e.rethrowFromSystemServer(); 511 } 512 } else { 513 Log.w(TAG, "isFaceHardwareDetected(): Service not connected!"); 514 } 515 return false; 516 } 517 518 /** 519 * Retrieves the authenticator token for binding keys to the lifecycle 520 * of the calling user's face. Used only by internal clients. 521 * 522 * @hide 523 */ getAuthenticatorId()524 public long getAuthenticatorId() { 525 if (mService != null) { 526 try { 527 return mService.getAuthenticatorId(mContext.getOpPackageName()); 528 } catch (RemoteException e) { 529 throw e.rethrowFromSystemServer(); 530 } 531 } else { 532 Log.w(TAG, "getAuthenticatorId(): Service not connected!"); 533 } 534 return 0; 535 } 536 537 /** 538 * @hide 539 */ 540 @RequiresPermission(USE_BIOMETRIC_INTERNAL) addLockoutResetCallback(final LockoutResetCallback callback)541 public void addLockoutResetCallback(final LockoutResetCallback callback) { 542 if (mService != null) { 543 try { 544 final PowerManager powerManager = mContext.getSystemService(PowerManager.class); 545 mService.addLockoutResetCallback( 546 new IBiometricServiceLockoutResetCallback.Stub() { 547 548 @Override 549 public void onLockoutReset(long deviceId, 550 IRemoteCallback serverCallback) 551 throws RemoteException { 552 try { 553 final PowerManager.WakeLock wakeLock = powerManager.newWakeLock( 554 PowerManager.PARTIAL_WAKE_LOCK, 555 "faceLockoutResetCallback"); 556 wakeLock.acquire(); 557 mHandler.post(() -> { 558 try { 559 callback.onLockoutReset(); 560 } finally { 561 wakeLock.release(); 562 } 563 }); 564 } finally { 565 serverCallback.sendResult(null /* data */); 566 } 567 } 568 }); 569 } catch (RemoteException e) { 570 throw e.rethrowFromSystemServer(); 571 } 572 } else { 573 Log.w(TAG, "addLockoutResetCallback(): Service not connected!"); 574 } 575 } 576 getCurrentUserId()577 private int getCurrentUserId() { 578 try { 579 return ActivityManager.getService().getCurrentUser().id; 580 } catch (RemoteException e) { 581 throw e.rethrowFromSystemServer(); 582 } 583 } 584 cancelEnrollment()585 private void cancelEnrollment() { 586 if (mService != null) { 587 try { 588 mService.cancelEnrollment(mToken); 589 } catch (RemoteException e) { 590 throw e.rethrowFromSystemServer(); 591 } 592 } 593 } 594 cancelAuthentication(CryptoObject cryptoObject)595 private void cancelAuthentication(CryptoObject cryptoObject) { 596 if (mService != null) { 597 try { 598 mService.cancelAuthentication(mToken, mContext.getOpPackageName()); 599 } catch (RemoteException e) { 600 throw e.rethrowFromSystemServer(); 601 } 602 } 603 } 604 605 /** 606 * @hide 607 */ getErrorString(Context context, int errMsg, int vendorCode)608 public static String getErrorString(Context context, int errMsg, int vendorCode) { 609 switch (errMsg) { 610 case FACE_ERROR_HW_UNAVAILABLE: 611 return context.getString( 612 com.android.internal.R.string.face_error_hw_not_available); 613 case FACE_ERROR_UNABLE_TO_PROCESS: 614 return context.getString( 615 com.android.internal.R.string.face_error_unable_to_process); 616 case FACE_ERROR_TIMEOUT: 617 return context.getString(com.android.internal.R.string.face_error_timeout); 618 case FACE_ERROR_NO_SPACE: 619 return context.getString(com.android.internal.R.string.face_error_no_space); 620 case FACE_ERROR_CANCELED: 621 return context.getString(com.android.internal.R.string.face_error_canceled); 622 case FACE_ERROR_LOCKOUT: 623 return context.getString(com.android.internal.R.string.face_error_lockout); 624 case FACE_ERROR_LOCKOUT_PERMANENT: 625 return context.getString( 626 com.android.internal.R.string.face_error_lockout_permanent); 627 case FACE_ERROR_USER_CANCELED: 628 return context.getString(com.android.internal.R.string.face_error_user_canceled); 629 case FACE_ERROR_NOT_ENROLLED: 630 return context.getString(com.android.internal.R.string.face_error_not_enrolled); 631 case FACE_ERROR_HW_NOT_PRESENT: 632 return context.getString(com.android.internal.R.string.face_error_hw_not_present); 633 case FACE_ERROR_VENDOR: { 634 String[] msgArray = context.getResources().getStringArray( 635 com.android.internal.R.array.face_error_vendor); 636 if (vendorCode < msgArray.length) { 637 return msgArray[vendorCode]; 638 } 639 } 640 } 641 Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode); 642 return ""; 643 } 644 645 /** 646 * @hide 647 */ getAcquiredString(Context context, int acquireInfo, int vendorCode)648 public static String getAcquiredString(Context context, int acquireInfo, int vendorCode) { 649 switch (acquireInfo) { 650 case FACE_ACQUIRED_GOOD: 651 return null; 652 case FACE_ACQUIRED_INSUFFICIENT: 653 return context.getString(R.string.face_acquired_insufficient); 654 case FACE_ACQUIRED_TOO_BRIGHT: 655 return context.getString(R.string.face_acquired_too_bright); 656 case FACE_ACQUIRED_TOO_DARK: 657 return context.getString(R.string.face_acquired_too_dark); 658 case FACE_ACQUIRED_TOO_CLOSE: 659 return context.getString(R.string.face_acquired_too_close); 660 case FACE_ACQUIRED_TOO_FAR: 661 return context.getString(R.string.face_acquired_too_far); 662 case FACE_ACQUIRED_TOO_HIGH: 663 return context.getString(R.string.face_acquired_too_high); 664 case FACE_ACQUIRED_TOO_LOW: 665 return context.getString(R.string.face_acquired_too_low); 666 case FACE_ACQUIRED_TOO_RIGHT: 667 return context.getString(R.string.face_acquired_too_right); 668 case FACE_ACQUIRED_TOO_LEFT: 669 return context.getString(R.string.face_acquired_too_left); 670 case FACE_ACQUIRED_POOR_GAZE: 671 return context.getString(R.string.face_acquired_poor_gaze); 672 case FACE_ACQUIRED_NOT_DETECTED: 673 return context.getString(R.string.face_acquired_not_detected); 674 case FACE_ACQUIRED_TOO_MUCH_MOTION: 675 return context.getString(R.string.face_acquired_too_much_motion); 676 case FACE_ACQUIRED_RECALIBRATE: 677 return context.getString(R.string.face_acquired_recalibrate); 678 case FACE_ACQUIRED_TOO_DIFFERENT: 679 return context.getString(R.string.face_acquired_too_different); 680 case FACE_ACQUIRED_TOO_SIMILAR: 681 return context.getString(R.string.face_acquired_too_similar); 682 case FACE_ACQUIRED_PAN_TOO_EXTREME: 683 return context.getString(R.string.face_acquired_pan_too_extreme); 684 case FACE_ACQUIRED_TILT_TOO_EXTREME: 685 return context.getString(R.string.face_acquired_tilt_too_extreme); 686 case FACE_ACQUIRED_ROLL_TOO_EXTREME: 687 return context.getString(R.string.face_acquired_roll_too_extreme); 688 case FACE_ACQUIRED_FACE_OBSCURED: 689 return context.getString(R.string.face_acquired_obscured); 690 case FACE_ACQUIRED_START: 691 return null; 692 case FACE_ACQUIRED_SENSOR_DIRTY: 693 return context.getString(R.string.face_acquired_sensor_dirty); 694 case FACE_ACQUIRED_VENDOR: { 695 String[] msgArray = context.getResources().getStringArray( 696 R.array.face_acquired_vendor); 697 if (vendorCode < msgArray.length) { 698 return msgArray[vendorCode]; 699 } 700 } 701 } 702 Slog.w(TAG, "Invalid acquired message: " + acquireInfo + ", " + vendorCode); 703 return null; 704 } 705 706 /** 707 * Used so BiometricPrompt can map the face ones onto existing public constants. 708 * @hide 709 */ getMappedAcquiredInfo(int acquireInfo, int vendorCode)710 public static int getMappedAcquiredInfo(int acquireInfo, int vendorCode) { 711 switch (acquireInfo) { 712 case FACE_ACQUIRED_GOOD: 713 return BiometricConstants.BIOMETRIC_ACQUIRED_GOOD; 714 case FACE_ACQUIRED_INSUFFICIENT: 715 case FACE_ACQUIRED_TOO_BRIGHT: 716 case FACE_ACQUIRED_TOO_DARK: 717 return BiometricConstants.BIOMETRIC_ACQUIRED_INSUFFICIENT; 718 case FACE_ACQUIRED_TOO_CLOSE: 719 case FACE_ACQUIRED_TOO_FAR: 720 case FACE_ACQUIRED_TOO_HIGH: 721 case FACE_ACQUIRED_TOO_LOW: 722 case FACE_ACQUIRED_TOO_RIGHT: 723 case FACE_ACQUIRED_TOO_LEFT: 724 return BiometricConstants.BIOMETRIC_ACQUIRED_PARTIAL; 725 case FACE_ACQUIRED_POOR_GAZE: 726 case FACE_ACQUIRED_NOT_DETECTED: 727 case FACE_ACQUIRED_TOO_MUCH_MOTION: 728 case FACE_ACQUIRED_RECALIBRATE: 729 return BiometricConstants.BIOMETRIC_ACQUIRED_INSUFFICIENT; 730 case FACE_ACQUIRED_VENDOR: 731 return BiometricConstants.BIOMETRIC_ACQUIRED_VENDOR_BASE + vendorCode; 732 default: 733 return BiometricConstants.BIOMETRIC_ACQUIRED_GOOD; 734 } 735 } 736 737 /** 738 * Container for callback data from {@link FaceManager#authenticate(CryptoObject, 739 * CancellationSignal, int, AuthenticationCallback, Handler)}. 740 */ 741 public static class AuthenticationResult { 742 private Face mFace; 743 private CryptoObject mCryptoObject; 744 private int mUserId; 745 746 /** 747 * Authentication result 748 * 749 * @param crypto the crypto object 750 * @param face the recognized face data, if allowed. 751 * @hide 752 */ AuthenticationResult(CryptoObject crypto, Face face, int userId)753 public AuthenticationResult(CryptoObject crypto, Face face, int userId) { 754 mCryptoObject = crypto; 755 mFace = face; 756 mUserId = userId; 757 } 758 759 /** 760 * Obtain the crypto object associated with this transaction 761 * 762 * @return crypto object provided to {@link FaceManager#authenticate 763 * (CryptoObject, 764 * CancellationSignal, int, AuthenticationCallback, Handler)}. 765 */ getCryptoObject()766 public CryptoObject getCryptoObject() { 767 return mCryptoObject; 768 } 769 770 /** 771 * Obtain the Face associated with this operation. Applications are strongly 772 * discouraged from associating specific faces with specific applications or operations. 773 * 774 * @hide 775 */ getFace()776 public Face getFace() { 777 return mFace; 778 } 779 780 /** 781 * Obtain the userId for which this face was authenticated. 782 * 783 * @hide 784 */ getUserId()785 public int getUserId() { 786 return mUserId; 787 } 788 } 789 790 /** 791 * Callback structure provided to {@link FaceManager#authenticate(CryptoObject, 792 * CancellationSignal, int, AuthenticationCallback, Handler)}. Users of {@link 793 * FaceManager#authenticate(CryptoObject, CancellationSignal, 794 * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening 795 * to face events. 796 */ 797 public abstract static class AuthenticationCallback 798 extends BiometricAuthenticator.AuthenticationCallback { 799 800 /** 801 * Called when an unrecoverable error has been encountered and the operation is complete. 802 * No further callbacks will be made on this object. 803 * 804 * @param errorCode An integer identifying the error message 805 * @param errString A human-readable error string that can be shown in UI 806 */ onAuthenticationError(int errorCode, CharSequence errString)807 public void onAuthenticationError(int errorCode, CharSequence errString) { 808 } 809 810 /** 811 * Called when a recoverable error has been encountered during authentication. The help 812 * string is provided to give the user guidance for what went wrong, such as 813 * "Sensor dirty, please clean it." 814 * 815 * @param helpCode An integer identifying the error message 816 * @param helpString A human-readable string that can be shown in UI 817 */ onAuthenticationHelp(int helpCode, CharSequence helpString)818 public void onAuthenticationHelp(int helpCode, CharSequence helpString) { 819 } 820 821 /** 822 * Called when a face is recognized. 823 * 824 * @param result An object containing authentication-related data 825 */ onAuthenticationSucceeded(AuthenticationResult result)826 public void onAuthenticationSucceeded(AuthenticationResult result) { 827 } 828 829 /** 830 * Called when a face is detected but not recognized. 831 */ onAuthenticationFailed()832 public void onAuthenticationFailed() { 833 } 834 835 /** 836 * Called when a face image has been acquired, but wasn't processed yet. 837 * 838 * @param acquireInfo one of FACE_ACQUIRED_* constants 839 * @hide 840 */ onAuthenticationAcquired(int acquireInfo)841 public void onAuthenticationAcquired(int acquireInfo) { 842 } 843 } 844 845 /** 846 * Callback structure provided to {@link FaceManager#enroll(long, 847 * EnrollmentCallback, CancellationSignal, int). Users of {@link #FaceAuthenticationManager()} 848 * must provide an implementation of this to {@link FaceManager#enroll(long, 849 * CancellationSignal, int, EnrollmentCallback) for listening to face enrollment events. 850 * 851 * @hide 852 */ 853 public abstract static class EnrollmentCallback { 854 855 /** 856 * Called when an unrecoverable error has been encountered and the operation is complete. 857 * No further callbacks will be made on this object. 858 * 859 * @param errMsgId An integer identifying the error message 860 * @param errString A human-readable error string that can be shown in UI 861 */ onEnrollmentError(int errMsgId, CharSequence errString)862 public void onEnrollmentError(int errMsgId, CharSequence errString) { 863 } 864 865 /** 866 * Called when a recoverable error has been encountered during enrollment. The help 867 * string is provided to give the user guidance for what went wrong, such as 868 * "Image too dark, uncover light source" or what they need to do next, such as 869 * "Rotate face up / down." 870 * 871 * @param helpMsgId An integer identifying the error message 872 * @param helpString A human-readable string that can be shown in UI 873 */ onEnrollmentHelp(int helpMsgId, CharSequence helpString)874 public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) { 875 } 876 877 /** 878 * Called as each enrollment step progresses. Enrollment is considered complete when 879 * remaining reaches 0. This function will not be called if enrollment fails. See 880 * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} 881 * 882 * @param remaining The number of remaining steps 883 */ onEnrollmentProgress(int remaining)884 public void onEnrollmentProgress(int remaining) { 885 } 886 } 887 888 /** 889 * Callback structure provided to {@link #remove}. Users of {@link FaceManager} 890 * may 891 * optionally provide an implementation of this to 892 * {@link #remove(Face, int, RemovalCallback)} for listening to face template 893 * removal events. 894 * 895 * @hide 896 */ 897 public abstract static class RemovalCallback { 898 899 /** 900 * Called when the given face can't be removed. 901 * 902 * @param face The face that the call attempted to remove 903 * @param errMsgId An associated error message id 904 * @param errString An error message indicating why the face id can't be removed 905 */ onRemovalError(Face face, int errMsgId, CharSequence errString)906 public void onRemovalError(Face face, int errMsgId, CharSequence errString) { 907 } 908 909 /** 910 * Called when a given face is successfully removed. 911 * 912 * @param face The face template that was removed. 913 */ onRemovalSucceeded(Face face, int remaining)914 public void onRemovalSucceeded(Face face, int remaining) { 915 } 916 } 917 918 /** 919 * @hide 920 */ 921 public abstract static class LockoutResetCallback { 922 923 /** 924 * Called when lockout period expired and clients are allowed to listen for face 925 * authentication 926 * again. 927 */ onLockoutReset()928 public void onLockoutReset() { 929 } 930 } 931 932 /** 933 * @hide 934 */ 935 public abstract static class SetFeatureCallback { onCompleted(boolean success, int feature)936 public abstract void onCompleted(boolean success, int feature); 937 } 938 939 /** 940 * @hide 941 */ 942 public abstract static class GetFeatureCallback { onCompleted(boolean success, int feature, boolean value)943 public abstract void onCompleted(boolean success, int feature, boolean value); 944 } 945 946 private class OnEnrollCancelListener implements OnCancelListener { 947 @Override onCancel()948 public void onCancel() { 949 cancelEnrollment(); 950 } 951 } 952 953 private class OnAuthenticationCancelListener implements OnCancelListener { 954 private CryptoObject mCrypto; 955 OnAuthenticationCancelListener(CryptoObject crypto)956 OnAuthenticationCancelListener(CryptoObject crypto) { 957 mCrypto = crypto; 958 } 959 960 @Override onCancel()961 public void onCancel() { 962 cancelAuthentication(mCrypto); 963 } 964 } 965 966 private class MyHandler extends Handler { MyHandler(Context context)967 private MyHandler(Context context) { 968 super(context.getMainLooper()); 969 } 970 MyHandler(Looper looper)971 private MyHandler(Looper looper) { 972 super(looper); 973 } 974 975 @Override handleMessage(android.os.Message msg)976 public void handleMessage(android.os.Message msg) { 977 Trace.beginSection("FaceManager#handleMessage: " + Integer.toString(msg.what)); 978 switch (msg.what) { 979 case MSG_ENROLL_RESULT: 980 sendEnrollResult((Face) msg.obj, msg.arg1 /* remaining */); 981 break; 982 case MSG_ACQUIRED: 983 sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */, 984 msg.arg2 /* vendorCode */); 985 break; 986 case MSG_AUTHENTICATION_SUCCEEDED: 987 sendAuthenticatedSucceeded((Face) msg.obj, msg.arg1 /* userId */); 988 break; 989 case MSG_AUTHENTICATION_FAILED: 990 sendAuthenticatedFailed(); 991 break; 992 case MSG_ERROR: 993 sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */, 994 msg.arg2 /* vendorCode */); 995 break; 996 case MSG_REMOVED: 997 sendRemovedResult((Face) msg.obj, msg.arg1 /* remaining */); 998 break; 999 case MSG_SET_FEATURE_COMPLETED: 1000 sendSetFeatureCompleted((boolean) msg.obj /* success */, 1001 msg.arg1 /* feature */); 1002 break; 1003 case MSG_GET_FEATURE_COMPLETED: 1004 SomeArgs args = (SomeArgs) msg.obj; 1005 sendGetFeatureCompleted((boolean) args.arg1 /* success */, 1006 args.argi1 /* feature */, 1007 (boolean) args.arg2 /* value */); 1008 args.recycle(); 1009 break; 1010 default: 1011 Log.w(TAG, "Unknown message: " + msg.what); 1012 } 1013 Trace.endSection(); 1014 } 1015 } 1016 sendSetFeatureCompleted(boolean success, int feature)1017 private void sendSetFeatureCompleted(boolean success, int feature) { 1018 if (mSetFeatureCallback == null) { 1019 return; 1020 } 1021 mSetFeatureCallback.onCompleted(success, feature); 1022 } 1023 sendGetFeatureCompleted(boolean success, int feature, boolean value)1024 private void sendGetFeatureCompleted(boolean success, int feature, boolean value) { 1025 if (mGetFeatureCallback == null) { 1026 return; 1027 } 1028 mGetFeatureCallback.onCompleted(success, feature, value); 1029 } 1030 sendRemovedResult(Face face, int remaining)1031 private void sendRemovedResult(Face face, int remaining) { 1032 if (mRemovalCallback == null) { 1033 return; 1034 } 1035 if (face == null) { 1036 Log.e(TAG, "Received MSG_REMOVED, but face is null"); 1037 return; 1038 } 1039 mRemovalCallback.onRemovalSucceeded(face, remaining); 1040 } 1041 sendErrorResult(long deviceId, int errMsgId, int vendorCode)1042 private void sendErrorResult(long deviceId, int errMsgId, int vendorCode) { 1043 // emulate HAL 2.1 behavior and send real errMsgId 1044 final int clientErrMsgId = errMsgId == FACE_ERROR_VENDOR 1045 ? (vendorCode + FACE_ERROR_VENDOR_BASE) : errMsgId; 1046 if (mEnrollmentCallback != null) { 1047 mEnrollmentCallback.onEnrollmentError(clientErrMsgId, 1048 getErrorString(mContext, errMsgId, vendorCode)); 1049 } else if (mAuthenticationCallback != null) { 1050 mAuthenticationCallback.onAuthenticationError(clientErrMsgId, 1051 getErrorString(mContext, errMsgId, vendorCode)); 1052 } else if (mRemovalCallback != null) { 1053 mRemovalCallback.onRemovalError(mRemovalFace, clientErrMsgId, 1054 getErrorString(mContext, errMsgId, vendorCode)); 1055 } 1056 } 1057 sendEnrollResult(Face face, int remaining)1058 private void sendEnrollResult(Face face, int remaining) { 1059 if (mEnrollmentCallback != null) { 1060 mEnrollmentCallback.onEnrollmentProgress(remaining); 1061 } 1062 } 1063 sendAuthenticatedSucceeded(Face face, int userId)1064 private void sendAuthenticatedSucceeded(Face face, int userId) { 1065 if (mAuthenticationCallback != null) { 1066 final AuthenticationResult result = 1067 new AuthenticationResult(mCryptoObject, face, userId); 1068 mAuthenticationCallback.onAuthenticationSucceeded(result); 1069 } 1070 } 1071 sendAuthenticatedFailed()1072 private void sendAuthenticatedFailed() { 1073 if (mAuthenticationCallback != null) { 1074 mAuthenticationCallback.onAuthenticationFailed(); 1075 } 1076 } 1077 sendAcquiredResult(long deviceId, int acquireInfo, int vendorCode)1078 private void sendAcquiredResult(long deviceId, int acquireInfo, int vendorCode) { 1079 if (mAuthenticationCallback != null) { 1080 mAuthenticationCallback.onAuthenticationAcquired(acquireInfo); 1081 } 1082 final String msg = getAcquiredString(mContext, acquireInfo, vendorCode); 1083 final int clientInfo = acquireInfo == FACE_ACQUIRED_VENDOR 1084 ? (vendorCode + FACE_ACQUIRED_VENDOR_BASE) : acquireInfo; 1085 if (mEnrollmentCallback != null) { 1086 mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg); 1087 } else if (mAuthenticationCallback != null && msg != null) { 1088 mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg); 1089 } 1090 } 1091 } 1092