1 /** 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.hardware.face; 18 19 import static android.Manifest.permission.INTERACT_ACROSS_USERS; 20 import static android.Manifest.permission.MANAGE_BIOMETRIC; 21 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; 22 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.RequiresPermission; 26 import android.annotation.SystemService; 27 import android.app.ActivityManager; 28 import android.content.Context; 29 import android.hardware.biometrics.BiometricAuthenticator; 30 import android.hardware.biometrics.BiometricConstants; 31 import android.hardware.biometrics.BiometricFaceConstants; 32 import android.hardware.biometrics.CryptoObject; 33 import android.hardware.biometrics.IBiometricServiceLockoutResetCallback; 34 import android.os.Binder; 35 import android.os.CancellationSignal; 36 import android.os.CancellationSignal.OnCancelListener; 37 import android.os.Handler; 38 import android.os.IBinder; 39 import android.os.IRemoteCallback; 40 import android.os.Looper; 41 import android.os.PowerManager; 42 import android.os.RemoteException; 43 import android.os.Trace; 44 import android.os.UserHandle; 45 import android.util.Log; 46 import android.util.Slog; 47 48 import com.android.internal.R; 49 import com.android.internal.os.SomeArgs; 50 51 import java.util.List; 52 53 /** 54 * A class that coordinates access to the face authentication hardware. 55 * @hide 56 */ 57 @SystemService(Context.FACE_SERVICE) 58 public class FaceManager implements BiometricAuthenticator, BiometricFaceConstants { 59 60 private static final String TAG = "FaceManager"; 61 private static final boolean DEBUG = true; 62 private static final int MSG_ENROLL_RESULT = 100; 63 private static final int MSG_ACQUIRED = 101; 64 private static final int MSG_AUTHENTICATION_SUCCEEDED = 102; 65 private static final int MSG_AUTHENTICATION_FAILED = 103; 66 private static final int MSG_ERROR = 104; 67 private static final int MSG_REMOVED = 105; 68 private static final int MSG_GET_FEATURE_COMPLETED = 106; 69 private static final int MSG_SET_FEATURE_COMPLETED = 107; 70 71 private IFaceService mService; 72 private final Context mContext; 73 private IBinder mToken = new Binder(); 74 private AuthenticationCallback mAuthenticationCallback; 75 private EnrollmentCallback mEnrollmentCallback; 76 private RemovalCallback mRemovalCallback; 77 private SetFeatureCallback mSetFeatureCallback; 78 private GetFeatureCallback mGetFeatureCallback; 79 private CryptoObject mCryptoObject; 80 private Face mRemovalFace; 81 private Handler mHandler; 82 83 private IFaceServiceReceiver mServiceReceiver = new IFaceServiceReceiver.Stub() { 84 85 @Override // binder call 86 public void onEnrollResult(long deviceId, int faceId, int remaining) { 87 mHandler.obtainMessage(MSG_ENROLL_RESULT, remaining, 0, 88 new Face(null, faceId, deviceId)).sendToTarget(); 89 } 90 91 @Override // binder call 92 public void onAcquired(long deviceId, int acquireInfo, int vendorCode) { 93 mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode, deviceId).sendToTarget(); 94 } 95 96 @Override // binder call 97 public void onAuthenticationSucceeded(long deviceId, Face face, int userId, 98 boolean isStrongBiometric) { 99 mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, isStrongBiometric ? 1 : 0, 100 face).sendToTarget(); 101 } 102 103 @Override // binder call 104 public void onAuthenticationFailed(long deviceId) { 105 mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget(); 106 } 107 108 @Override // binder call 109 public void onError(long deviceId, int error, int vendorCode) { 110 mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget(); 111 } 112 113 @Override // binder call 114 public void onRemoved(long deviceId, int faceId, int remaining) { 115 mHandler.obtainMessage(MSG_REMOVED, remaining, 0, 116 new Face(null, faceId, deviceId)).sendToTarget(); 117 } 118 119 @Override 120 public void onEnumerated(long deviceId, int faceId, int remaining) { 121 // TODO: Finish. Low priority since it's not used. 122 } 123 124 @Override 125 public void onFeatureSet(boolean success, int feature) { 126 mHandler.obtainMessage(MSG_SET_FEATURE_COMPLETED, feature, 0, success).sendToTarget(); 127 } 128 129 @Override 130 public void onFeatureGet(boolean success, int feature, boolean value) { 131 SomeArgs args = SomeArgs.obtain(); 132 args.arg1 = success; 133 args.argi1 = feature; 134 args.arg2 = value; 135 mHandler.obtainMessage(MSG_GET_FEATURE_COMPLETED, args).sendToTarget(); 136 } 137 }; 138 139 /** 140 * @hide 141 */ FaceManager(Context context, IFaceService service)142 public FaceManager(Context context, IFaceService service) { 143 mContext = context; 144 mService = service; 145 if (mService == null) { 146 Slog.v(TAG, "FaceAuthenticationManagerService was null"); 147 } 148 mHandler = new MyHandler(context); 149 } 150 151 /** 152 * Request authentication of a crypto object. This call operates the face recognition hardware 153 * and starts capturing images. It terminates when 154 * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or 155 * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at 156 * which point the object is no longer valid. The operation can be canceled by using the 157 * provided cancel object. 158 * 159 * @param crypto object associated with the call or null if none required. 160 * @param cancel an object that can be used to cancel authentication 161 * @param flags optional flags; should be 0 162 * @param callback an object to receive authentication events 163 * @param handler an optional handler to handle callback events 164 * @throws IllegalArgumentException if the crypto operation is not supported or is not backed 165 * by 166 * <a href="{@docRoot}training/articles/keystore.html">Android 167 * Keystore facility</a>. 168 * @throws IllegalStateException if the crypto primitive is not initialized. 169 * @hide 170 */ 171 @RequiresPermission(USE_BIOMETRIC_INTERNAL) authenticate(@ullable CryptoObject crypto, @Nullable CancellationSignal cancel, int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler)172 public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel, 173 int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) { 174 authenticate(crypto, cancel, flags, callback, handler, mContext.getUserId()); 175 } 176 177 /** 178 * Use the provided handler thread for events. 179 */ useHandler(Handler handler)180 private void useHandler(Handler handler) { 181 if (handler != null) { 182 mHandler = new MyHandler(handler.getLooper()); 183 } else if (mHandler.getLooper() != mContext.getMainLooper()) { 184 mHandler = new MyHandler(mContext.getMainLooper()); 185 } 186 } 187 188 /** 189 * Request authentication of a crypto object. This call operates the face recognition hardware 190 * and starts capturing images. It terminates when 191 * {@link AuthenticationCallback#onAuthenticationError(int, CharSequence)} or 192 * {@link AuthenticationCallback#onAuthenticationSucceeded(AuthenticationResult)} is called, at 193 * which point the object is no longer valid. The operation can be canceled by using the 194 * provided cancel object. 195 * 196 * @param crypto object associated with the call or null if none required. 197 * @param cancel an object that can be used to cancel authentication 198 * @param flags optional flags; should be 0 199 * @param callback an object to receive authentication events 200 * @param handler an optional handler to handle callback events 201 * @param userId userId to authenticate for 202 * @throws IllegalArgumentException if the crypto operation is not supported or is not backed 203 * by 204 * <a href="{@docRoot}training/articles/keystore.html">Android 205 * Keystore facility</a>. 206 * @throws IllegalStateException if the crypto primitive is not initialized. 207 * @hide 208 */ authenticate(@ullable CryptoObject crypto, @Nullable CancellationSignal cancel, int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler, int userId)209 public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel, 210 int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler, 211 int userId) { 212 if (callback == null) { 213 throw new IllegalArgumentException("Must supply an authentication callback"); 214 } 215 216 if (cancel != null) { 217 if (cancel.isCanceled()) { 218 Log.w(TAG, "authentication already canceled"); 219 return; 220 } else { 221 cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto)); 222 } 223 } 224 225 if (mService != null) { 226 try { 227 useHandler(handler); 228 mAuthenticationCallback = callback; 229 mCryptoObject = crypto; 230 long sessionId = crypto != null ? crypto.getOpId() : 0; 231 Trace.beginSection("FaceManager#authenticate"); 232 mService.authenticate(mToken, sessionId, userId, mServiceReceiver, 233 flags, mContext.getOpPackageName()); 234 } catch (RemoteException e) { 235 Log.w(TAG, "Remote exception while authenticating: ", e); 236 if (callback != null) { 237 // Though this may not be a hardware issue, it will cause apps to give up or 238 // try again later. 239 callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE, 240 getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE, 241 0 /* vendorCode */)); 242 } 243 } finally { 244 Trace.endSection(); 245 } 246 } 247 } 248 249 /** 250 * Request face authentication enrollment. This call operates the face authentication hardware 251 * and starts capturing images. Progress will be indicated by callbacks to the 252 * {@link EnrollmentCallback} object. It terminates when 253 * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} or 254 * {@link EnrollmentCallback#onEnrollmentProgress(int) is called with remaining == 0, at 255 * which point the object is no longer valid. The operation can be canceled by using the 256 * provided cancel object. 257 * 258 * @param token a unique token provided by a recent creation or verification of device 259 * credentials (e.g. pin, pattern or password). 260 * @param cancel an object that can be used to cancel enrollment 261 * @param flags optional flags 262 * @param userId the user to whom this face will belong to 263 * @param callback an object to receive enrollment events 264 * @hide 265 */ 266 @RequiresPermission(MANAGE_BIOMETRIC) enroll(int userId, byte[] token, CancellationSignal cancel, EnrollmentCallback callback, int[] disabledFeatures)267 public void enroll(int userId, byte[] token, CancellationSignal cancel, 268 EnrollmentCallback callback, int[] disabledFeatures) { 269 if (callback == null) { 270 throw new IllegalArgumentException("Must supply an enrollment callback"); 271 } 272 273 if (cancel != null) { 274 if (cancel.isCanceled()) { 275 Log.w(TAG, "enrollment already canceled"); 276 return; 277 } else { 278 cancel.setOnCancelListener(new OnEnrollCancelListener()); 279 } 280 } 281 282 if (mService != null) { 283 try { 284 mEnrollmentCallback = callback; 285 Trace.beginSection("FaceManager#enroll"); 286 mService.enroll(userId, mToken, token, mServiceReceiver, 287 mContext.getOpPackageName(), disabledFeatures); 288 } catch (RemoteException e) { 289 Log.w(TAG, "Remote exception in enroll: ", e); 290 if (callback != null) { 291 // Though this may not be a hardware issue, it will cause apps to give up or 292 // try again later. 293 callback.onEnrollmentError(FACE_ERROR_HW_UNAVAILABLE, 294 getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE, 295 0 /* vendorCode */)); 296 } 297 } finally { 298 Trace.endSection(); 299 } 300 } 301 } 302 303 /** 304 * Requests an auth token to tie sensitive operations to the confirmation of 305 * existing device credentials (e.g. pin/pattern/password). 306 * 307 * @hide 308 */ 309 @RequiresPermission(MANAGE_BIOMETRIC) generateChallenge()310 public long generateChallenge() { 311 long result = 0; 312 if (mService != null) { 313 try { 314 result = mService.generateChallenge(mToken); 315 } catch (RemoteException e) { 316 throw e.rethrowFromSystemServer(); 317 } 318 } 319 return result; 320 } 321 322 /** 323 * Invalidates the current auth token. 324 * 325 * @hide 326 */ 327 @RequiresPermission(MANAGE_BIOMETRIC) revokeChallenge()328 public int revokeChallenge() { 329 int result = 0; 330 if (mService != null) { 331 try { 332 result = mService.revokeChallenge(mToken); 333 } catch (RemoteException e) { 334 throw e.rethrowFromSystemServer(); 335 } 336 } 337 return result; 338 } 339 340 /** 341 * @hide 342 */ 343 @RequiresPermission(MANAGE_BIOMETRIC) setFeature(int userId, int feature, boolean enabled, byte[] token, SetFeatureCallback callback)344 public void setFeature(int userId, int feature, boolean enabled, byte[] token, 345 SetFeatureCallback callback) { 346 if (mService != null) { 347 try { 348 mSetFeatureCallback = callback; 349 mService.setFeature(userId, feature, enabled, token, mServiceReceiver, 350 mContext.getOpPackageName()); 351 } catch (RemoteException e) { 352 throw e.rethrowFromSystemServer(); 353 } 354 } 355 } 356 357 /** 358 * @hide 359 */ 360 @RequiresPermission(MANAGE_BIOMETRIC) getFeature(int userId, int feature, GetFeatureCallback callback)361 public void getFeature(int userId, int feature, GetFeatureCallback callback) { 362 if (mService != null) { 363 try { 364 mGetFeatureCallback = callback; 365 mService.getFeature(userId, feature, mServiceReceiver, mContext.getOpPackageName()); 366 } catch (RemoteException e) { 367 throw e.rethrowFromSystemServer(); 368 } 369 } 370 } 371 372 /** 373 * Pokes the the driver to have it start looking for faces again. 374 * @hide 375 */ 376 @RequiresPermission(MANAGE_BIOMETRIC) userActivity()377 public void userActivity() { 378 if (mService != null) { 379 try { 380 mService.userActivity(); 381 } catch (RemoteException e) { 382 throw e.rethrowFromSystemServer(); 383 } 384 } 385 } 386 387 /** 388 * Sets the active user. This is meant to be used to select the current profile for enrollment 389 * to allow separate enrolled faces for a work profile 390 * 391 * @hide 392 */ 393 @RequiresPermission(MANAGE_BIOMETRIC) 394 @Override setActiveUser(int userId)395 public void setActiveUser(int userId) { 396 if (mService != null) { 397 try { 398 mService.setActiveUser(userId); 399 } catch (RemoteException e) { 400 throw e.rethrowFromSystemServer(); 401 } 402 } 403 } 404 405 /** 406 * Remove given face template from face hardware and/or protected storage. 407 * 408 * @param face the face item to remove 409 * @param userId the user who this face belongs to 410 * @param callback an optional callback to verify that face templates have been 411 * successfully removed. May be null if no callback is required. 412 * @hide 413 */ 414 @RequiresPermission(MANAGE_BIOMETRIC) remove(Face face, int userId, RemovalCallback callback)415 public void remove(Face face, int userId, RemovalCallback callback) { 416 if (mService != null) { 417 try { 418 mRemovalCallback = callback; 419 mRemovalFace = face; 420 mService.remove(mToken, face.getBiometricId(), userId, mServiceReceiver, 421 mContext.getOpPackageName()); 422 } catch (RemoteException e) { 423 Log.w(TAG, "Remote exception in remove: ", e); 424 if (callback != null) { 425 callback.onRemovalError(face, FACE_ERROR_HW_UNAVAILABLE, 426 getErrorString(mContext, FACE_ERROR_HW_UNAVAILABLE, 427 0 /* vendorCode */)); 428 } 429 } 430 } 431 } 432 433 /** 434 * Obtain the enrolled face template. 435 * 436 * @return the current face item 437 * @hide 438 */ 439 @RequiresPermission(MANAGE_BIOMETRIC) getEnrolledFaces(int userId)440 public List<Face> getEnrolledFaces(int userId) { 441 if (mService != null) { 442 try { 443 return mService.getEnrolledFaces(userId, mContext.getOpPackageName()); 444 } catch (RemoteException e) { 445 throw e.rethrowFromSystemServer(); 446 } 447 } 448 return null; 449 } 450 451 /** 452 * Obtain the enrolled face template. 453 * 454 * @return the current face item 455 * @hide 456 */ 457 @RequiresPermission(MANAGE_BIOMETRIC) getEnrolledFaces()458 public List<Face> getEnrolledFaces() { 459 return getEnrolledFaces(UserHandle.myUserId()); 460 } 461 462 /** 463 * Determine if there is a face enrolled. 464 * 465 * @return true if a face is enrolled, false otherwise 466 */ 467 @RequiresPermission(USE_BIOMETRIC_INTERNAL) 468 @Override hasEnrolledTemplates()469 public boolean hasEnrolledTemplates() { 470 if (mService != null) { 471 try { 472 return mService.hasEnrolledFaces( 473 UserHandle.myUserId(), mContext.getOpPackageName()); 474 } catch (RemoteException e) { 475 throw e.rethrowFromSystemServer(); 476 } 477 } 478 return false; 479 } 480 481 /** 482 * @hide 483 */ 484 @RequiresPermission(allOf = { 485 USE_BIOMETRIC_INTERNAL, 486 INTERACT_ACROSS_USERS}) 487 @Override hasEnrolledTemplates(int userId)488 public boolean hasEnrolledTemplates(int userId) { 489 if (mService != null) { 490 try { 491 return mService.hasEnrolledFaces(userId, mContext.getOpPackageName()); 492 } catch (RemoteException e) { 493 throw e.rethrowFromSystemServer(); 494 } 495 } 496 return false; 497 } 498 499 /** 500 * Determine if face authentication sensor hardware is present and functional. 501 * 502 * @return true if hardware is present and functional, false otherwise. 503 */ 504 @RequiresPermission(USE_BIOMETRIC_INTERNAL) 505 @Override isHardwareDetected()506 public boolean isHardwareDetected() { 507 if (mService != null) { 508 try { 509 return mService.isHardwareDetected(mContext.getOpPackageName()); 510 } catch (RemoteException e) { 511 throw e.rethrowFromSystemServer(); 512 } 513 } else { 514 Log.w(TAG, "isFaceHardwareDetected(): Service not connected!"); 515 } 516 return false; 517 } 518 519 /** 520 * @hide 521 */ 522 @RequiresPermission(USE_BIOMETRIC_INTERNAL) addLockoutResetCallback(final LockoutResetCallback callback)523 public void addLockoutResetCallback(final LockoutResetCallback callback) { 524 if (mService != null) { 525 try { 526 final PowerManager powerManager = mContext.getSystemService(PowerManager.class); 527 mService.addLockoutResetCallback( 528 new IBiometricServiceLockoutResetCallback.Stub() { 529 530 @Override 531 public void onLockoutReset(long deviceId, 532 IRemoteCallback serverCallback) 533 throws RemoteException { 534 try { 535 final PowerManager.WakeLock wakeLock = powerManager.newWakeLock( 536 PowerManager.PARTIAL_WAKE_LOCK, 537 "faceLockoutResetCallback"); 538 wakeLock.acquire(); 539 mHandler.post(() -> { 540 try { 541 callback.onLockoutReset(); 542 } finally { 543 wakeLock.release(); 544 } 545 }); 546 } finally { 547 serverCallback.sendResult(null /* data */); 548 } 549 } 550 }); 551 } catch (RemoteException e) { 552 throw e.rethrowFromSystemServer(); 553 } 554 } else { 555 Log.w(TAG, "addLockoutResetCallback(): Service not connected!"); 556 } 557 } 558 getCurrentUserId()559 private int getCurrentUserId() { 560 try { 561 return ActivityManager.getService().getCurrentUser().id; 562 } catch (RemoteException e) { 563 throw e.rethrowFromSystemServer(); 564 } 565 } 566 cancelEnrollment()567 private void cancelEnrollment() { 568 if (mService != null) { 569 try { 570 mService.cancelEnrollment(mToken); 571 } catch (RemoteException e) { 572 throw e.rethrowFromSystemServer(); 573 } 574 } 575 } 576 cancelAuthentication(CryptoObject cryptoObject)577 private void cancelAuthentication(CryptoObject cryptoObject) { 578 if (mService != null) { 579 try { 580 mService.cancelAuthentication(mToken, mContext.getOpPackageName()); 581 } catch (RemoteException e) { 582 throw e.rethrowFromSystemServer(); 583 } 584 } 585 } 586 587 /** 588 * @hide 589 */ getErrorString(Context context, int errMsg, int vendorCode)590 public static String getErrorString(Context context, int errMsg, int vendorCode) { 591 switch (errMsg) { 592 case FACE_ERROR_HW_UNAVAILABLE: 593 return context.getString( 594 com.android.internal.R.string.face_error_hw_not_available); 595 case FACE_ERROR_UNABLE_TO_PROCESS: 596 return context.getString( 597 com.android.internal.R.string.face_error_unable_to_process); 598 case FACE_ERROR_TIMEOUT: 599 return context.getString(com.android.internal.R.string.face_error_timeout); 600 case FACE_ERROR_NO_SPACE: 601 return context.getString(com.android.internal.R.string.face_error_no_space); 602 case FACE_ERROR_CANCELED: 603 return context.getString(com.android.internal.R.string.face_error_canceled); 604 case FACE_ERROR_LOCKOUT: 605 return context.getString(com.android.internal.R.string.face_error_lockout); 606 case FACE_ERROR_LOCKOUT_PERMANENT: 607 return context.getString( 608 com.android.internal.R.string.face_error_lockout_permanent); 609 case FACE_ERROR_USER_CANCELED: 610 return context.getString(com.android.internal.R.string.face_error_user_canceled); 611 case FACE_ERROR_NOT_ENROLLED: 612 return context.getString(com.android.internal.R.string.face_error_not_enrolled); 613 case FACE_ERROR_HW_NOT_PRESENT: 614 return context.getString(com.android.internal.R.string.face_error_hw_not_present); 615 case BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED: 616 return context.getString( 617 com.android.internal.R.string.face_error_security_update_required); 618 case FACE_ERROR_VENDOR: { 619 String[] msgArray = context.getResources().getStringArray( 620 com.android.internal.R.array.face_error_vendor); 621 if (vendorCode < msgArray.length) { 622 return msgArray[vendorCode]; 623 } 624 } 625 } 626 Slog.w(TAG, "Invalid error message: " + errMsg + ", " + vendorCode); 627 return ""; 628 } 629 630 /** 631 * @hide 632 */ getAcquiredString(Context context, int acquireInfo, int vendorCode)633 public static String getAcquiredString(Context context, int acquireInfo, int vendorCode) { 634 switch (acquireInfo) { 635 case FACE_ACQUIRED_GOOD: 636 return null; 637 case FACE_ACQUIRED_INSUFFICIENT: 638 return context.getString(R.string.face_acquired_insufficient); 639 case FACE_ACQUIRED_TOO_BRIGHT: 640 return context.getString(R.string.face_acquired_too_bright); 641 case FACE_ACQUIRED_TOO_DARK: 642 return context.getString(R.string.face_acquired_too_dark); 643 case FACE_ACQUIRED_TOO_CLOSE: 644 return context.getString(R.string.face_acquired_too_close); 645 case FACE_ACQUIRED_TOO_FAR: 646 return context.getString(R.string.face_acquired_too_far); 647 case FACE_ACQUIRED_TOO_HIGH: 648 return context.getString(R.string.face_acquired_too_high); 649 case FACE_ACQUIRED_TOO_LOW: 650 return context.getString(R.string.face_acquired_too_low); 651 case FACE_ACQUIRED_TOO_RIGHT: 652 return context.getString(R.string.face_acquired_too_right); 653 case FACE_ACQUIRED_TOO_LEFT: 654 return context.getString(R.string.face_acquired_too_left); 655 case FACE_ACQUIRED_POOR_GAZE: 656 return context.getString(R.string.face_acquired_poor_gaze); 657 case FACE_ACQUIRED_NOT_DETECTED: 658 return context.getString(R.string.face_acquired_not_detected); 659 case FACE_ACQUIRED_TOO_MUCH_MOTION: 660 return context.getString(R.string.face_acquired_too_much_motion); 661 case FACE_ACQUIRED_RECALIBRATE: 662 return context.getString(R.string.face_acquired_recalibrate); 663 case FACE_ACQUIRED_TOO_DIFFERENT: 664 return context.getString(R.string.face_acquired_too_different); 665 case FACE_ACQUIRED_TOO_SIMILAR: 666 return context.getString(R.string.face_acquired_too_similar); 667 case FACE_ACQUIRED_PAN_TOO_EXTREME: 668 return context.getString(R.string.face_acquired_pan_too_extreme); 669 case FACE_ACQUIRED_TILT_TOO_EXTREME: 670 return context.getString(R.string.face_acquired_tilt_too_extreme); 671 case FACE_ACQUIRED_ROLL_TOO_EXTREME: 672 return context.getString(R.string.face_acquired_roll_too_extreme); 673 case FACE_ACQUIRED_FACE_OBSCURED: 674 return context.getString(R.string.face_acquired_obscured); 675 case FACE_ACQUIRED_START: 676 return null; 677 case FACE_ACQUIRED_SENSOR_DIRTY: 678 return context.getString(R.string.face_acquired_sensor_dirty); 679 case FACE_ACQUIRED_VENDOR: { 680 String[] msgArray = context.getResources().getStringArray( 681 R.array.face_acquired_vendor); 682 if (vendorCode < msgArray.length) { 683 return msgArray[vendorCode]; 684 } 685 } 686 } 687 Slog.w(TAG, "Invalid acquired message: " + acquireInfo + ", " + vendorCode); 688 return null; 689 } 690 691 /** 692 * Used so BiometricPrompt can map the face ones onto existing public constants. 693 * @hide 694 */ getMappedAcquiredInfo(int acquireInfo, int vendorCode)695 public static int getMappedAcquiredInfo(int acquireInfo, int vendorCode) { 696 switch (acquireInfo) { 697 case FACE_ACQUIRED_GOOD: 698 return BiometricConstants.BIOMETRIC_ACQUIRED_GOOD; 699 case FACE_ACQUIRED_INSUFFICIENT: 700 case FACE_ACQUIRED_TOO_BRIGHT: 701 case FACE_ACQUIRED_TOO_DARK: 702 return BiometricConstants.BIOMETRIC_ACQUIRED_INSUFFICIENT; 703 case FACE_ACQUIRED_TOO_CLOSE: 704 case FACE_ACQUIRED_TOO_FAR: 705 case FACE_ACQUIRED_TOO_HIGH: 706 case FACE_ACQUIRED_TOO_LOW: 707 case FACE_ACQUIRED_TOO_RIGHT: 708 case FACE_ACQUIRED_TOO_LEFT: 709 return BiometricConstants.BIOMETRIC_ACQUIRED_PARTIAL; 710 case FACE_ACQUIRED_POOR_GAZE: 711 case FACE_ACQUIRED_NOT_DETECTED: 712 case FACE_ACQUIRED_TOO_MUCH_MOTION: 713 case FACE_ACQUIRED_RECALIBRATE: 714 return BiometricConstants.BIOMETRIC_ACQUIRED_INSUFFICIENT; 715 case FACE_ACQUIRED_VENDOR: 716 return BiometricConstants.BIOMETRIC_ACQUIRED_VENDOR_BASE + vendorCode; 717 default: 718 return BiometricConstants.BIOMETRIC_ACQUIRED_GOOD; 719 } 720 } 721 722 /** 723 * Container for callback data from {@link FaceManager#authenticate(CryptoObject, 724 * CancellationSignal, int, AuthenticationCallback, Handler)}. 725 */ 726 public static class AuthenticationResult { 727 private Face mFace; 728 private CryptoObject mCryptoObject; 729 private int mUserId; 730 private boolean mIsStrongBiometric; 731 732 /** 733 * Authentication result 734 * 735 * @param crypto the crypto object 736 * @param face the recognized face data, if allowed. 737 * @hide 738 */ AuthenticationResult(CryptoObject crypto, Face face, int userId, boolean isStrongBiometric)739 public AuthenticationResult(CryptoObject crypto, Face face, int userId, 740 boolean isStrongBiometric) { 741 mCryptoObject = crypto; 742 mFace = face; 743 mUserId = userId; 744 mIsStrongBiometric = isStrongBiometric; 745 } 746 747 /** 748 * Obtain the crypto object associated with this transaction 749 * 750 * @return crypto object provided to {@link FaceManager#authenticate 751 * (CryptoObject, 752 * CancellationSignal, int, AuthenticationCallback, Handler)}. 753 */ getCryptoObject()754 public CryptoObject getCryptoObject() { 755 return mCryptoObject; 756 } 757 758 /** 759 * Obtain the Face associated with this operation. Applications are strongly 760 * discouraged from associating specific faces with specific applications or operations. 761 * 762 * @hide 763 */ getFace()764 public Face getFace() { 765 return mFace; 766 } 767 768 /** 769 * Obtain the userId for which this face was authenticated. 770 * 771 * @hide 772 */ getUserId()773 public int getUserId() { 774 return mUserId; 775 } 776 777 /** 778 * Check whether the strength of the face modality associated with this operation is strong 779 * (i.e. not weak or convenience). 780 * 781 * @hide 782 */ isStrongBiometric()783 public boolean isStrongBiometric() { 784 return mIsStrongBiometric; 785 } 786 } 787 788 /** 789 * Callback structure provided to {@link FaceManager#authenticate(CryptoObject, 790 * CancellationSignal, int, AuthenticationCallback, Handler)}. Users of {@link 791 * FaceManager#authenticate(CryptoObject, CancellationSignal, 792 * int, AuthenticationCallback, Handler) } must provide an implementation of this for listening 793 * to face events. 794 */ 795 public abstract static class AuthenticationCallback 796 extends BiometricAuthenticator.AuthenticationCallback { 797 798 /** 799 * Called when an unrecoverable error has been encountered and the operation is complete. 800 * No further callbacks will be made on this object. 801 * 802 * @param errorCode An integer identifying the error message 803 * @param errString A human-readable error string that can be shown in UI 804 */ onAuthenticationError(int errorCode, CharSequence errString)805 public void onAuthenticationError(int errorCode, CharSequence errString) { 806 } 807 808 /** 809 * Called when a recoverable error has been encountered during authentication. The help 810 * string is provided to give the user guidance for what went wrong, such as 811 * "Sensor dirty, please clean it." 812 * 813 * @param helpCode An integer identifying the error message 814 * @param helpString A human-readable string that can be shown in UI 815 */ onAuthenticationHelp(int helpCode, CharSequence helpString)816 public void onAuthenticationHelp(int helpCode, CharSequence helpString) { 817 } 818 819 /** 820 * Called when a face is recognized. 821 * 822 * @param result An object containing authentication-related data 823 */ onAuthenticationSucceeded(AuthenticationResult result)824 public void onAuthenticationSucceeded(AuthenticationResult result) { 825 } 826 827 /** 828 * Called when a face is detected but not recognized. 829 */ onAuthenticationFailed()830 public void onAuthenticationFailed() { 831 } 832 833 /** 834 * Called when a face image has been acquired, but wasn't processed yet. 835 * 836 * @param acquireInfo one of FACE_ACQUIRED_* constants 837 * @hide 838 */ onAuthenticationAcquired(int acquireInfo)839 public void onAuthenticationAcquired(int acquireInfo) { 840 } 841 } 842 843 /** 844 * Callback structure provided to {@link FaceManager#enroll(long, 845 * EnrollmentCallback, CancellationSignal, int). Users of {@link #FaceAuthenticationManager()} 846 * must provide an implementation of this to {@link FaceManager#enroll(long, 847 * CancellationSignal, int, EnrollmentCallback) for listening to face enrollment events. 848 * 849 * @hide 850 */ 851 public abstract static class EnrollmentCallback { 852 853 /** 854 * Called when an unrecoverable error has been encountered and the operation is complete. 855 * No further callbacks will be made on this object. 856 * 857 * @param errMsgId An integer identifying the error message 858 * @param errString A human-readable error string that can be shown in UI 859 */ onEnrollmentError(int errMsgId, CharSequence errString)860 public void onEnrollmentError(int errMsgId, CharSequence errString) { 861 } 862 863 /** 864 * Called when a recoverable error has been encountered during enrollment. The help 865 * string is provided to give the user guidance for what went wrong, such as 866 * "Image too dark, uncover light source" or what they need to do next, such as 867 * "Rotate face up / down." 868 * 869 * @param helpMsgId An integer identifying the error message 870 * @param helpString A human-readable string that can be shown in UI 871 */ onEnrollmentHelp(int helpMsgId, CharSequence helpString)872 public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) { 873 } 874 875 /** 876 * Called as each enrollment step progresses. Enrollment is considered complete when 877 * remaining reaches 0. This function will not be called if enrollment fails. See 878 * {@link EnrollmentCallback#onEnrollmentError(int, CharSequence)} 879 * 880 * @param remaining The number of remaining steps 881 */ onEnrollmentProgress(int remaining)882 public void onEnrollmentProgress(int remaining) { 883 } 884 } 885 886 /** 887 * Callback structure provided to {@link #remove}. Users of {@link FaceManager} 888 * may 889 * optionally provide an implementation of this to 890 * {@link #remove(Face, int, RemovalCallback)} for listening to face template 891 * removal events. 892 * 893 * @hide 894 */ 895 public abstract static class RemovalCallback { 896 897 /** 898 * Called when the given face can't be removed. 899 * 900 * @param face The face that the call attempted to remove 901 * @param errMsgId An associated error message id 902 * @param errString An error message indicating why the face id can't be removed 903 */ onRemovalError(Face face, int errMsgId, CharSequence errString)904 public void onRemovalError(Face face, int errMsgId, CharSequence errString) { 905 } 906 907 /** 908 * Called when a given face is successfully removed. 909 * 910 * @param face The face template that was removed. 911 */ onRemovalSucceeded(Face face, int remaining)912 public void onRemovalSucceeded(Face face, int remaining) { 913 } 914 } 915 916 /** 917 * @hide 918 */ 919 public abstract static class LockoutResetCallback { 920 921 /** 922 * Called when lockout period expired and clients are allowed to listen for face 923 * authentication 924 * again. 925 */ onLockoutReset()926 public void onLockoutReset() { 927 } 928 } 929 930 /** 931 * @hide 932 */ 933 public abstract static class SetFeatureCallback { onCompleted(boolean success, int feature)934 public abstract void onCompleted(boolean success, int feature); 935 } 936 937 /** 938 * @hide 939 */ 940 public abstract static class GetFeatureCallback { onCompleted(boolean success, int feature, boolean value)941 public abstract void onCompleted(boolean success, int feature, boolean value); 942 } 943 944 private class OnEnrollCancelListener implements OnCancelListener { 945 @Override onCancel()946 public void onCancel() { 947 cancelEnrollment(); 948 } 949 } 950 951 private class OnAuthenticationCancelListener implements OnCancelListener { 952 private CryptoObject mCrypto; 953 OnAuthenticationCancelListener(CryptoObject crypto)954 OnAuthenticationCancelListener(CryptoObject crypto) { 955 mCrypto = crypto; 956 } 957 958 @Override onCancel()959 public void onCancel() { 960 cancelAuthentication(mCrypto); 961 } 962 } 963 964 private class MyHandler extends Handler { MyHandler(Context context)965 private MyHandler(Context context) { 966 super(context.getMainLooper()); 967 } 968 MyHandler(Looper looper)969 private MyHandler(Looper looper) { 970 super(looper); 971 } 972 973 @Override handleMessage(android.os.Message msg)974 public void handleMessage(android.os.Message msg) { 975 Trace.beginSection("FaceManager#handleMessage: " + Integer.toString(msg.what)); 976 switch (msg.what) { 977 case MSG_ENROLL_RESULT: 978 sendEnrollResult((Face) msg.obj, msg.arg1 /* remaining */); 979 break; 980 case MSG_ACQUIRED: 981 sendAcquiredResult((Long) msg.obj /* deviceId */, msg.arg1 /* acquire info */, 982 msg.arg2 /* vendorCode */); 983 break; 984 case MSG_AUTHENTICATION_SUCCEEDED: 985 sendAuthenticatedSucceeded((Face) msg.obj, msg.arg1 /* userId */, 986 msg.arg2 == 1 /* isStrongBiometric */); 987 break; 988 case MSG_AUTHENTICATION_FAILED: 989 sendAuthenticatedFailed(); 990 break; 991 case MSG_ERROR: 992 sendErrorResult((Long) msg.obj /* deviceId */, msg.arg1 /* errMsgId */, 993 msg.arg2 /* vendorCode */); 994 break; 995 case MSG_REMOVED: 996 sendRemovedResult((Face) msg.obj, msg.arg1 /* remaining */); 997 break; 998 case MSG_SET_FEATURE_COMPLETED: 999 sendSetFeatureCompleted((boolean) msg.obj /* success */, 1000 msg.arg1 /* feature */); 1001 break; 1002 case MSG_GET_FEATURE_COMPLETED: 1003 SomeArgs args = (SomeArgs) msg.obj; 1004 sendGetFeatureCompleted((boolean) args.arg1 /* success */, 1005 args.argi1 /* feature */, 1006 (boolean) args.arg2 /* value */); 1007 args.recycle(); 1008 break; 1009 default: 1010 Log.w(TAG, "Unknown message: " + msg.what); 1011 } 1012 Trace.endSection(); 1013 } 1014 } 1015 sendSetFeatureCompleted(boolean success, int feature)1016 private void sendSetFeatureCompleted(boolean success, int feature) { 1017 if (mSetFeatureCallback == null) { 1018 return; 1019 } 1020 mSetFeatureCallback.onCompleted(success, feature); 1021 } 1022 sendGetFeatureCompleted(boolean success, int feature, boolean value)1023 private void sendGetFeatureCompleted(boolean success, int feature, boolean value) { 1024 if (mGetFeatureCallback == null) { 1025 return; 1026 } 1027 mGetFeatureCallback.onCompleted(success, feature, value); 1028 } 1029 sendRemovedResult(Face face, int remaining)1030 private void sendRemovedResult(Face face, int remaining) { 1031 if (mRemovalCallback == null) { 1032 return; 1033 } 1034 if (face == null) { 1035 Log.e(TAG, "Received MSG_REMOVED, but face is null"); 1036 return; 1037 } 1038 mRemovalCallback.onRemovalSucceeded(face, remaining); 1039 } 1040 sendErrorResult(long deviceId, int errMsgId, int vendorCode)1041 private void sendErrorResult(long deviceId, int errMsgId, int vendorCode) { 1042 // emulate HAL 2.1 behavior and send real errMsgId 1043 final int clientErrMsgId = errMsgId == FACE_ERROR_VENDOR 1044 ? (vendorCode + FACE_ERROR_VENDOR_BASE) : errMsgId; 1045 if (mEnrollmentCallback != null) { 1046 mEnrollmentCallback.onEnrollmentError(clientErrMsgId, 1047 getErrorString(mContext, errMsgId, vendorCode)); 1048 } else if (mAuthenticationCallback != null) { 1049 mAuthenticationCallback.onAuthenticationError(clientErrMsgId, 1050 getErrorString(mContext, errMsgId, vendorCode)); 1051 } else if (mRemovalCallback != null) { 1052 mRemovalCallback.onRemovalError(mRemovalFace, clientErrMsgId, 1053 getErrorString(mContext, errMsgId, vendorCode)); 1054 } 1055 } 1056 sendEnrollResult(Face face, int remaining)1057 private void sendEnrollResult(Face face, int remaining) { 1058 if (mEnrollmentCallback != null) { 1059 mEnrollmentCallback.onEnrollmentProgress(remaining); 1060 } 1061 } 1062 sendAuthenticatedSucceeded(Face face, int userId, boolean isStrongBiometric)1063 private void sendAuthenticatedSucceeded(Face face, int userId, boolean isStrongBiometric) { 1064 if (mAuthenticationCallback != null) { 1065 final AuthenticationResult result = 1066 new AuthenticationResult(mCryptoObject, face, userId, isStrongBiometric); 1067 mAuthenticationCallback.onAuthenticationSucceeded(result); 1068 } 1069 } 1070 sendAuthenticatedFailed()1071 private void sendAuthenticatedFailed() { 1072 if (mAuthenticationCallback != null) { 1073 mAuthenticationCallback.onAuthenticationFailed(); 1074 } 1075 } 1076 sendAcquiredResult(long deviceId, int acquireInfo, int vendorCode)1077 private void sendAcquiredResult(long deviceId, int acquireInfo, int vendorCode) { 1078 if (mAuthenticationCallback != null) { 1079 mAuthenticationCallback.onAuthenticationAcquired(acquireInfo); 1080 } 1081 final String msg = getAcquiredString(mContext, acquireInfo, vendorCode); 1082 final int clientInfo = acquireInfo == FACE_ACQUIRED_VENDOR 1083 ? (vendorCode + FACE_ACQUIRED_VENDOR_BASE) : acquireInfo; 1084 if (mEnrollmentCallback != null) { 1085 mEnrollmentCallback.onEnrollmentHelp(clientInfo, msg); 1086 } else if (mAuthenticationCallback != null && msg != null) { 1087 mAuthenticationCallback.onAuthenticationHelp(clientInfo, msg); 1088 } 1089 } 1090 } 1091