blob: aec754048ed02272a266afdbff93198faf709017 [file] [log] [blame]
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.biometrics;
import static android.hardware.biometrics.BiometricManager.Authenticators;
import android.content.Context;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.BiometricPrompt.AuthenticationResultType;
import android.hardware.biometrics.IBiometricNativeHandle;
import android.os.Build;
import android.os.Bundle;
import android.os.NativeHandle;
import android.os.UserHandle;
import android.provider.Settings;
import android.util.Slog;
import java.io.FileDescriptor;
import java.io.IOException;
public class Utils {
public static boolean isDebugEnabled(Context context, int targetUserId) {
if (targetUserId == UserHandle.USER_NULL) {
return false;
}
if (!(Build.IS_ENG || Build.IS_USERDEBUG)) {
return false;
}
if (Settings.Secure.getIntForUser(context.getContentResolver(),
Settings.Secure.BIOMETRIC_DEBUG_ENABLED, 0,
targetUserId) == 0) {
return false;
}
return true;
}
/**
* Combines {@link BiometricPrompt#KEY_ALLOW_DEVICE_CREDENTIAL} with
* {@link BiometricPrompt#KEY_AUTHENTICATORS_ALLOWED}, as the former is not flexible enough.
*/
public static void combineAuthenticatorBundles(Bundle bundle) {
// Cache and remove explicit ALLOW_DEVICE_CREDENTIAL boolean flag from the bundle.
final boolean deviceCredentialAllowed =
bundle.getBoolean(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL, false);
bundle.remove(BiometricPrompt.KEY_ALLOW_DEVICE_CREDENTIAL);
final @Authenticators.Types int authenticators;
if (bundle.containsKey(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED)) {
// Ignore ALLOW_DEVICE_CREDENTIAL flag if AUTH_TYPES_ALLOWED is defined.
authenticators = bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, 0);
} else {
// Otherwise, use ALLOW_DEVICE_CREDENTIAL flag along with Weak+ biometrics by default.
authenticators = deviceCredentialAllowed
? Authenticators.DEVICE_CREDENTIAL | Authenticators.BIOMETRIC_WEAK
: Authenticators.BIOMETRIC_WEAK;
}
bundle.putInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED, authenticators);
}
/**
* @param authenticators composed of one or more values from {@link Authenticators}
* @return true if device credential is allowed.
*/
public static boolean isCredentialRequested(@Authenticators.Types int authenticators) {
return (authenticators & Authenticators.DEVICE_CREDENTIAL) != 0;
}
/**
* @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)}
* @return true if device credential is allowed.
*/
public static boolean isCredentialRequested(Bundle bundle) {
return isCredentialRequested(bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
}
/**
* Checks if any of the publicly defined strengths are set.
*
* @param authenticators composed of one or more values from {@link Authenticators}
* @return minimal allowed biometric strength or 0 if biometric authentication is not allowed.
*/
public static int getPublicBiometricStrength(@Authenticators.Types int authenticators) {
// Only biometrics WEAK and above are allowed to integrate with the public APIs.
return authenticators & Authenticators.BIOMETRIC_WEAK;
}
/**
* Checks if any of the publicly defined strengths are set.
*
* @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)}
* @return minimal allowed biometric strength or 0 if biometric authentication is not allowed.
*/
public static int getPublicBiometricStrength(Bundle bundle) {
return getPublicBiometricStrength(
bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED));
}
/**
* Checks if any of the publicly defined strengths are set.
*
* @param bundle should be first processed by {@link #combineAuthenticatorBundles(Bundle)}
* @return true if biometric authentication is allowed.
*/
public static boolean isBiometricRequested(Bundle bundle) {
return getPublicBiometricStrength(bundle) != 0;
}
/**
* @param sensorStrength the strength of the sensor
* @param requestedStrength the strength that it must meet
* @return true only if the sensor is at least as strong as the requested strength
*/
public static boolean isAtLeastStrength(int sensorStrength, int requestedStrength) {
// Clear out any bits that are not reserved for biometric
sensorStrength = sensorStrength & Authenticators.BIOMETRIC_MIN_STRENGTH;
// If the authenticator contains bits outside of the requested strength, it is too weak.
if ((sensorStrength & ~requestedStrength) != 0) {
return false;
}
for (int i = Authenticators.BIOMETRIC_MAX_STRENGTH;
i <= requestedStrength; i = i << 1 | 1) {
if (i == sensorStrength) {
return true;
}
}
Slog.e(BiometricService.TAG, "Unknown sensorStrength: " + sensorStrength
+ ", requestedStrength: " + requestedStrength);
return false;
}
/**
* Checks if the authenticator configuration is a valid combination of the public APIs
* @param bundle
* @return
*/
public static boolean isValidAuthenticatorConfig(Bundle bundle) {
final int authenticators = bundle.getInt(BiometricPrompt.KEY_AUTHENTICATORS_ALLOWED);
return isValidAuthenticatorConfig(authenticators);
}
/**
* Checks if the authenticator configuration is a valid combination of the public APIs
* @param authenticators
* @return
*/
public static boolean isValidAuthenticatorConfig(int authenticators) {
// The caller is not required to set the authenticators. But if they do, check the below.
if (authenticators == 0) {
return true;
}
// Check if any of the non-biometric and non-credential bits are set. If so, this is
// invalid.
final int testBits = ~(Authenticators.DEVICE_CREDENTIAL
| Authenticators.BIOMETRIC_MIN_STRENGTH);
if ((authenticators & testBits) != 0) {
Slog.e(BiometricService.TAG, "Non-biometric, non-credential bits found."
+ " Authenticators: " + authenticators);
return false;
}
// Check that biometrics bits are either NONE, WEAK, or STRONG. If NONE, DEVICE_CREDENTIAL
// should be set.
final int biometricBits = authenticators & Authenticators.BIOMETRIC_MIN_STRENGTH;
if (biometricBits == Authenticators.EMPTY_SET
&& isCredentialRequested(authenticators)) {
return true;
} else if (biometricBits == Authenticators.BIOMETRIC_STRONG) {
return true;
} else if (biometricBits == Authenticators.BIOMETRIC_WEAK) {
return true;
}
Slog.e(BiometricService.TAG, "Unsupported biometric flags. Authenticators: "
+ authenticators);
// Non-supported biometric flags are being used
return false;
}
/**
* Converts error codes from BiometricConstants, which are used in most of the internal plumbing
* and eventually returned to {@link BiometricPrompt.AuthenticationCallback} to public
* {@link BiometricManager} constants, which are used by APIs such as
* {@link BiometricManager#canAuthenticate(int)}
*
* @param biometricConstantsCode see {@link BiometricConstants}
* @return see {@link BiometricManager}
*/
public static int biometricConstantsToBiometricManager(int biometricConstantsCode) {
final int biometricManagerCode;
switch (biometricConstantsCode) {
case BiometricConstants.BIOMETRIC_SUCCESS:
biometricManagerCode = BiometricManager.BIOMETRIC_SUCCESS;
break;
case BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS:
case BiometricConstants.BIOMETRIC_ERROR_NO_DEVICE_CREDENTIAL:
biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED;
break;
case BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE:
biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
break;
case BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT:
biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE;
break;
case BiometricConstants.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED:
biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED;
break;
default:
Slog.e(BiometricService.TAG, "Unhandled result code: " + biometricConstantsCode);
biometricManagerCode = BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
break;
}
return biometricManagerCode;
}
/**
* Converts a {@link BiometricPrompt} dismissal reason to an authentication type at the level of
* granularity supported by {@link BiometricPrompt.AuthenticationResult}.
*
* @param reason The reason that the {@link BiometricPrompt} was dismissed. Must be one of:
* {@link BiometricPrompt#DISMISSED_REASON_CREDENTIAL_CONFIRMED},
* {@link BiometricPrompt#DISMISSED_REASON_BIOMETRIC_CONFIRMED}, or
* {@link BiometricPrompt#DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED}
* @return An integer representing the authentication type for {@link
* BiometricPrompt.AuthenticationResult}.
* @throws IllegalArgumentException if given an invalid dismissal reason.
*/
public static @AuthenticationResultType int getAuthenticationTypeForResult(int reason) {
switch (reason) {
case BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED:
return BiometricPrompt.AUTHENTICATION_RESULT_TYPE_DEVICE_CREDENTIAL;
case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED:
case BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED:
return BiometricPrompt.AUTHENTICATION_RESULT_TYPE_BIOMETRIC;
default:
throw new IllegalArgumentException("Unsupported dismissal reason: " + reason);
}
}
/**
* Converts an {@link IBiometricNativeHandle} to a {@link NativeHandle} by duplicating the
* the underlying file descriptors.
*
* Both the original and new handle must be closed after use.
*
* @param h {@link IBiometricNativeHandle} received as a binder call argument. Usually used to
* identify a WindowManager window. Can be null.
* @return A {@link NativeHandle} representation of {@code h}. Will be null if either {@code h}
* or its contents are null.
*/
public static NativeHandle dupNativeHandle(IBiometricNativeHandle h) {
NativeHandle handle = null;
if (h != null && h.fds != null && h.ints != null) {
FileDescriptor[] fds = new FileDescriptor[h.fds.length];
for (int i = 0; i < h.fds.length; ++i) {
try {
fds[i] = h.fds[i].dup().getFileDescriptor();
} catch (IOException e) {
return null;
}
}
handle = new NativeHandle(fds, h.ints, true /* own */);
}
return handle;
}
}