• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_CREDENTIAL;
20 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
21 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
22 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_IRIS;
23 import static android.hardware.biometrics.BiometricAuthenticator.TYPE_NONE;
24 
25 import android.annotation.IntDef;
26 import android.annotation.NonNull;
27 import android.app.admin.DevicePolicyManager;
28 import android.app.trust.ITrustManager;
29 import android.content.Context;
30 import android.hardware.SensorPrivacyManager;
31 import android.hardware.biometrics.BiometricAuthenticator;
32 import android.hardware.biometrics.BiometricManager;
33 import android.hardware.biometrics.PromptInfo;
34 import android.os.RemoteException;
35 import android.util.Pair;
36 import android.util.Slog;
37 
38 import com.android.server.biometrics.sensors.LockoutTracker;
39 
40 import java.lang.annotation.Retention;
41 import java.lang.annotation.RetentionPolicy;
42 import java.util.ArrayList;
43 import java.util.List;
44 
45 /**
46  * Class representing the calling client's request. Additionally, derives/calculates
47  * preliminary info that would be useful in helping serve this request. Note that generating
48  * the PreAuthInfo should not change any sensor state.
49  */
50 class PreAuthInfo {
51     private static final String TAG = "BiometricService/PreAuthInfo";
52 
53     static final int AUTHENTICATOR_OK = 1;
54     static final int BIOMETRIC_NO_HARDWARE = 2;
55     static final int BIOMETRIC_DISABLED_BY_DEVICE_POLICY = 3;
56     static final int BIOMETRIC_INSUFFICIENT_STRENGTH = 4;
57     static final int BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE = 5;
58     static final int BIOMETRIC_HARDWARE_NOT_DETECTED = 6;
59     static final int BIOMETRIC_NOT_ENROLLED = 7;
60     static final int BIOMETRIC_NOT_ENABLED_FOR_APPS = 8;
61     static final int CREDENTIAL_NOT_ENROLLED = 9;
62     static final int BIOMETRIC_LOCKOUT_TIMED = 10;
63     static final int BIOMETRIC_LOCKOUT_PERMANENT = 11;
64     static final int BIOMETRIC_SENSOR_PRIVACY_ENABLED = 12;
65     @IntDef({AUTHENTICATOR_OK,
66             BIOMETRIC_NO_HARDWARE,
67             BIOMETRIC_DISABLED_BY_DEVICE_POLICY,
68             BIOMETRIC_INSUFFICIENT_STRENGTH,
69             BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE,
70             BIOMETRIC_HARDWARE_NOT_DETECTED,
71             BIOMETRIC_NOT_ENROLLED,
72             BIOMETRIC_NOT_ENABLED_FOR_APPS,
73             CREDENTIAL_NOT_ENROLLED,
74             BIOMETRIC_LOCKOUT_TIMED,
75             BIOMETRIC_LOCKOUT_PERMANENT,
76             BIOMETRIC_SENSOR_PRIVACY_ENABLED})
77     @Retention(RetentionPolicy.SOURCE)
78     @interface AuthenticatorStatus {}
79 
80     private final boolean mBiometricRequested;
81     private final int mBiometricStrengthRequested;
82 
83     final boolean credentialRequested;
84     // Sensors that can be used for this request (e.g. strong enough, enrolled, enabled).
85     final List<BiometricSensor> eligibleSensors;
86     // Sensors that cannot be used for this request. Pair<BiometricSensor, AuthenticatorStatus>
87     final List<Pair<BiometricSensor, Integer>> ineligibleSensors;
88     final boolean credentialAvailable;
89     final boolean confirmationRequested;
90     final boolean ignoreEnrollmentState;
91     final int userId;
92     final Context context;
93 
create(ITrustManager trustManager, DevicePolicyManager devicePolicyManager, BiometricService.SettingObserver settingObserver, List<BiometricSensor> sensors, int userId, PromptInfo promptInfo, String opPackageName, boolean checkDevicePolicyManager, Context context)94     static PreAuthInfo create(ITrustManager trustManager,
95             DevicePolicyManager devicePolicyManager,
96             BiometricService.SettingObserver settingObserver,
97             List<BiometricSensor> sensors,
98             int userId, PromptInfo promptInfo, String opPackageName,
99             boolean checkDevicePolicyManager, Context context)
100             throws RemoteException {
101 
102         final boolean confirmationRequested = promptInfo.isConfirmationRequested();
103         final boolean biometricRequested = Utils.isBiometricRequested(promptInfo);
104         final int requestedStrength = Utils.getPublicBiometricStrength(promptInfo);
105         final boolean credentialRequested = Utils.isCredentialRequested(promptInfo);
106 
107         final boolean credentialAvailable = trustManager.isDeviceSecure(userId,
108                 context.getAssociatedDisplayId());
109 
110         // Assuming that biometric authenticators are listed in priority-order, the rest of this
111         // function will attempt to find the first authenticator that's as strong or stronger than
112         // the requested strength, available, enrolled, and enabled. The tricky part is returning
113         // the correct error. Error strings that are modality-specific should also respect the
114         // priority-order.
115 
116         final List<BiometricSensor> eligibleSensors = new ArrayList<>();
117         final List<Pair<BiometricSensor, Integer>> ineligibleSensors = new ArrayList<>();
118 
119         if (biometricRequested) {
120             for (BiometricSensor sensor : sensors) {
121 
122                 @AuthenticatorStatus int status = getStatusForBiometricAuthenticator(
123                         devicePolicyManager, settingObserver, sensor, userId, opPackageName,
124                         checkDevicePolicyManager, requestedStrength,
125                         promptInfo.getAllowedSensorIds(),
126                         promptInfo.isIgnoreEnrollmentState(),
127                         context);
128 
129                 Slog.d(TAG, "Package: " + opPackageName
130                         + " Sensor ID: " + sensor.id
131                         + " Modality: " + sensor.modality
132                         + " Status: " + status);
133 
134                 // A sensor with privacy enabled will still be eligible to
135                 // authenticate with biometric prompt. This is so the framework can display
136                 // a sensor privacy error message to users after briefly showing the
137                 // Biometric Prompt.
138                 //
139                 // Note: if only a certain sensor is required and the privacy is enabled,
140                 // canAuthenticate() will return false.
141                 if (status == AUTHENTICATOR_OK || status == BIOMETRIC_SENSOR_PRIVACY_ENABLED) {
142                     eligibleSensors.add(sensor);
143                 } else {
144                     ineligibleSensors.add(new Pair<>(sensor, status));
145                 }
146             }
147         }
148 
149         return new PreAuthInfo(biometricRequested, requestedStrength, credentialRequested,
150                 eligibleSensors, ineligibleSensors, credentialAvailable, confirmationRequested,
151                 promptInfo.isIgnoreEnrollmentState(), userId, context);
152     }
153 
154     /**
155      * Returns the status of the authenticator, with errors returned in a specific priority order.
156      * For example, {@link #BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE} is only returned
157      * if it has enrollments, and is enabled for apps.
158      *
159      * @return @AuthenticatorStatus
160      */
getStatusForBiometricAuthenticator( DevicePolicyManager devicePolicyManager, BiometricService.SettingObserver settingObserver, BiometricSensor sensor, int userId, String opPackageName, boolean checkDevicePolicyManager, int requestedStrength, @NonNull List<Integer> requestedSensorIds, boolean ignoreEnrollmentState, Context context)161     private static @AuthenticatorStatus int getStatusForBiometricAuthenticator(
162             DevicePolicyManager devicePolicyManager,
163             BiometricService.SettingObserver settingObserver,
164             BiometricSensor sensor, int userId, String opPackageName,
165             boolean checkDevicePolicyManager, int requestedStrength,
166             @NonNull List<Integer> requestedSensorIds,
167             boolean ignoreEnrollmentState, Context context) {
168 
169         if (!requestedSensorIds.isEmpty() && !requestedSensorIds.contains(sensor.id)) {
170             return BIOMETRIC_NO_HARDWARE;
171         }
172 
173         final boolean wasStrongEnough =
174                 Utils.isAtLeastStrength(sensor.oemStrength, requestedStrength);
175         final boolean isStrongEnough =
176                 Utils.isAtLeastStrength(sensor.getCurrentStrength(), requestedStrength);
177 
178         if (wasStrongEnough && !isStrongEnough) {
179             return BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE;
180         } else if (!wasStrongEnough) {
181             return BIOMETRIC_INSUFFICIENT_STRENGTH;
182         }
183 
184         try {
185             if (!sensor.impl.isHardwareDetected(opPackageName)) {
186                 return BIOMETRIC_HARDWARE_NOT_DETECTED;
187             }
188 
189             if (!sensor.impl.hasEnrolledTemplates(userId, opPackageName)
190                     && !ignoreEnrollmentState) {
191                 return BIOMETRIC_NOT_ENROLLED;
192             }
193             final SensorPrivacyManager sensorPrivacyManager = context
194                     .getSystemService(SensorPrivacyManager.class);
195 
196             if (sensorPrivacyManager != null && sensor.modality == TYPE_FACE) {
197                 if (sensorPrivacyManager
198                         .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, userId)) {
199                     return BIOMETRIC_SENSOR_PRIVACY_ENABLED;
200                 }
201             }
202 
203 
204             final @LockoutTracker.LockoutMode int lockoutMode =
205                     sensor.impl.getLockoutModeForUser(userId);
206             if (lockoutMode == LockoutTracker.LOCKOUT_TIMED) {
207                 return BIOMETRIC_LOCKOUT_TIMED;
208             } else if (lockoutMode == LockoutTracker.LOCKOUT_PERMANENT) {
209                 return BIOMETRIC_LOCKOUT_PERMANENT;
210             }
211         } catch (RemoteException e) {
212             return BIOMETRIC_HARDWARE_NOT_DETECTED;
213         }
214 
215         if (!isEnabledForApp(settingObserver, sensor.modality, userId)) {
216             return BIOMETRIC_NOT_ENABLED_FOR_APPS;
217         }
218 
219         if (checkDevicePolicyManager) {
220             if (isBiometricDisabledByDevicePolicy(devicePolicyManager, sensor.modality, userId)) {
221                 return BIOMETRIC_DISABLED_BY_DEVICE_POLICY;
222             }
223         }
224 
225         return AUTHENTICATOR_OK;
226     }
227 
isEnabledForApp(BiometricService.SettingObserver settingObserver, @BiometricAuthenticator.Modality int modality, int userId)228     private static boolean isEnabledForApp(BiometricService.SettingObserver settingObserver,
229             @BiometricAuthenticator.Modality int modality, int userId) {
230         return settingObserver.getEnabledForApps(userId);
231     }
232 
isBiometricDisabledByDevicePolicy( DevicePolicyManager devicePolicyManager, @BiometricAuthenticator.Modality int modality, int effectiveUserId)233     private static boolean isBiometricDisabledByDevicePolicy(
234             DevicePolicyManager devicePolicyManager, @BiometricAuthenticator.Modality int modality,
235             int effectiveUserId) {
236         final int biometricToCheck = mapModalityToDevicePolicyType(modality);
237         if (biometricToCheck == DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE) {
238             throw new IllegalStateException("Modality unknown to devicePolicyManager: " + modality);
239         }
240         final int devicePolicyDisabledFeatures =
241                 devicePolicyManager.getKeyguardDisabledFeatures(null, effectiveUserId);
242         final boolean isBiometricDisabled =
243                 (biometricToCheck & devicePolicyDisabledFeatures) != 0;
244         Slog.w(TAG, "isBiometricDisabledByDevicePolicy(" + modality + "," + effectiveUserId
245                 + ")=" + isBiometricDisabled);
246         return isBiometricDisabled;
247     }
248 
249     /**
250      * @param modality one of {@link BiometricAuthenticator#TYPE_FINGERPRINT},
251      * {@link BiometricAuthenticator#TYPE_IRIS} or {@link BiometricAuthenticator#TYPE_FACE}
252      * @return
253      */
mapModalityToDevicePolicyType(int modality)254     private static int mapModalityToDevicePolicyType(int modality) {
255         switch (modality) {
256             case TYPE_FINGERPRINT:
257                 return DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT;
258             case TYPE_IRIS:
259                 return DevicePolicyManager.KEYGUARD_DISABLE_IRIS;
260             case TYPE_FACE:
261                 return DevicePolicyManager.KEYGUARD_DISABLE_FACE;
262             default:
263                 Slog.e(TAG, "Error modality=" + modality);
264                 return DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE;
265         }
266     }
267 
PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested, boolean credentialRequested, List<BiometricSensor> eligibleSensors, List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable, boolean confirmationRequested, boolean ignoreEnrollmentState, int userId, Context context)268     private PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested,
269             boolean credentialRequested, List<BiometricSensor> eligibleSensors,
270             List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable,
271             boolean confirmationRequested, boolean ignoreEnrollmentState, int userId,
272             Context context) {
273         mBiometricRequested = biometricRequested;
274         mBiometricStrengthRequested = biometricStrengthRequested;
275         this.credentialRequested = credentialRequested;
276 
277         this.eligibleSensors = eligibleSensors;
278         this.ineligibleSensors = ineligibleSensors;
279         this.credentialAvailable = credentialAvailable;
280         this.confirmationRequested = confirmationRequested;
281         this.ignoreEnrollmentState = ignoreEnrollmentState;
282         this.userId = userId;
283         this.context = context;
284     }
285 
calculateErrorByPriority()286     private Pair<BiometricSensor, Integer> calculateErrorByPriority() {
287         // If the caller requested STRONG, and the device contains both STRONG and non-STRONG
288         // sensors, prioritize BIOMETRIC_NOT_ENROLLED over the weak sensor's
289         // BIOMETRIC_INSUFFICIENT_STRENGTH error. Pretty sure we can always prioritize
290         // BIOMETRIC_NOT_ENROLLED over any other error (unless of course its calculation is
291         // wrong, in which case we should fix that instead).
292         for (Pair<BiometricSensor, Integer> pair : ineligibleSensors) {
293             if (pair.second == BIOMETRIC_NOT_ENROLLED) {
294                 return pair;
295             }
296         }
297 
298         return ineligibleSensors.get(0);
299     }
300 
301     /**
302      * With {@link PreAuthInfo} generated with the requested authenticators from the public API
303      * surface, combined with the actual sensor/credential and user/system settings, calculate the
304      * internal {@link AuthenticatorStatus} that should be returned to the client. Note that this
305      * will need to be converted into the public API constant.
306      * @return Pair<Modality, Error> with error being the internal {@link AuthenticatorStatus} code
307      */
getInternalStatus()308     private Pair<Integer, Integer> getInternalStatus() {
309         @AuthenticatorStatus final int status;
310         @BiometricAuthenticator.Modality int modality = TYPE_NONE;
311 
312         final SensorPrivacyManager sensorPrivacyManager = context
313                 .getSystemService(SensorPrivacyManager.class);
314 
315         boolean cameraPrivacyEnabled = false;
316         if (sensorPrivacyManager != null) {
317             cameraPrivacyEnabled = sensorPrivacyManager
318                     .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, userId);
319         }
320 
321         if (mBiometricRequested && credentialRequested) {
322             if (credentialAvailable || !eligibleSensors.isEmpty()) {
323                 for (BiometricSensor sensor : eligibleSensors) {
324                     modality |= sensor.modality;
325                 }
326 
327                 if (credentialAvailable) {
328                     modality |= TYPE_CREDENTIAL;
329                     status = AUTHENTICATOR_OK;
330                 } else if (modality == TYPE_FACE && cameraPrivacyEnabled) {
331                     // If the only modality requested is face, credential is unavailable,
332                     // and the face sensor privacy is enabled then return
333                     // BIOMETRIC_SENSOR_PRIVACY_ENABLED.
334                     //
335                     // Note: This sensor will still be eligible for calls to authenticate.
336                     status = BIOMETRIC_SENSOR_PRIVACY_ENABLED;
337                 } else {
338                     status = AUTHENTICATOR_OK;
339                 }
340             } else {
341                 // Pick the first sensor error if it exists
342                 if (!ineligibleSensors.isEmpty()) {
343                     final Pair<BiometricSensor, Integer> pair = calculateErrorByPriority();
344                     modality |= pair.first.modality;
345                     status = pair.second;
346                 } else {
347                     modality |= TYPE_CREDENTIAL;
348                     status = CREDENTIAL_NOT_ENROLLED;
349                 }
350             }
351         } else if (mBiometricRequested) {
352             if (!eligibleSensors.isEmpty()) {
353                 for (BiometricSensor sensor : eligibleSensors) {
354                     modality |= sensor.modality;
355                 }
356                 if (modality == TYPE_FACE && cameraPrivacyEnabled) {
357                     // If the only modality requested is face and the privacy is enabled
358                     // then return BIOMETRIC_SENSOR_PRIVACY_ENABLED.
359                     //
360                     // Note: This sensor will still be eligible for calls to authenticate.
361                     status = BIOMETRIC_SENSOR_PRIVACY_ENABLED;
362                 } else {
363                     status = AUTHENTICATOR_OK;
364                 }
365             } else {
366                 // Pick the first sensor error if it exists
367                 if (!ineligibleSensors.isEmpty()) {
368                     final Pair<BiometricSensor, Integer> pair = calculateErrorByPriority();
369                     modality |= pair.first.modality;
370                     status = pair.second;
371                 } else {
372                     modality |= TYPE_NONE;
373                     status = BIOMETRIC_NO_HARDWARE;
374                 }
375             }
376         } else if (credentialRequested) {
377             modality |= TYPE_CREDENTIAL;
378             status = credentialAvailable ? AUTHENTICATOR_OK : CREDENTIAL_NOT_ENROLLED;
379         } else {
380             // This should not be possible via the public API surface and is here mainly for
381             // "correctness". An exception should have been thrown before getting here.
382             Slog.e(TAG, "No authenticators requested");
383             status = BIOMETRIC_NO_HARDWARE;
384         }
385         Slog.d(TAG, "getCanAuthenticateInternal Modality: " + modality
386                 + " AuthenticatorStatus: " + status);
387 
388         return new Pair<>(modality, status);
389     }
390 
391     /**
392      * @return public BiometricManager result for the current request.
393      */
getCanAuthenticateResult()394     @BiometricManager.BiometricError int getCanAuthenticateResult() {
395         // TODO: Convert this directly
396         return Utils.biometricConstantsToBiometricManager(
397                 Utils.authenticatorStatusToBiometricConstant(
398                         getInternalStatus().second));
399     }
400 
401     /**
402      * For the given request, generate the appropriate reason why authentication cannot be started.
403      * Note that for some errors, modality is intentionally cleared.
404      * @return Pair<Modality, Error> with modality being filtered if necessary, and error
405      * being one of the public {@link android.hardware.biometrics.BiometricConstants} codes.
406      */
getPreAuthenticateStatus()407     Pair<Integer, Integer> getPreAuthenticateStatus() {
408         final Pair<Integer, Integer> internalStatus = getInternalStatus();
409 
410         final int publicError = Utils.authenticatorStatusToBiometricConstant(internalStatus.second);
411         int modality = internalStatus.first;
412         switch (internalStatus.second) {
413             case AUTHENTICATOR_OK:
414             case BIOMETRIC_NO_HARDWARE:
415             case BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE:
416             case BIOMETRIC_HARDWARE_NOT_DETECTED:
417             case BIOMETRIC_NOT_ENROLLED:
418             case CREDENTIAL_NOT_ENROLLED:
419             case BIOMETRIC_LOCKOUT_TIMED:
420             case BIOMETRIC_LOCKOUT_PERMANENT:
421             case BIOMETRIC_SENSOR_PRIVACY_ENABLED:
422                 break;
423 
424             case BIOMETRIC_DISABLED_BY_DEVICE_POLICY:
425             case BIOMETRIC_INSUFFICIENT_STRENGTH:
426             case BIOMETRIC_NOT_ENABLED_FOR_APPS:
427             default:
428                 modality = TYPE_NONE;
429                 break;
430         }
431 
432         return new Pair<>(modality, publicError);
433     }
434 
435     /**
436      * @return true if SystemUI should show the credential UI.
437      */
shouldShowCredential()438     boolean shouldShowCredential() {
439         return credentialRequested && credentialAvailable;
440     }
441 
442     /**
443      * @return bitmask representing the modalities that are running or could be running for the
444      * current session.
445      */
getEligibleModalities()446     @BiometricAuthenticator.Modality int getEligibleModalities() {
447         @BiometricAuthenticator.Modality int modalities = 0;
448         for (BiometricSensor sensor : eligibleSensors) {
449             modalities |= sensor.modality;
450         }
451 
452         if (credentialRequested && credentialAvailable) {
453             modalities |= TYPE_CREDENTIAL;
454         }
455         return modalities;
456     }
457 
numSensorsWaitingForCookie()458     int numSensorsWaitingForCookie() {
459         int numWaiting = 0;
460         for (BiometricSensor sensor : eligibleSensors) {
461             if (sensor.getSensorState() == BiometricSensor.STATE_WAITING_FOR_COOKIE) {
462                 Slog.d(TAG, "Sensor ID: " + sensor.id
463                         + " Waiting for cookie: " + sensor.getCookie());
464                 numWaiting++;
465             }
466         }
467         return numWaiting;
468     }
469 
470     @Override
toString()471     public String toString() {
472         StringBuilder string = new StringBuilder(
473                 "BiometricRequested: " + mBiometricRequested
474                         + ", StrengthRequested: " + mBiometricStrengthRequested
475                         + ", CredentialRequested: " + credentialRequested);
476         string.append(", Eligible:{");
477         for (BiometricSensor sensor: eligibleSensors) {
478             string.append(sensor.id).append(" ");
479         }
480         string.append("}");
481 
482         string.append(", Ineligible:{");
483         for (Pair<BiometricSensor, Integer> ineligible : ineligibleSensors) {
484             string.append(ineligible.first).append(":").append(ineligible.second).append(" ");
485         }
486         string.append("}");
487 
488         string.append(", CredentialAvailable: ").append(credentialAvailable);
489         string.append(", ");
490         return string.toString();
491     }
492 }
493