BiometricPrompt之三 - Fingerprint, Iris, Face UI优先级

Android Q以来,一直在推广建议鼓励三方应用BiometricPrompt API。

其效果见:

BiometricPrompt对话框效果

其号称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配置项来源更加灵活.

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值