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