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