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 109 // Assuming that biometric authenticators are listed in priority-order, the rest of this 110 // function will attempt to find the first authenticator that's as strong or stronger than 111 // the requested strength, available, enrolled, and enabled. The tricky part is returning 112 // the correct error. Error strings that are modality-specific should also respect the 113 // priority-order. 114 115 final List<BiometricSensor> eligibleSensors = new ArrayList<>(); 116 final List<Pair<BiometricSensor, Integer>> ineligibleSensors = new ArrayList<>(); 117 118 if (biometricRequested) { 119 for (BiometricSensor sensor : sensors) { 120 121 @AuthenticatorStatus int status = getStatusForBiometricAuthenticator( 122 devicePolicyManager, settingObserver, sensor, userId, opPackageName, 123 checkDevicePolicyManager, requestedStrength, 124 promptInfo.getAllowedSensorIds(), 125 promptInfo.isIgnoreEnrollmentState(), 126 context); 127 128 Slog.d(TAG, "Package: " + opPackageName 129 + " Sensor ID: " + sensor.id 130 + " Modality: " + sensor.modality 131 + " Status: " + status); 132 133 // A sensor with privacy enabled will still be eligible to 134 // authenticate with biometric prompt. This is so the framework can display 135 // a sensor privacy error message to users after briefly showing the 136 // Biometric Prompt. 137 // 138 // Note: if only a certain sensor is required and the privacy is enabled, 139 // canAuthenticate() will return false. 140 if (status == AUTHENTICATOR_OK || status == BIOMETRIC_SENSOR_PRIVACY_ENABLED) { 141 eligibleSensors.add(sensor); 142 } else { 143 ineligibleSensors.add(new Pair<>(sensor, status)); 144 } 145 } 146 } 147 148 return new PreAuthInfo(biometricRequested, requestedStrength, credentialRequested, 149 eligibleSensors, ineligibleSensors, credentialAvailable, confirmationRequested, 150 promptInfo.isIgnoreEnrollmentState(), userId, context); 151 } 152 153 /** 154 * Returns the status of the authenticator, with errors returned in a specific priority order. 155 * For example, {@link #BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE} is only returned 156 * if it has enrollments, and is enabled for apps. 157 * 158 * @return @AuthenticatorStatus 159 */ getStatusForBiometricAuthenticator( DevicePolicyManager devicePolicyManager, BiometricService.SettingObserver settingObserver, BiometricSensor sensor, int userId, String opPackageName, boolean checkDevicePolicyManager, int requestedStrength, @NonNull List<Integer> requestedSensorIds, boolean ignoreEnrollmentState, Context context)160 private static @AuthenticatorStatus int getStatusForBiometricAuthenticator( 161 DevicePolicyManager devicePolicyManager, 162 BiometricService.SettingObserver settingObserver, 163 BiometricSensor sensor, int userId, String opPackageName, 164 boolean checkDevicePolicyManager, int requestedStrength, 165 @NonNull List<Integer> requestedSensorIds, 166 boolean ignoreEnrollmentState, Context context) { 167 168 if (!requestedSensorIds.isEmpty() && !requestedSensorIds.contains(sensor.id)) { 169 return BIOMETRIC_NO_HARDWARE; 170 } 171 172 final boolean wasStrongEnough = 173 Utils.isAtLeastStrength(sensor.oemStrength, requestedStrength); 174 final boolean isStrongEnough = 175 Utils.isAtLeastStrength(sensor.getCurrentStrength(), requestedStrength); 176 177 if (wasStrongEnough && !isStrongEnough) { 178 return BIOMETRIC_INSUFFICIENT_STRENGTH_AFTER_DOWNGRADE; 179 } else if (!wasStrongEnough) { 180 return BIOMETRIC_INSUFFICIENT_STRENGTH; 181 } 182 183 try { 184 if (!sensor.impl.isHardwareDetected(opPackageName)) { 185 return BIOMETRIC_HARDWARE_NOT_DETECTED; 186 } 187 188 if (!sensor.impl.hasEnrolledTemplates(userId, opPackageName) 189 && !ignoreEnrollmentState) { 190 return BIOMETRIC_NOT_ENROLLED; 191 } 192 final SensorPrivacyManager sensorPrivacyManager = context 193 .getSystemService(SensorPrivacyManager.class); 194 195 if (sensorPrivacyManager != null && sensor.modality == TYPE_FACE) { 196 if (sensorPrivacyManager 197 .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, userId)) { 198 return BIOMETRIC_SENSOR_PRIVACY_ENABLED; 199 } 200 } 201 202 203 final @LockoutTracker.LockoutMode int lockoutMode = 204 sensor.impl.getLockoutModeForUser(userId); 205 if (lockoutMode == LockoutTracker.LOCKOUT_TIMED) { 206 return BIOMETRIC_LOCKOUT_TIMED; 207 } else if (lockoutMode == LockoutTracker.LOCKOUT_PERMANENT) { 208 return BIOMETRIC_LOCKOUT_PERMANENT; 209 } 210 } catch (RemoteException e) { 211 return BIOMETRIC_HARDWARE_NOT_DETECTED; 212 } 213 214 if (!isEnabledForApp(settingObserver, sensor.modality, userId)) { 215 return BIOMETRIC_NOT_ENABLED_FOR_APPS; 216 } 217 218 if (checkDevicePolicyManager) { 219 if (isBiometricDisabledByDevicePolicy(devicePolicyManager, sensor.modality, userId)) { 220 return BIOMETRIC_DISABLED_BY_DEVICE_POLICY; 221 } 222 } 223 224 return AUTHENTICATOR_OK; 225 } 226 isEnabledForApp(BiometricService.SettingObserver settingObserver, @BiometricAuthenticator.Modality int modality, int userId)227 private static boolean isEnabledForApp(BiometricService.SettingObserver settingObserver, 228 @BiometricAuthenticator.Modality int modality, int userId) { 229 return settingObserver.getEnabledForApps(userId); 230 } 231 isBiometricDisabledByDevicePolicy( DevicePolicyManager devicePolicyManager, @BiometricAuthenticator.Modality int modality, int effectiveUserId)232 private static boolean isBiometricDisabledByDevicePolicy( 233 DevicePolicyManager devicePolicyManager, @BiometricAuthenticator.Modality int modality, 234 int effectiveUserId) { 235 final int biometricToCheck = mapModalityToDevicePolicyType(modality); 236 if (biometricToCheck == DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE) { 237 throw new IllegalStateException("Modality unknown to devicePolicyManager: " + modality); 238 } 239 final int devicePolicyDisabledFeatures = 240 devicePolicyManager.getKeyguardDisabledFeatures(null, effectiveUserId); 241 final boolean isBiometricDisabled = 242 (biometricToCheck & devicePolicyDisabledFeatures) != 0; 243 Slog.w(TAG, "isBiometricDisabledByDevicePolicy(" + modality + "," + effectiveUserId 244 + ")=" + isBiometricDisabled); 245 return isBiometricDisabled; 246 } 247 248 /** 249 * @param modality one of {@link BiometricAuthenticator#TYPE_FINGERPRINT}, 250 * {@link BiometricAuthenticator#TYPE_IRIS} or {@link BiometricAuthenticator#TYPE_FACE} 251 * @return 252 */ mapModalityToDevicePolicyType(int modality)253 private static int mapModalityToDevicePolicyType(int modality) { 254 switch (modality) { 255 case TYPE_FINGERPRINT: 256 return DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT; 257 case TYPE_IRIS: 258 return DevicePolicyManager.KEYGUARD_DISABLE_IRIS; 259 case TYPE_FACE: 260 return DevicePolicyManager.KEYGUARD_DISABLE_FACE; 261 default: 262 Slog.e(TAG, "Error modality=" + modality); 263 return DevicePolicyManager.KEYGUARD_DISABLE_FEATURES_NONE; 264 } 265 } 266 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)267 private PreAuthInfo(boolean biometricRequested, int biometricStrengthRequested, 268 boolean credentialRequested, List<BiometricSensor> eligibleSensors, 269 List<Pair<BiometricSensor, Integer>> ineligibleSensors, boolean credentialAvailable, 270 boolean confirmationRequested, boolean ignoreEnrollmentState, int userId, 271 Context context) { 272 mBiometricRequested = biometricRequested; 273 mBiometricStrengthRequested = biometricStrengthRequested; 274 this.credentialRequested = credentialRequested; 275 276 this.eligibleSensors = eligibleSensors; 277 this.ineligibleSensors = ineligibleSensors; 278 this.credentialAvailable = credentialAvailable; 279 this.confirmationRequested = confirmationRequested; 280 this.ignoreEnrollmentState = ignoreEnrollmentState; 281 this.userId = userId; 282 this.context = context; 283 } 284 calculateErrorByPriority()285 private Pair<BiometricSensor, Integer> calculateErrorByPriority() { 286 // If the caller requested STRONG, and the device contains both STRONG and non-STRONG 287 // sensors, prioritize BIOMETRIC_NOT_ENROLLED over the weak sensor's 288 // BIOMETRIC_INSUFFICIENT_STRENGTH error. Pretty sure we can always prioritize 289 // BIOMETRIC_NOT_ENROLLED over any other error (unless of course its calculation is 290 // wrong, in which case we should fix that instead). 291 for (Pair<BiometricSensor, Integer> pair : ineligibleSensors) { 292 if (pair.second == BIOMETRIC_NOT_ENROLLED) { 293 return pair; 294 } 295 } 296 297 return ineligibleSensors.get(0); 298 } 299 300 /** 301 * With {@link PreAuthInfo} generated with the requested authenticators from the public API 302 * surface, combined with the actual sensor/credential and user/system settings, calculate the 303 * internal {@link AuthenticatorStatus} that should be returned to the client. Note that this 304 * will need to be converted into the public API constant. 305 * @return Pair<Modality, Error> with error being the internal {@link AuthenticatorStatus} code 306 */ getInternalStatus()307 private Pair<Integer, Integer> getInternalStatus() { 308 @AuthenticatorStatus final int status; 309 @BiometricAuthenticator.Modality int modality = TYPE_NONE; 310 311 final SensorPrivacyManager sensorPrivacyManager = context 312 .getSystemService(SensorPrivacyManager.class); 313 314 boolean cameraPrivacyEnabled = false; 315 if (sensorPrivacyManager != null) { 316 cameraPrivacyEnabled = sensorPrivacyManager 317 .isSensorPrivacyEnabled(SensorPrivacyManager.Sensors.CAMERA, userId); 318 } 319 320 if (mBiometricRequested && credentialRequested) { 321 if (credentialAvailable || !eligibleSensors.isEmpty()) { 322 for (BiometricSensor sensor : eligibleSensors) { 323 modality |= sensor.modality; 324 } 325 326 if (credentialAvailable) { 327 modality |= TYPE_CREDENTIAL; 328 status = AUTHENTICATOR_OK; 329 } else if (modality == TYPE_FACE && cameraPrivacyEnabled) { 330 // If the only modality requested is face, credential is unavailable, 331 // and the face sensor privacy is enabled then return 332 // BIOMETRIC_SENSOR_PRIVACY_ENABLED. 333 // 334 // Note: This sensor will still be eligible for calls to authenticate. 335 status = BIOMETRIC_SENSOR_PRIVACY_ENABLED; 336 } else { 337 status = AUTHENTICATOR_OK; 338 } 339 } else { 340 // Pick the first sensor error if it exists 341 if (!ineligibleSensors.isEmpty()) { 342 final Pair<BiometricSensor, Integer> pair = calculateErrorByPriority(); 343 modality |= pair.first.modality; 344 status = pair.second; 345 } else { 346 modality |= TYPE_CREDENTIAL; 347 status = CREDENTIAL_NOT_ENROLLED; 348 } 349 } 350 } else if (mBiometricRequested) { 351 if (!eligibleSensors.isEmpty()) { 352 for (BiometricSensor sensor : eligibleSensors) { 353 modality |= sensor.modality; 354 } 355 if (modality == TYPE_FACE && cameraPrivacyEnabled) { 356 // If the only modality requested is face and the privacy is enabled 357 // then return BIOMETRIC_SENSOR_PRIVACY_ENABLED. 358 // 359 // Note: This sensor will still be eligible for calls to authenticate. 360 status = BIOMETRIC_SENSOR_PRIVACY_ENABLED; 361 } else { 362 status = AUTHENTICATOR_OK; 363 } 364 } else { 365 // Pick the first sensor error if it exists 366 if (!ineligibleSensors.isEmpty()) { 367 final Pair<BiometricSensor, Integer> pair = calculateErrorByPriority(); 368 modality |= pair.first.modality; 369 status = pair.second; 370 } else { 371 modality |= TYPE_NONE; 372 status = BIOMETRIC_NO_HARDWARE; 373 } 374 } 375 } else if (credentialRequested) { 376 modality |= TYPE_CREDENTIAL; 377 status = credentialAvailable ? AUTHENTICATOR_OK : CREDENTIAL_NOT_ENROLLED; 378 } else { 379 // This should not be possible via the public API surface and is here mainly for 380 // "correctness". An exception should have been thrown before getting here. 381 Slog.e(TAG, "No authenticators requested"); 382 status = BIOMETRIC_NO_HARDWARE; 383 } 384 Slog.d(TAG, "getCanAuthenticateInternal Modality: " + modality 385 + " AuthenticatorStatus: " + status); 386 387 return new Pair<>(modality, status); 388 } 389 390 /** 391 * @return public BiometricManager result for the current request. 392 */ getCanAuthenticateResult()393 @BiometricManager.BiometricError int getCanAuthenticateResult() { 394 // TODO: Convert this directly 395 return Utils.biometricConstantsToBiometricManager( 396 Utils.authenticatorStatusToBiometricConstant( 397 getInternalStatus().second)); 398 } 399 400 /** 401 * For the given request, generate the appropriate reason why authentication cannot be started. 402 * Note that for some errors, modality is intentionally cleared. 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 */ getEligibleModalities()445 @BiometricAuthenticator.Modality int getEligibleModalities() { 446 @BiometricAuthenticator.Modality int modalities = 0; 447 for (BiometricSensor sensor : eligibleSensors) { 448 modalities |= sensor.modality; 449 } 450 451 if (credentialRequested && credentialAvailable) { 452 modalities |= TYPE_CREDENTIAL; 453 } 454 return modalities; 455 } 456 numSensorsWaitingForCookie()457 int numSensorsWaitingForCookie() { 458 int numWaiting = 0; 459 for (BiometricSensor sensor : eligibleSensors) { 460 if (sensor.getSensorState() == BiometricSensor.STATE_WAITING_FOR_COOKIE) { 461 Slog.d(TAG, "Sensor ID: " + sensor.id 462 + " Waiting for cookie: " + sensor.getCookie()); 463 numWaiting++; 464 } 465 } 466 return numWaiting; 467 } 468 469 @Override toString()470 public String toString() { 471 StringBuilder string = new StringBuilder( 472 "BiometricRequested: " + mBiometricRequested 473 + ", StrengthRequested: " + mBiometricStrengthRequested 474 + ", CredentialRequested: " + credentialRequested); 475 string.append(", Eligible:{"); 476 for (BiometricSensor sensor: eligibleSensors) { 477 string.append(sensor.id).append(" "); 478 } 479 string.append("}"); 480 481 string.append(", Ineligible:{"); 482 for (Pair<BiometricSensor, Integer> ineligible : ineligibleSensors) { 483 string.append(ineligible.first).append(":").append(ineligible.second).append(" "); 484 } 485 string.append("}"); 486 487 string.append(", CredentialAvailable: ").append(credentialAvailable); 488 string.append(", "); 489 return string.toString(); 490 } 491 } 492