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