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