BiometricPrompt之三 - Fingerprint, Iris, Face UI优先级
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不可能同時支持。
總結
以上是生活随笔為你收集整理的BiometricPrompt之三 - Fingerprint, Iris, Face UI优先级的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 修改Tomcat8的默认访问端口8080
- 下一篇: 计算机表格怎么求面积,在wps表格中怎么