• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 com.android.server.biometrics;
18 
19 
20 // TODO(b/141025588): Create separate internal and external permissions for AuthService.
21 // TODO(b/141025588): Get rid of the USE_FINGERPRINT permission.
22 
23 import static android.Manifest.permission.TEST_BIOMETRIC;
24 import static android.Manifest.permission.USE_BIOMETRIC;
25 import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
26 import static android.Manifest.permission.USE_FINGERPRINT;
27 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
28 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
29 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS;
30 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
31 import static android.hardware.biometrics.BiometricConstants.BIOMETRIC_ERROR_CANCELED;
32 import static android.hardware.biometrics.BiometricManager.Authenticators;
33 
34 import android.annotation.NonNull;
35 import android.annotation.Nullable;
36 import android.app.AppOpsManager;
37 import android.content.Context;
38 import android.content.pm.PackageManager;
39 import android.hardware.biometrics.BiometricAuthenticator;
40 import android.hardware.biometrics.BiometricManager;
41 import android.hardware.biometrics.ComponentInfoInternal;
42 import android.hardware.biometrics.IAuthService;
43 import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
44 import android.hardware.biometrics.IBiometricService;
45 import android.hardware.biometrics.IBiometricServiceReceiver;
46 import android.hardware.biometrics.IInvalidationCallback;
47 import android.hardware.biometrics.ITestSession;
48 import android.hardware.biometrics.ITestSessionCallback;
49 import android.hardware.biometrics.PromptInfo;
50 import android.hardware.biometrics.SensorLocationInternal;
51 import android.hardware.biometrics.SensorPropertiesInternal;
52 import android.hardware.face.FaceSensorProperties;
53 import android.hardware.face.FaceSensorPropertiesInternal;
54 import android.hardware.face.IFaceService;
55 import android.hardware.fingerprint.FingerprintSensorProperties;
56 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
57 import android.hardware.fingerprint.IFingerprintService;
58 import android.hardware.iris.IIrisService;
59 import android.os.Binder;
60 import android.os.Build;
61 import android.os.IBinder;
62 import android.os.RemoteException;
63 import android.os.ServiceManager;
64 import android.os.SystemProperties;
65 import android.os.UserHandle;
66 import android.provider.Settings;
67 import android.util.Slog;
68 
69 import com.android.internal.R;
70 import com.android.internal.annotations.VisibleForTesting;
71 import com.android.internal.util.ArrayUtils;
72 import com.android.server.SystemService;
73 
74 import java.util.ArrayList;
75 import java.util.Arrays;
76 import java.util.List;
77 
78 /**
79  * System service that provides an interface for authenticating with biometrics and
80  * PIN/pattern/password to BiometricPrompt and lock screen.
81  */
82 public class AuthService extends SystemService {
83     private static final String TAG = "AuthService";
84     private static final String SETTING_HIDL_DISABLED =
85             "com.android.server.biometrics.AuthService.hidlDisabled";
86     private static final int DEFAULT_HIDL_DISABLED = 0;
87     private static final String SYSPROP_FIRST_API_LEVEL = "ro.board.first_api_level";
88     private static final String SYSPROP_API_LEVEL = "ro.board.api_level";
89 
90     private final Injector mInjector;
91 
92     private IBiometricService mBiometricService;
93     @VisibleForTesting
94     final IAuthService.Stub mImpl;
95 
96     /**
97      * Class for injecting dependencies into AuthService.
98      * TODO(b/141025588): Replace with a dependency injection framework (e.g. Guice, Dagger).
99      */
100     @VisibleForTesting
101     public static class Injector {
102 
103         /**
104          * Allows to mock BiometricService for testing.
105          */
106         @VisibleForTesting
getBiometricService()107         public IBiometricService getBiometricService() {
108             return IBiometricService.Stub.asInterface(
109                     ServiceManager.getService(Context.BIOMETRIC_SERVICE));
110         }
111 
112         /**
113          * Allows to stub publishBinderService(...) for testing.
114          */
115         @VisibleForTesting
publishBinderService(AuthService service, IAuthService.Stub impl)116         public void publishBinderService(AuthService service, IAuthService.Stub impl) {
117             service.publishBinderService(Context.AUTH_SERVICE, impl);
118         }
119 
120         /**
121          * Allows to test with various device sensor configurations.
122          * @param context
123          * @return
124          */
125         @VisibleForTesting
getConfiguration(Context context)126         public String[] getConfiguration(Context context) {
127             return context.getResources().getStringArray(R.array.config_biometric_sensors);
128         }
129 
130         /**
131          * Allows us to mock FingerprintService for testing
132          */
133         @VisibleForTesting
getFingerprintService()134         public IFingerprintService getFingerprintService() {
135             return IFingerprintService.Stub.asInterface(
136                     ServiceManager.getService(Context.FINGERPRINT_SERVICE));
137         }
138 
139         /**
140          * Allows us to mock FaceService for testing
141          */
142         @VisibleForTesting
getFaceService()143         public IFaceService getFaceService() {
144             return IFaceService.Stub.asInterface(
145                     ServiceManager.getService(Context.FACE_SERVICE));
146         }
147 
148         /**
149          * Allows us to mock IrisService for testing
150          */
151         @VisibleForTesting
getIrisService()152         public IIrisService getIrisService() {
153             return IIrisService.Stub.asInterface(
154                     ServiceManager.getService(Context.IRIS_SERVICE));
155         }
156 
157         @VisibleForTesting
getAppOps(Context context)158         public AppOpsManager getAppOps(Context context) {
159             return context.getSystemService(AppOpsManager.class);
160         }
161 
162         /**
163          * Allows to ignore HIDL HALs on debug builds based on a secure setting.
164          */
165         @VisibleForTesting
isHidlDisabled(Context context)166         public boolean isHidlDisabled(Context context) {
167             if (Build.IS_ENG || Build.IS_USERDEBUG) {
168                 return Settings.Secure.getIntForUser(context.getContentResolver(),
169                         SETTING_HIDL_DISABLED, DEFAULT_HIDL_DISABLED, UserHandle.USER_CURRENT) == 1;
170             }
171             return false;
172         }
173     }
174 
175     private final class AuthServiceImpl extends IAuthService.Stub {
176         @Override
createTestSession(int sensorId, @NonNull ITestSessionCallback callback, @NonNull String opPackageName)177         public ITestSession createTestSession(int sensorId, @NonNull ITestSessionCallback callback,
178                 @NonNull String opPackageName) throws RemoteException {
179             Utils.checkPermission(getContext(), TEST_BIOMETRIC);
180 
181             final long identity = Binder.clearCallingIdentity();
182             try {
183                 return mInjector.getBiometricService()
184                         .createTestSession(sensorId, callback, opPackageName);
185             } finally {
186                 Binder.restoreCallingIdentity(identity);
187             }
188         }
189 
190         @Override
getSensorProperties(String opPackageName)191         public List<SensorPropertiesInternal> getSensorProperties(String opPackageName)
192                 throws RemoteException {
193             Utils.checkPermission(getContext(), TEST_BIOMETRIC);
194 
195             final long identity = Binder.clearCallingIdentity();
196             try {
197                 // Get the result from BiometricService, since it is the source of truth for all
198                 // biometric sensors.
199                 return mInjector.getBiometricService().getSensorProperties(opPackageName);
200             } finally {
201                 Binder.restoreCallingIdentity(identity);
202             }
203         }
204 
205         @Override
getUiPackage()206         public String getUiPackage() {
207             Utils.checkPermission(getContext(), TEST_BIOMETRIC);
208 
209             return getContext().getResources()
210                     .getString(R.string.config_biometric_prompt_ui_package);
211         }
212 
213         @Override
authenticate(IBinder token, long sessionId, int userId, IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo)214         public long authenticate(IBinder token, long sessionId, int userId,
215                 IBiometricServiceReceiver receiver, String opPackageName, PromptInfo promptInfo)
216                 throws RemoteException {
217             // Only allow internal clients to authenticate with a different userId.
218             final int callingUserId = UserHandle.getCallingUserId();
219             final int callingUid = Binder.getCallingUid();
220             final int callingPid = Binder.getCallingPid();
221             if (userId == callingUserId) {
222                 checkPermission();
223             } else {
224                 Slog.w(TAG, "User " + callingUserId + " is requesting authentication of userid: "
225                         + userId);
226                 checkInternalPermission();
227             }
228 
229             if (!checkAppOps(callingUid, opPackageName, "authenticate()")) {
230                 authenticateFastFail("Denied by app ops: " + opPackageName, receiver);
231                 return -1;
232             }
233 
234             if (token == null || receiver == null || opPackageName == null || promptInfo == null) {
235                 authenticateFastFail(
236                         "Unable to authenticate, one or more null arguments", receiver);
237                 return -1;
238             }
239 
240             if (!Utils.isForeground(callingUid, callingPid)) {
241                 authenticateFastFail("Caller is not foreground: " + opPackageName, receiver);
242                 return -1;
243             }
244 
245             if (promptInfo.containsTestConfigurations()) {
246                 if (getContext().checkCallingOrSelfPermission(TEST_BIOMETRIC)
247                         != PackageManager.PERMISSION_GRANTED) {
248                     checkInternalPermission();
249                 }
250             }
251 
252             // Only allow internal clients to enable non-public options.
253             if (promptInfo.containsPrivateApiConfigurations()) {
254                 checkInternalPermission();
255             }
256 
257             final long identity = Binder.clearCallingIdentity();
258             try {
259                 return mBiometricService.authenticate(
260                         token, sessionId, userId, receiver, opPackageName, promptInfo);
261             } finally {
262                 Binder.restoreCallingIdentity(identity);
263             }
264         }
265 
authenticateFastFail(String message, IBiometricServiceReceiver receiver)266         private void authenticateFastFail(String message, IBiometricServiceReceiver receiver) {
267             // notify caller in cases where authentication is aborted before calling into
268             // IBiometricService without raising an exception
269             Slog.e(TAG, "authenticateFastFail: " + message);
270             try {
271                 receiver.onError(TYPE_NONE, BIOMETRIC_ERROR_CANCELED, 0 /*vendorCode */);
272             } catch (RemoteException e) {
273                 Slog.e(TAG, "authenticateFastFail failed to notify caller", e);
274             }
275         }
276 
277         @Override
cancelAuthentication(IBinder token, String opPackageName, long requestId)278         public void cancelAuthentication(IBinder token, String opPackageName, long requestId)
279                 throws RemoteException {
280             checkPermission();
281 
282             if (token == null || opPackageName == null) {
283                 Slog.e(TAG, "Unable to cancel authentication, one or more null arguments");
284                 return;
285             }
286 
287             final long identity = Binder.clearCallingIdentity();
288             try {
289                 mBiometricService.cancelAuthentication(token, opPackageName, requestId);
290             } finally {
291                 Binder.restoreCallingIdentity(identity);
292             }
293         }
294 
295         @Override
canAuthenticate(String opPackageName, int userId, @Authenticators.Types int authenticators)296         public int canAuthenticate(String opPackageName, int userId,
297                 @Authenticators.Types int authenticators) throws RemoteException {
298 
299             // Only allow internal clients to call canAuthenticate with a different userId.
300             final int callingUserId = UserHandle.getCallingUserId();
301 
302             if (userId != callingUserId) {
303                 checkInternalPermission();
304             } else {
305                 checkPermission();
306             }
307 
308             final long identity = Binder.clearCallingIdentity();
309             try {
310                 final int result = mBiometricService.canAuthenticate(
311                         opPackageName, userId, callingUserId, authenticators);
312                 Slog.d(TAG, "canAuthenticate"
313                         + ", userId: " + userId
314                         + ", callingUserId: " + callingUserId
315                         + ", authenticators: " + authenticators
316                         + ", result: " + result);
317                 return result;
318             } finally {
319                 Binder.restoreCallingIdentity(identity);
320             }
321         }
322 
323         @Override
hasEnrolledBiometrics(int userId, String opPackageName)324         public boolean hasEnrolledBiometrics(int userId, String opPackageName)
325                 throws RemoteException {
326             checkInternalPermission();
327             final long identity = Binder.clearCallingIdentity();
328             try {
329                 return mBiometricService.hasEnrolledBiometrics(userId, opPackageName);
330             } finally {
331                 Binder.restoreCallingIdentity(identity);
332             }
333         }
334 
335         @Override
registerEnabledOnKeyguardCallback( IBiometricEnabledOnKeyguardCallback callback)336         public void registerEnabledOnKeyguardCallback(
337                 IBiometricEnabledOnKeyguardCallback callback) throws RemoteException {
338             checkInternalPermission();
339             final int callingUserId = UserHandle.getCallingUserId();
340             final long identity = Binder.clearCallingIdentity();
341             try {
342                 mBiometricService.registerEnabledOnKeyguardCallback(callback, callingUserId);
343             } finally {
344                 Binder.restoreCallingIdentity(identity);
345             }
346         }
347 
348         @Override
invalidateAuthenticatorIds(int userId, int fromSensorId, IInvalidationCallback callback)349         public void invalidateAuthenticatorIds(int userId, int fromSensorId,
350                 IInvalidationCallback callback) throws RemoteException {
351             checkInternalPermission();
352 
353             final long identity = Binder.clearCallingIdentity();
354             try {
355                 mBiometricService.invalidateAuthenticatorIds(userId, fromSensorId, callback);
356             } finally {
357                 Binder.restoreCallingIdentity(identity);
358             }
359         }
360 
361         @Override
getAuthenticatorIds(int userId)362         public long[] getAuthenticatorIds(int userId) throws RemoteException {
363             // In this method, we're not checking whether the caller is permitted to use face
364             // API because current authenticator ID is leaked (in a more contrived way) via Android
365             // Keystore (android.security.keystore package): the user of that API can create a key
366             // which requires face authentication for its use, and then query the key's
367             // characteristics (hidden API) which returns, among other things, face
368             // authenticator ID which was active at key creation time.
369             //
370             // Reason: The part of Android Keystore which runs inside an app's process invokes this
371             // method in certain cases. Those cases are not always where the developer demonstrates
372             // explicit intent to use biometric functionality. Thus, to avoiding throwing an
373             // unexpected SecurityException this method does not check whether its caller is
374             // permitted to use face API.
375             //
376             // The permission check should be restored once Android Keystore no longer invokes this
377             // method from inside app processes.
378 
379             final int callingUserId = UserHandle.getCallingUserId();
380             if (userId != callingUserId) {
381                 getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL,
382                         "Must have " + USE_BIOMETRIC_INTERNAL + " permission.");
383             }
384             final long identity = Binder.clearCallingIdentity();
385             try {
386                 return mBiometricService.getAuthenticatorIds(userId);
387             } finally {
388                 Binder.restoreCallingIdentity(identity);
389             }
390         }
391 
392         @Override
resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId, int userId, byte[] hardwareAuthToken)393         public void resetLockoutTimeBound(IBinder token, String opPackageName, int fromSensorId,
394                 int userId, byte[] hardwareAuthToken) throws RemoteException {
395             checkInternalPermission();
396 
397             final long identity = Binder.clearCallingIdentity();
398             try {
399                 mBiometricService.resetLockoutTimeBound(token, opPackageName, fromSensorId, userId,
400                         hardwareAuthToken);
401             } finally {
402                 Binder.restoreCallingIdentity(identity);
403             }
404         }
405 
406         @Override
getButtonLabel( int userId, String opPackageName, @Authenticators.Types int authenticators)407         public CharSequence getButtonLabel(
408                 int userId,
409                 String opPackageName,
410                 @Authenticators.Types int authenticators) throws RemoteException {
411 
412             // Only allow internal clients to call getButtonLabel with a different userId.
413             final int callingUserId = UserHandle.getCallingUserId();
414 
415             if (userId != callingUserId) {
416                 checkInternalPermission();
417             } else {
418                 checkPermission();
419             }
420 
421             final long identity = Binder.clearCallingIdentity();
422             try {
423                 @BiometricAuthenticator.Modality final int modality =
424                         mBiometricService.getCurrentModality(
425                                 opPackageName, userId, callingUserId, authenticators);
426 
427                 final String result;
428                 switch (getCredentialBackupModality(modality)) {
429                     case BiometricAuthenticator.TYPE_NONE:
430                         result = null;
431                         break;
432                     case BiometricAuthenticator.TYPE_CREDENTIAL:
433                         result = getContext().getString(R.string.screen_lock_app_setting_name);
434                         break;
435                     case BiometricAuthenticator.TYPE_FINGERPRINT:
436                         result = getContext().getString(R.string.fingerprint_app_setting_name);
437                         break;
438                     case BiometricAuthenticator.TYPE_FACE:
439                         result = getContext().getString(R.string.face_app_setting_name);
440                         break;
441                     default:
442                         result = getContext().getString(R.string.biometric_app_setting_name);
443                         break;
444                 }
445 
446                 return result;
447             } finally {
448                 Binder.restoreCallingIdentity(identity);
449             }
450         }
451 
452         @Override
getPromptMessage( int userId, String opPackageName, @Authenticators.Types int authenticators)453         public CharSequence getPromptMessage(
454                 int userId,
455                 String opPackageName,
456                 @Authenticators.Types int authenticators) throws RemoteException {
457 
458             // Only allow internal clients to call getButtonLabel with a different userId.
459             final int callingUserId = UserHandle.getCallingUserId();
460 
461             if (userId != callingUserId) {
462                 checkInternalPermission();
463             } else {
464                 checkPermission();
465             }
466 
467             final long identity = Binder.clearCallingIdentity();
468             try {
469                 @BiometricAuthenticator.Modality final int modality =
470                         mBiometricService.getCurrentModality(
471                                 opPackageName, userId, callingUserId, authenticators);
472 
473                 final boolean isCredentialAllowed = Utils.isCredentialRequested(authenticators);
474 
475                 final String result;
476                 switch (getCredentialBackupModality(modality)) {
477                     case BiometricAuthenticator.TYPE_NONE:
478                         result = null;
479                         break;
480 
481                     case BiometricAuthenticator.TYPE_CREDENTIAL:
482                         result = getContext().getString(
483                                 R.string.screen_lock_dialog_default_subtitle);
484                         break;
485 
486                     case BiometricAuthenticator.TYPE_FINGERPRINT:
487                         if (isCredentialAllowed) {
488                             result = getContext().getString(
489                                     R.string.fingerprint_or_screen_lock_dialog_default_subtitle);
490                         } else {
491                             result = getContext().getString(
492                                     R.string.fingerprint_dialog_default_subtitle);
493                         }
494                         break;
495 
496                     case BiometricAuthenticator.TYPE_FACE:
497                         if (isCredentialAllowed) {
498                             result = getContext().getString(
499                                     R.string.face_or_screen_lock_dialog_default_subtitle);
500                         } else {
501                             result = getContext().getString(R.string.face_dialog_default_subtitle);
502                         }
503                         break;
504 
505                     default:
506                         if (isCredentialAllowed) {
507                             result = getContext().getString(
508                                     R.string.biometric_or_screen_lock_dialog_default_subtitle);
509                         } else {
510                             result = getContext().getString(
511                                     R.string.biometric_dialog_default_subtitle);
512                         }
513                         break;
514                 }
515 
516                 return result;
517             } finally {
518                 Binder.restoreCallingIdentity(identity);
519             }
520         }
521 
522         @Override
getSettingName( int userId, String opPackageName, @Authenticators.Types int authenticators)523         public CharSequence getSettingName(
524                 int userId,
525                 String opPackageName,
526                 @Authenticators.Types int authenticators) throws RemoteException {
527 
528             // Only allow internal clients to call getButtonLabel with a different userId.
529             final int callingUserId = UserHandle.getCallingUserId();
530 
531             if (userId != callingUserId) {
532                 checkInternalPermission();
533             } else {
534                 checkPermission();
535             }
536 
537             final long identity = Binder.clearCallingIdentity();
538             try {
539                 @BiometricAuthenticator.Modality final int modality =
540                         mBiometricService.getSupportedModalities(authenticators);
541 
542                 final String result;
543                 switch (modality) {
544                     // Handle the case of a single supported modality.
545                     case BiometricAuthenticator.TYPE_NONE:
546                         result = null;
547                         break;
548                     case BiometricAuthenticator.TYPE_CREDENTIAL:
549                         result = getContext().getString(R.string.screen_lock_app_setting_name);
550                         break;
551                     case BiometricAuthenticator.TYPE_IRIS:
552                         result = getContext().getString(R.string.biometric_app_setting_name);
553                         break;
554                     case BiometricAuthenticator.TYPE_FINGERPRINT:
555                         result = getContext().getString(R.string.fingerprint_app_setting_name);
556                         break;
557                     case BiometricAuthenticator.TYPE_FACE:
558                         result = getContext().getString(R.string.face_app_setting_name);
559                         break;
560 
561                     // Handle other possible modality combinations.
562                     default:
563                         if ((modality & BiometricAuthenticator.TYPE_CREDENTIAL) == 0) {
564                             // 2+ biometric modalities are supported (but not device credential).
565                             result = getContext().getString(R.string.biometric_app_setting_name);
566                         } else {
567                             @BiometricAuthenticator.Modality final int biometricModality =
568                                     modality & ~BiometricAuthenticator.TYPE_CREDENTIAL;
569                             if (biometricModality == BiometricAuthenticator.TYPE_FINGERPRINT) {
570                                 // Only device credential and fingerprint are supported.
571                                 result = getContext().getString(
572                                         R.string.fingerprint_or_screen_lock_app_setting_name);
573                             } else if (biometricModality == BiometricAuthenticator.TYPE_FACE) {
574                                 // Only device credential and face are supported.
575                                 result = getContext().getString(
576                                         R.string.face_or_screen_lock_app_setting_name);
577                             } else {
578                                 // Device credential and 1+ other biometric(s) are supported.
579                                 result = getContext().getString(
580                                         R.string.biometric_or_screen_lock_app_setting_name);
581                             }
582                         }
583                         break;
584                 }
585                 return result;
586             } finally {
587                 Binder.restoreCallingIdentity(identity);
588             }
589         }
590     }
591 
AuthService(Context context)592     public AuthService(Context context) {
593         this(context, new Injector());
594     }
595 
AuthService(Context context, Injector injector)596     public AuthService(Context context, Injector injector) {
597         super(context);
598 
599         mInjector = injector;
600         mImpl = new AuthServiceImpl();
601     }
602 
603 
604     /**
605      * Registration of all HIDL and AIDL biometric HALs starts here.
606      * The flow looks like this:
607      * AuthService
608      * └── .onStart()
609      *     └── .registerAuthenticators(...)
610      *         ├── FaceService.registerAuthenticators(...)
611      *         │   └── for (p : serviceProviders)
612      *         │       └── for (s : p.sensors)
613      *         │           └── BiometricService.registerAuthenticator(s)
614      *         │
615      *         ├── FingerprintService.registerAuthenticators(...)
616      *         │   └── for (p : serviceProviders)
617      *         │       └── for (s : p.sensors)
618      *         │           └── BiometricService.registerAuthenticator(s)
619      *         │
620      *         └── IrisService.registerAuthenticators(...)
621      *             └── for (p : serviceProviders)
622      *                 └── for (s : p.sensors)
623      *                     └── BiometricService.registerAuthenticator(s)
624      */
625     @Override
onStart()626     public void onStart() {
627         mBiometricService = mInjector.getBiometricService();
628 
629         final SensorConfig[] hidlConfigs;
630         if (!mInjector.isHidlDisabled(getContext())) {
631             final int firstApiLevel = SystemProperties.getInt(SYSPROP_FIRST_API_LEVEL, 0);
632             final int apiLevel = SystemProperties.getInt(SYSPROP_API_LEVEL, firstApiLevel);
633             String[] configStrings = mInjector.getConfiguration(getContext());
634             if (configStrings.length == 0 && apiLevel == Build.VERSION_CODES.R) {
635                 // For backwards compatibility with R where biometrics could work without being
636                 // configured in config_biometric_sensors. In the absence of a vendor provided
637                 // configuration, we assume the weakest biometric strength (i.e. convenience).
638                 Slog.w(TAG, "Found R vendor partition without config_biometric_sensors");
639                 configStrings = generateRSdkCompatibleConfiguration();
640             }
641             hidlConfigs = new SensorConfig[configStrings.length];
642             for (int i = 0; i < configStrings.length; ++i) {
643                 hidlConfigs[i] = new SensorConfig(configStrings[i]);
644             }
645         } else {
646             hidlConfigs = null;
647         }
648 
649         // Registers HIDL and AIDL authenticators, but only HIDL configs need to be provided.
650         registerAuthenticators(hidlConfigs);
651 
652         mInjector.publishBinderService(this, mImpl);
653     }
654 
655     /**
656      * Generates an array of string configs with entries that correspond to the biometric features
657      * declared on the device. Returns an empty array if no biometric features are declared.
658      * Biometrics are assumed to be of the weakest strength class, i.e. convenience.
659      */
generateRSdkCompatibleConfiguration()660     private @NonNull String[] generateRSdkCompatibleConfiguration() {
661         final PackageManager pm = getContext().getPackageManager();
662         final ArrayList<String> modalities = new ArrayList<>();
663         if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
664             modalities.add(String.valueOf(BiometricAuthenticator.TYPE_FINGERPRINT));
665         }
666         if (pm.hasSystemFeature(PackageManager.FEATURE_FACE)) {
667             modalities.add(String.valueOf(BiometricAuthenticator.TYPE_FACE));
668         }
669         final String strength = String.valueOf(Authenticators.BIOMETRIC_CONVENIENCE);
670         final String[] configStrings = new String[modalities.size()];
671         for (int i = 0; i < modalities.size(); ++i) {
672             final String id = String.valueOf(i);
673             final String modality = modalities.get(i);
674             configStrings[i] = String.join(":" /* delimiter */, id, modality, strength);
675         }
676         Slog.d(TAG, "Generated config_biometric_sensors: " + Arrays.toString(configStrings));
677         return configStrings;
678     }
679 
680     /**
681      * Registers HIDL and AIDL authenticators for all of the available modalities.
682      *
683      * @param hidlSensors Array of {@link SensorConfig} configuration for all of the HIDL sensors
684      *                    available on the device. This array may contain configuration for
685      *                    different modalities and different sensors of the same modality in
686      *                    arbitrary order. Can be null if no HIDL sensors exist on the device.
687      */
registerAuthenticators(@ullable SensorConfig[] hidlSensors)688     private void registerAuthenticators(@Nullable SensorConfig[] hidlSensors) {
689         List<FingerprintSensorPropertiesInternal> hidlFingerprintSensors = new ArrayList<>();
690         List<FaceSensorPropertiesInternal> hidlFaceSensors = new ArrayList<>();
691         // Iris doesn't have IrisSensorPropertiesInternal, using SensorPropertiesInternal instead.
692         List<SensorPropertiesInternal> hidlIrisSensors = new ArrayList<>();
693 
694         if (hidlSensors != null) {
695             for (SensorConfig sensor : hidlSensors) {
696                 Slog.d(TAG, "Registering HIDL ID: " + sensor.id + " Modality: " + sensor.modality
697                         + " Strength: " + sensor.strength);
698                 switch (sensor.modality) {
699                     case TYPE_FINGERPRINT:
700                         hidlFingerprintSensors.add(
701                                 getHidlFingerprintSensorProps(sensor.id, sensor.strength));
702                         break;
703 
704                     case TYPE_FACE:
705                         hidlFaceSensors.add(getHidlFaceSensorProps(sensor.id, sensor.strength));
706                         break;
707 
708                     case TYPE_IRIS:
709                         hidlIrisSensors.add(getHidlIrisSensorProps(sensor.id, sensor.strength));
710                         break;
711 
712                     default:
713                         Slog.e(TAG, "Unknown modality: " + sensor.modality);
714                 }
715             }
716         }
717 
718         final IFingerprintService fingerprintService = mInjector.getFingerprintService();
719         if (fingerprintService != null) {
720             try {
721                 fingerprintService.registerAuthenticators(hidlFingerprintSensors);
722             } catch (RemoteException e) {
723                 Slog.e(TAG, "RemoteException when registering fingerprint authenticators", e);
724             }
725         } else if (hidlFingerprintSensors.size() > 0) {
726             Slog.e(TAG, "HIDL fingerprint configuration exists, but FingerprintService is null.");
727         }
728 
729         final IFaceService faceService = mInjector.getFaceService();
730         if (faceService != null) {
731             try {
732                 faceService.registerAuthenticators(hidlFaceSensors);
733             } catch (RemoteException e) {
734                 Slog.e(TAG, "RemoteException when registering face authenticators", e);
735             }
736         } else if (hidlFaceSensors.size() > 0) {
737             Slog.e(TAG, "HIDL face configuration exists, but FaceService is null.");
738         }
739 
740         final IIrisService irisService = mInjector.getIrisService();
741         if (irisService != null) {
742             try {
743                 irisService.registerAuthenticators(hidlIrisSensors);
744             } catch (RemoteException e) {
745                 Slog.e(TAG, "RemoteException when registering iris authenticators", e);
746             }
747         } else if (hidlIrisSensors.size() > 0) {
748             Slog.e(TAG, "HIDL iris configuration exists, but IrisService is null.");
749         }
750     }
751 
checkInternalPermission()752     private void checkInternalPermission() {
753         getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL,
754                 "Must have USE_BIOMETRIC_INTERNAL permission");
755     }
756 
checkPermission()757     private void checkPermission() {
758         if (getContext().checkCallingOrSelfPermission(USE_FINGERPRINT)
759                 != PackageManager.PERMISSION_GRANTED) {
760             getContext().enforceCallingOrSelfPermission(USE_BIOMETRIC,
761                     "Must have USE_BIOMETRIC permission");
762         }
763     }
764 
checkAppOps(int uid, String opPackageName, String reason)765     private boolean checkAppOps(int uid, String opPackageName, String reason) {
766         return mInjector.getAppOps(getContext()).noteOp(AppOpsManager.OP_USE_BIOMETRIC, uid,
767                 opPackageName, null /* attributionTag */, reason) == AppOpsManager.MODE_ALLOWED;
768     }
769 
770     @BiometricAuthenticator.Modality
getCredentialBackupModality(@iometricAuthenticator.Modality int modality)771     private static int getCredentialBackupModality(@BiometricAuthenticator.Modality int modality) {
772         return modality == BiometricAuthenticator.TYPE_CREDENTIAL
773                 ? modality : (modality & ~BiometricAuthenticator.TYPE_CREDENTIAL);
774     }
775 
776 
getHidlFingerprintSensorProps(int sensorId, @BiometricManager.Authenticators.Types int strength)777     private FingerprintSensorPropertiesInternal getHidlFingerprintSensorProps(int sensorId,
778             @BiometricManager.Authenticators.Types int strength) {
779         // The existence of config_udfps_sensor_props indicates that the sensor is UDFPS.
780         final int[] udfpsProps = getContext().getResources().getIntArray(
781                 com.android.internal.R.array.config_udfps_sensor_props);
782 
783         final boolean isUdfps = !ArrayUtils.isEmpty(udfpsProps);
784 
785         // config_is_powerbutton_fps indicates whether device has a power button fingerprint sensor.
786         final boolean isPowerbuttonFps = getContext().getResources().getBoolean(
787                 R.bool.config_is_powerbutton_fps);
788 
789         final @FingerprintSensorProperties.SensorType int sensorType;
790         if (isUdfps) {
791             sensorType = FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
792         } else if (isPowerbuttonFps) {
793             sensorType = FingerprintSensorProperties.TYPE_POWER_BUTTON;
794         } else {
795             sensorType = FingerprintSensorProperties.TYPE_REAR;
796         }
797 
798         // IBiometricsFingerprint@2.1 does not manage timeout below the HAL, so the Gatekeeper HAT
799         // cannot be checked.
800         final boolean resetLockoutRequiresHardwareAuthToken = false;
801         final int maxEnrollmentsPerUser = getContext().getResources().getInteger(
802                 R.integer.config_fingerprintMaxTemplatesPerUser);
803 
804         final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
805         if (isUdfps && udfpsProps.length == 3) {
806             return new FingerprintSensorPropertiesInternal(sensorId,
807                     Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser,
808                     componentInfo, sensorType, true /* halControlsIllumination */,
809                     resetLockoutRequiresHardwareAuthToken,
810                     List.of(new SensorLocationInternal("" /* display */, udfpsProps[0],
811                             udfpsProps[1], udfpsProps[2])));
812         } else {
813             return new FingerprintSensorPropertiesInternal(sensorId,
814                     Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser,
815                     componentInfo, sensorType, resetLockoutRequiresHardwareAuthToken);
816         }
817     }
818 
getHidlFaceSensorProps(int sensorId, @BiometricManager.Authenticators.Types int strength)819     private FaceSensorPropertiesInternal getHidlFaceSensorProps(int sensorId,
820             @BiometricManager.Authenticators.Types int strength) {
821         final boolean supportsSelfIllumination = getContext().getResources().getBoolean(
822                 R.bool.config_faceAuthSupportsSelfIllumination);
823         final int maxTemplatesAllowed = getContext().getResources().getInteger(
824                 R.integer.config_faceMaxTemplatesPerUser);
825         final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
826         final boolean supportsFaceDetect = false;
827         final boolean resetLockoutRequiresChallenge = true;
828         return new FaceSensorPropertiesInternal(sensorId,
829                 Utils.authenticatorStrengthToPropertyStrength(strength), maxTemplatesAllowed,
830                 componentInfo, FaceSensorProperties.TYPE_UNKNOWN, supportsFaceDetect,
831                 supportsSelfIllumination, resetLockoutRequiresChallenge);
832     }
833 
getHidlIrisSensorProps(int sensorId, @BiometricManager.Authenticators.Types int strength)834     private SensorPropertiesInternal getHidlIrisSensorProps(int sensorId,
835             @BiometricManager.Authenticators.Types int strength) {
836         final int maxEnrollmentsPerUser = 1;
837         final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
838         final boolean resetLockoutRequiresHardwareAuthToken = false;
839         final boolean resetLockoutRequiresChallenge = false;
840         return new SensorPropertiesInternal(sensorId,
841                 Utils.authenticatorStrengthToPropertyStrength(strength), maxEnrollmentsPerUser,
842                 componentInfo, resetLockoutRequiresHardwareAuthToken,
843                 resetLockoutRequiresChallenge);
844     }
845 }
846