Android Q以来,一直在推广建议鼓励三方应用BiometricPrompt API。
其效果见:
其号称BiometricService兼容了Fingerprint, Iris, Face这几种识别方案。
有贴《BiometricPrompt之一 - 简单用法》可以看出,BiometricPrompt并没有提供API让3rd-app来决策选择哪种方式来验证,例如:Fingerprint ? Face?
那么当三者均支持时,UI显示规则是怎樣子的?
功能如何? 三者可同时使用吗?
先上【结论】:
三者均支持时,应当是BiometricService查询到第一个支持验证三方应用的feature为 优先,且是独占,具有排他性,并非像锁屏上同时兼容三者。
以Android Q AOSP的代码来看,第一优先是Fingerprint, 其次Iris, 最后Face。
Fingerprint -> Iris -> Face
关键代码如下:
frameworks/base/services/core/java/com/android/server/biometrics/BiometricService.java
914 /**
915 * Checks if there are any available biometrics, and returns the modality. This method also
916 * returns errors through the callback (no biometric feature, hardware not detected, no
917 * templates enrolled, etc). This service must not start authentication if errors are sent.
918 *
919 * @Returns A pair [Modality, Error] with Modality being one of
920 * {@link BiometricAuthenticator#TYPE_NONE},
921 * {@link BiometricAuthenticator#TYPE_FINGERPRINT},
922 * {@link BiometricAuthenticator#TYPE_IRIS},
923 * {@link BiometricAuthenticator#TYPE_FACE}
924 * and the error containing one of the {@link BiometricConstants} errors.
925 */
926 private Pair<Integer, Integer> checkAndGetBiometricModality(int userId) {
927 int modality = TYPE_NONE;
928
929 // No biometric features, send error
930 if (mAuthenticators.isEmpty()) {
931 return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT);
932 }
933
934 // Assuming that authenticators are listed in priority-order, the rest of this function
935 // will go through and find the first authenticator that's available, enrolled, and enabled.
936 // The tricky part is returning the correct error. Error strings that are modality-specific
937 // should also respect the priority-order.
938
939 // Find first authenticator that's detected, enrolled, and enabled.
940 boolean isHardwareDetected = false;
941 boolean hasTemplatesEnrolled = false;
942 boolean enabledForApps = false;
943
944 int firstHwAvailable = TYPE_NONE;
945 for (int i = 0; i < mAuthenticators.size(); i++) {
946 modality = mAuthenticators.get(i).getType();
947 BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator();
948 if (authenticator.isHardwareDetected()) {
949 isHardwareDetected = true;
950 if (firstHwAvailable == TYPE_NONE) {
951 // Store the first one since we want to return the error in correct priority
952 // order.
953 firstHwAvailable = modality;
954 }
955 if (authenticator.hasEnrolledTemplates(userId)) {
956 hasTemplatesEnrolled = true;
957 if (isEnabledForApp(modality, userId)) {
958 // TODO(b/110907543): When face settings (and other settings) have both a
959 // user toggle as well as a work profile settings page, this needs to be
960 // updated to reflect the correct setting.
961 enabledForApps = true;
962 break; // 找到第一个符合条件的biometric type即止
963 }
964 }
965 }
966 }
967
968 // Check error conditions
969 if (!isHardwareDetected) {
970 return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
971 } else if (!hasTemplatesEnrolled) {
972 // Return the modality here so the correct error string can be sent. This error is
973 // preferred over !enabledForApps
974 return new Pair<>(firstHwAvailable, BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS);
975 } else if (!enabledForApps) {
976 return new Pair<>(TYPE_NONE, BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE);
977 }
978
979 return new Pair<>(modality, BiometricConstants.BIOMETRIC_SUCCESS);
980 }
由上段代码可知,从第0个元素开始查询,关键在于mAuthenticators这个List里边存储的第0个对象是谁 以及顺序?
266 // Get and cache the available authenticator (manager) classes. Used since aidl doesn't support
267 // polymorphism :/
268 final ArrayList<Authenticator> mAuthenticators = new ArrayList<>();
这个要回溯到BiometricService服务启动初始化时。
850 /**
851 * Initializes the system service.
852 * <p>
853 * Subclasses must define a single argument constructor that accepts the context
854 * and passes it to super.
855 * </p>
856 *
857 * @param context The system server context.
858 */
859 public BiometricService(Context context) {
860 super(context);
861
862 mAppOps = context.getSystemService(AppOpsManager.class);
863 mEnabledOnKeyguardCallbacks = new ArrayList<>();
864 mSettingObserver = new SettingObserver(mHandler);
865
866 final PackageManager pm = context.getPackageManager();
867 mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
868 mHasFeatureIris = pm.hasSystemFeature(PackageManager.FEATURE_IRIS);
869 mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
870
871 try {
872 ActivityManager.getService().registerUserSwitchObserver(
873 new UserSwitchObserver() {
874 @Override
875 public void onUserSwitchComplete(int newUserId) {
876 mSettingObserver.updateContentObserver();
877 mSettingObserver.notifyEnabledOnKeyguardCallbacks(newUserId);
878 }
879 }, BiometricService.class.getName()
880 );
881 } catch (RemoteException e) {
882 Slog.e(TAG, "Failed to register user switch observer", e);
883 }
884 }
885
886 @Override
887 public void onStart() {
888 // TODO: maybe get these on-demand
889 if (mHasFeatureFingerprint) {
890 mFingerprintService = IFingerprintService.Stub.asInterface(
891 ServiceManager.getService(Context.FINGERPRINT_SERVICE));
892 }
893 if (mHasFeatureFace) {
894 mFaceService = IFaceService.Stub.asInterface(
895 ServiceManager.getService(Context.FACE_SERVICE));
896 }
897
898 mActivityTaskManager = ActivityTaskManager.getService();
899 mStatusBarService = IStatusBarService.Stub.asInterface(
900 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
901
902 // Cache the authenticators
903 for (int i = 0; i < FEATURE_ID.length; i++) {
904 if (hasFeature(FEATURE_ID[i])) {
905 Authenticator authenticator =
906 new Authenticator(FEATURE_ID[i], getAuthenticator(FEATURE_ID[i]));
907 mAuthenticators.add(authenticator);// 添加支持feature manager, 例如:FingerprintManager, FaceManager
908 }
909 }
910
911 publishBinderService(Context.BIOMETRIC_SERVICE, new BiometricServiceWrapper());
912 }
加入到mAuthenticators的顺序是取决于FEATURE_ID这个数组,从第0个元素开始添加。
105 private static final int[] FEATURE_ID = {
106 TYPE_FINGERPRINT,
107 TYPE_IRIS,
108 TYPE_FACE
109 };
【结论】
有以上可知,三者同时支持的情况下,有且仅有Fingerprint功能是支持的,优先级最高。
最后来看下isEnabledForApp()这个函数。
981
982 private boolean isEnabledForApp(int modality, int userId) {
983 switch(modality) {
984 case TYPE_FINGERPRINT:
985 return true;
986 case TYPE_IRIS:
987 return true;
988 case TYPE_FACE:
989 return mSettingObserver.getFaceEnabledForApps(userId);
990 default:
991 Slog.w(TAG, "Unsupported modality: " + modality);
992 return false;
993 }
994 }
以及BiometricService调用决策方案checkandgetbiometricmodality()的地方:
1463
1464 private void handleAuthenticate(IBinder token, long sessionId, int userId,
1465 IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
1466 int callingUid, int callingPid, int callingUserId,
1467 IBiometricConfirmDeviceCredentialCallback callback) {
1468
1469 mHandler.post(() -> {
1470 final Pair<Integer, Integer> result = checkAndGetBiometricModality(userId); /// 决策来源
1471 final int modality = result.first;
1472 final int error = result.second;
1473
1474 // Check for errors, notify callback, and return
1475 if (error != BiometricConstants.BIOMETRIC_SUCCESS) {
1476 try {
1477 final String hardwareUnavailable =
1478 getContext().getString(R.string.biometric_error_hw_unavailable);
1479 switch (error) {
1480 case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
1481 receiver.onError(error, hardwareUnavailable);
1482 break;
1483 case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE:
1484 receiver.onError(error, hardwareUnavailable);
1485 break;
1486 case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS:
1487 receiver.onError(error,
1488 getErrorString(modality, error, 0 /* vendorCode */));
1489 break;
1490 default:
1491 Slog.e(TAG, "Unhandled error");
1492 break;
1493 }
1494 } catch (RemoteException e) {
1495 Slog.e(TAG, "Unable to send error", e);
1496 }
1497 return;
1498 }
1499
1500 mCurrentModality = modality; // 得到的决策biometric type
1501
1502 // Start preparing for authentication. Authentication starts when
1503 // all modalities requested have invoked onReadyForAuthentication.
1504 authenticateInternal(token, sessionId, userId, receiver, opPackageName, bundle,
1505 callingUid, callingPid, callingUserId, modality, callback);
1506 });
1507 }
1508
1509 /**
1510 * authenticate() (above) which is called from BiometricPrompt determines which
1511 * modality/modalities to start authenticating with. authenticateInternal() should only be
1512 * used for:
1513 * 1) Preparing <Biometric>Services for authentication when BiometricPrompt#authenticate is,
1514 * invoked, shortly after which BiometricPrompt is shown and authentication starts
1515 * 2) Preparing <Biometric>Services for authentication when BiometricPrompt is already shown
1516 * and the user has pressed "try again"
1517 */
1518 private void authenticateInternal(IBinder token, long sessionId, int userId,
1519 IBiometricServiceReceiver receiver, String opPackageName, Bundle bundle,
1520 int callingUid, int callingPid, int callingUserId, int modality,
1521 IBiometricConfirmDeviceCredentialCallback callback) {
1522 try {
1523 boolean requireConfirmation = bundle.getBoolean(
1524 BiometricPrompt.KEY_REQUIRE_CONFIRMATION, true /* default */);
1525 if ((modality & TYPE_FACE) != 0) {
1526 // Check if the user has forced confirmation to be required in Settings.
1527 requireConfirmation = requireConfirmation
1528 || mSettingObserver.getFaceAlwaysRequireConfirmation(userId);
1529 }
1530 // Generate random cookies to pass to the services that should prepare to start
1531 // authenticating. Store the cookie here and wait for all services to "ack"
1532 // with the cookie. Once all cookies are received, we can show the prompt
1533 // and let the services start authenticating. The cookie should be non-zero.
1534 final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
1535 Slog.d(TAG, "Creating auth session. Modality: " + modality
1536 + ", cookie: " + cookie);
1537 final HashMap<Integer, Integer> authenticators = new HashMap<>();
1538 authenticators.put(modality, cookie);
1539 mPendingAuthSession = new AuthSession(authenticators, token, sessionId, userId,
1540 receiver, opPackageName, bundle, callingUid, callingPid, callingUserId,
1541 modality, requireConfirmation, callback);
1542 mPendingAuthSession.mState = STATE_AUTH_CALLED;
1543 // No polymorphism :(
1544 if ((modality & TYPE_FINGERPRINT) != 0) {
1545 mFingerprintService.prepareForAuthentication(token, sessionId, userId,
1546 mInternalReceiver, opPackageName, cookie,
1547 callingUid, callingPid, callingUserId);
1548 }
1549 if ((modality & TYPE_IRIS) != 0) {
1550 Slog.w(TAG, "Iris unsupported"); // Android Q暂时不支持Iris
1551 }
1552 if ((modality & TYPE_FACE) != 0) {
1553 mFaceService.prepareForAuthentication(requireConfirmation,
1554 token, sessionId, userId, mInternalReceiver, opPackageName,
1555 cookie, callingUid, callingPid, callingUserId);
1556 }
1557 } catch (RemoteException e) {
1558 Slog.e(TAG, "Unable to start authentication", e);
1559 }
1560 }
上面看起来并没有使用else if 结构,看起来像是可以二者同时兼容,但从决策方法来看,一次只能有一个,modality不可能同时支持。
以上结论仅仅针对Android Q生效. 更新的版本已经没有这个有限次序限制了.
[上结论]
BP UI对指纹人脸,密码的支持情况.
1) Android P:API 28 只支持指纹,不支持人脸(无FaceService)和密码. BiometricPrompt直接调用FingerprintManager来完成biometic的申请.
2)Adnroid Q:API 29 支持指纹和人脸, 某一个时刻只支持一种biometric, 且有有限次序, fingerprint > iris > face; SystemUI部分不支持密码,但是BiometricPrompt支持要求密码验证.密码验证不由SYSUI实现. 而在Settings端实现CDC(ConfirmDeviceCredential). BiometricService authenticate()方法中.
3)Android R:API 30 指纹指纹人脸验证, 某一时刻也是只支持一种. 支持密码验证. 且有有限次序,且其策略依然是找到第一个符合要求的biometric.
注意:顺序不一定是fingerprint > iris > face, 取决于设备中R.array.config_biometric_sensors的配置顺序.
开始引入AuthService, 直接BP UI适配密码验证,不再使用CDC界面来验证密码.
4) Android S:API 31 同时支持指纹和人脸识别,也可单独存在,另外,还支持了屏幕指纹. 还支持密码验证.
5) Android T:API 33 同时支持指纹和人脸识别,也可单独存在,另外,还支持了屏幕指纹.
//本次更新只是不再将Udfp单独布局,而是融合到AuthBiometricFingerprintView了而已.
6) Android U:API 34 做的更加复杂了. 前代版本的已经成为legacyPrompt了.
引入了一个FeatureFlag的概念. 使得更多feature配置项来源更加灵活.