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