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.getAssociatedDisplayId()); 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 for (Pair<BiometricSensor, Integer> pair : ineligibleSensors) { 277 int status = pair.second; 278 if (status == BIOMETRIC_LOCKOUT_TIMED || status == BIOMETRIC_LOCKOUT_PERMANENT) { 279 sensorLockout = pair; 280 } 281 if (pair.second == BIOMETRIC_NOT_ENROLLED) { 282 sensorNotEnrolled = pair; 283 } 284 } 285 286 // If there is a sensor locked out, prioritize lockout over other sensor's error. 287 // See b/286923477. 288 if (sensorLockout != null) { 289 return sensorLockout; 290 } 291 292 // If the caller requested STRONG, and the device contains both STRONG and non-STRONG 293 // sensors, prioritize BIOMETRIC_NOT_ENROLLED over the weak sensor's 294 // BIOMETRIC_INSUFFICIENT_STRENGTH error. 295 if (sensorNotEnrolled != null) { 296 return sensorNotEnrolled; 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 * 307 * @return Pair<Modality, Error> with error being the internal {@link AuthenticatorStatus} code 308 */ getInternalStatus()309 private Pair<Integer, Integer> getInternalStatus() { 310 @AuthenticatorStatus final int status; 311 @BiometricAuthenticator.Modality int modality = TYPE_NONE; 312 313 boolean cameraPrivacyEnabled = false; 314 if (mBiometricCameraManager != null) { 315 cameraPrivacyEnabled = mBiometricCameraManager.isCameraPrivacyEnabled(); 316 } 317 318 if (mBiometricRequested && credentialRequested) { 319 if (credentialAvailable || !eligibleSensors.isEmpty()) { 320 for (BiometricSensor sensor : eligibleSensors) { 321 modality |= sensor.modality; 322 } 323 324 if (credentialAvailable) { 325 modality |= TYPE_CREDENTIAL; 326 status = AUTHENTICATOR_OK; 327 } else if (modality == TYPE_FACE && cameraPrivacyEnabled) { 328 // If the only modality requested is face, credential is unavailable, 329 // and the face sensor privacy is enabled then return 330 // BIOMETRIC_SENSOR_PRIVACY_ENABLED. 331 // 332 // Note: This sensor will not be eligible for calls to authenticate. 333 status = BIOMETRIC_SENSOR_PRIVACY_ENABLED; 334 } else { 335 status = AUTHENTICATOR_OK; 336 } 337 } else { 338 // Pick the first sensor error if it exists 339 if (!ineligibleSensors.isEmpty()) { 340 final Pair<BiometricSensor, Integer> pair = calculateErrorByPriority(); 341 modality |= pair.first.modality; 342 status = pair.second; 343 } else { 344 modality |= TYPE_CREDENTIAL; 345 status = CREDENTIAL_NOT_ENROLLED; 346 } 347 } 348 } else if (mBiometricRequested) { 349 if (!eligibleSensors.isEmpty()) { 350 for (BiometricSensor sensor : eligibleSensors) { 351 modality |= sensor.modality; 352 } 353 if (modality == TYPE_FACE && cameraPrivacyEnabled) { 354 // If the only modality requested is face and the privacy is enabled 355 // then return BIOMETRIC_SENSOR_PRIVACY_ENABLED. 356 // 357 // Note: This sensor will not be eligible for calls to authenticate. 358 status = BIOMETRIC_SENSOR_PRIVACY_ENABLED; 359 } else { 360 status = AUTHENTICATOR_OK; 361 } 362 } else { 363 // Pick the first sensor error if it exists 364 if (!ineligibleSensors.isEmpty()) { 365 final Pair<BiometricSensor, Integer> pair = calculateErrorByPriority(); 366 modality |= pair.first.modality; 367 status = pair.second; 368 } else { 369 modality |= TYPE_NONE; 370 status = BIOMETRIC_NO_HARDWARE; 371 } 372 } 373 } else if (credentialRequested) { 374 modality |= TYPE_CREDENTIAL; 375 status = credentialAvailable ? AUTHENTICATOR_OK : CREDENTIAL_NOT_ENROLLED; 376 } else { 377 // This should not be possible via the public API surface and is here mainly for 378 // "correctness". An exception should have been thrown before getting here. 379 Slog.e(TAG, "No authenticators requested"); 380 status = BIOMETRIC_NO_HARDWARE; 381 } 382 Slog.d(TAG, "getCanAuthenticateInternal Modality: " + modality 383 + " AuthenticatorStatus: " + status); 384 385 return new Pair<>(modality, status); 386 } 387 388 /** 389 * @return public BiometricManager result for the current request. 390 */ 391 @BiometricManager.BiometricError getCanAuthenticateResult()392 int getCanAuthenticateResult() { 393 // TODO: Convert this directly 394 return Utils.biometricConstantsToBiometricManager( 395 Utils.authenticatorStatusToBiometricConstant( 396 getInternalStatus().second)); 397 } 398 399 /** 400 * For the given request, generate the appropriate reason why authentication cannot be started. 401 * Note that for some errors, modality is intentionally cleared. 402 * 403 * @return Pair<Modality, Error> with modality being filtered if necessary, and error 404 * being one of the public {@link android.hardware.biometrics.BiometricConstants} codes. 405 */ getPreAuthenticateStatus()406 Pair<Integer, Integer> getPreAuthenticateStatus() { 407 final Pair<Integer, Integer> internalStatus = getInternalStatus(); 408 409 final int publicError = Utils.authenticatorStatusToBiometricConstant(internalStatus.second); 410 int modality = internalStatus.first; 411 switch (internalStatus.second) { 412 case AUTHENTICATOR_OK: 413 case BIOMETRIC_NO_HARDWARE: 414 case BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE: 415 case BIOMETRIC_HARDWARE_NOT_DETECTED: 416 case BIOMETRIC_NOT_ENROLLED: 417 case CREDENTIAL_NOT_ENROLLED: 418 case BIOMETRIC_LOCKOUT_TIMED: 419 case BIOMETRIC_LOCKOUT_PERMANENT: 420 case BIOMETRIC_SENSOR_PRIVACY_ENABLED: 421 break; 422 423 case BIOMETRIC_DISABLED_BY_DEVICE_POLICY: 424 case BIOMETRIC_INSUFFICIENT_STRENGTH: 425 case BIOMETRIC_NOT_ENABLED_FOR_APPS: 426 default: 427 modality = TYPE_NONE; 428 break; 429 } 430 431 return new Pair<>(modality, publicError); 432 } 433 434 /** 435 * @return true if SystemUI should show the credential UI. 436 */ shouldShowCredential()437 boolean shouldShowCredential() { 438 return credentialRequested && credentialAvailable; 439 } 440 441 /** 442 * @return bitmask representing the modalities that are running or could be running for the 443 * current session. 444 */ 445 @BiometricAuthenticator.Modality getEligibleModalities()446 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 @IntDef({AUTHENTICATOR_OK, 494 BIOMETRIC_NO_HARDWARE, 495 BIOMETRIC_DISABLED_BY_DEVICE_POLICY, 496 BIOMETRIC_INSUFFICIENT_STRENGTH, 497 BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE, 498 BIOMETRIC_HARDWARE_NOT_DETECTED, 499 BIOMETRIC_NOT_ENROLLED, 500 BIOMETRIC_NOT_ENABLED_FOR_APPS, 501 CREDENTIAL_NOT_ENROLLED, 502 BIOMETRIC_LOCKOUT_TIMED, 503 BIOMETRIC_LOCKOUT_PERMANENT, 504 BIOMETRIC_SENSOR_PRIVACY_ENABLED}) 505 @Retention(RetentionPolicy.SOURCE) 506 @interface AuthenticatorStatus { 507 } 508 } 509