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