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