blob: dbae011f4188ac8edf96a726e95afb6afada8050 [file] [log] [blame]
/*
* Copyright (C) 2018 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 android.hardware.biometrics.cts;
import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_CONVENIENCE;
import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG;
import static android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_WEAK;
import static android.hardware.biometrics.BiometricManager.Authenticators.DEVICE_CREDENTIAL;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assume.assumeFalse;
import static org.junit.Assume.assumeTrue;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.Flags;
import android.platform.test.annotations.Presubmit;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.text.TextUtils;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SmallTest;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.compatibility.common.util.ApiTest;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
/**
* Basic test cases for BiometricManager. See the manual biometric tests in CtsVerifier for a more
* comprehensive test suite.
*/
@RunWith(AndroidJUnit4.class)
@SmallTest
@Presubmit
public class BiometricManagerTest {
@Rule
public final CheckFlagsRule mCheckFlagsRule =
DeviceFlagsValueProvider.createCheckFlagsRule();
private Context mContext;
private BiometricManager mBiometricManager;
@Before
public void setUp() {
mContext = InstrumentationRegistry.getInstrumentation().getContext();
mBiometricManager = mContext.getSystemService(BiometricManager.class);
}
@Test
public void test_canAuthenticate() {
assertNotEquals("Device should not have any biometrics enrolled",
mBiometricManager.canAuthenticate(), BiometricManager.BIOMETRIC_SUCCESS);
assertNotEquals("Device should not have any biometrics enrolled",
mBiometricManager.canAuthenticate(DEVICE_CREDENTIAL | BIOMETRIC_WEAK),
BiometricManager.BIOMETRIC_SUCCESS);
}
@Test
public void test_getButtonLabel_isDifferentForDistinctAuthTypes() {
// Ensure labels for biometrics and credential are different (if non-empty).
final CharSequence biometricLabel = mBiometricManager.getStrings(BIOMETRIC_WEAK)
.getButtonLabel();
final CharSequence credentialLabel = mBiometricManager.getStrings(DEVICE_CREDENTIAL)
.getButtonLabel();
if (!TextUtils.isEmpty(biometricLabel) || !TextUtils.isEmpty(credentialLabel)) {
assertFalse("Biometric and credential button labels should not match",
TextUtils.equals(biometricLabel, credentialLabel));
}
}
@Test
public void test_getButtonLabel_isNonEmptyIfPresentForSubAuthType() {
// Ensure label for biometrics|credential is non-empty if one for biometrics or credential
// (or both) is non-empty.
final CharSequence biometricOrCredentialLabel = mBiometricManager.getStrings(
BIOMETRIC_WEAK | DEVICE_CREDENTIAL).getButtonLabel();
final CharSequence biometricLabel =
mBiometricManager.getStrings(BIOMETRIC_WEAK).getButtonLabel();
final CharSequence credentialLabel = mBiometricManager.getStrings(
DEVICE_CREDENTIAL).getButtonLabel();
final boolean isLabelPresentForSubAuthType =
!TextUtils.isEmpty(biometricLabel) || !TextUtils.isEmpty(credentialLabel);
boolean isBiometricOrCredentialLabel;
if (hasOnlyConvenienceSensors()) {
isBiometricOrCredentialLabel = TextUtils.isEmpty(credentialLabel);
} else {
isBiometricOrCredentialLabel = TextUtils.isEmpty(biometricOrCredentialLabel);
}
assertFalse("Label should not be empty if one for an authenticator sub-type is non-empty",
isBiometricOrCredentialLabel && isLabelPresentForSubAuthType);
}
@Test
public void test_getPromptMessage_isDifferentForDistinctAuthTypes() {
// Ensure messages for biometrics and credential are different (if non-empty).
final CharSequence biometricMessage = mBiometricManager.getStrings(BIOMETRIC_WEAK)
.getPromptMessage();
final CharSequence credentialMessage = mBiometricManager.getStrings(DEVICE_CREDENTIAL)
.getPromptMessage();
if (!TextUtils.isEmpty(biometricMessage) || !TextUtils.isEmpty(credentialMessage)) {
assertFalse("Biometric and credential prompt messages should not match",
TextUtils.equals(biometricMessage, credentialMessage));
}
}
@Test
public void test_getPromptMessage_isDifferentForBiometricsIfCredentialAllowed() {
// Ensure message for biometrics and biometrics|credential are different (if non-empty).
final CharSequence biometricMessage = mBiometricManager.getStrings(BIOMETRIC_WEAK)
.getPromptMessage();
final CharSequence bioOrCredMessage = mBiometricManager.getStrings(
BIOMETRIC_WEAK | DEVICE_CREDENTIAL).getPromptMessage();
if (!TextUtils.isEmpty(biometricMessage) || !TextUtils.isEmpty(bioOrCredMessage)) {
assertFalse("Biometric and biometric|credential prompt messages should not match",
TextUtils.equals(biometricMessage, bioOrCredMessage));
}
}
@Test
public void test_getSettingName_forBiometrics() {
final PackageManager pm = mContext.getPackageManager();
final boolean hasFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
final boolean hasIris = pm.hasSystemFeature(PackageManager.FEATURE_IRIS);
final boolean hasFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
assumeTrue("Test requires biometric hardware", hasFingerprint || hasIris || hasFace);
assumeFalse("Test requires biometric hardware above weak level",
hasOnlyConvenienceSensors());
// Ensure biometric setting name is non-empty if device supports biometrics.
assertFalse("Name should be non-empty if device supports biometric authentication",
TextUtils.isEmpty(mBiometricManager.getStrings(BIOMETRIC_WEAK).getSettingName()));
assertFalse("Name should be non-empty if device supports biometric authentication",
TextUtils.isEmpty(mBiometricManager.getStrings(BIOMETRIC_WEAK | DEVICE_CREDENTIAL)
.getSettingName()));
}
private boolean hasOnlyConvenienceSensors() {
return mBiometricManager.canAuthenticate(BIOMETRIC_WEAK)
== BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE;
}
@Test
public void test_getSettingName_forCredential() {
final KeyguardManager km = mContext.getSystemService(KeyguardManager.class);
assumeTrue("Test requires KeyguardManager", km != null);
// Ensure credential setting name is non-empty if device supports PIN/pattern/password.
assertFalse("Name should be non-empty if device supports PIN/pattern/password",
TextUtils.isEmpty(mBiometricManager.getStrings(DEVICE_CREDENTIAL)
.getSettingName()));
assertFalse("Name should be non-empty if device supports PIN/pattern/password",
TextUtils.isEmpty(mBiometricManager.getStrings(BIOMETRIC_WEAK | DEVICE_CREDENTIAL)
.getSettingName()));
}
@Test
@ApiTest(apis = {"android.hardware.biometrics.BiometricManager#getLastAuthenticationTime"})
@RequiresFlagsDisabled(Flags.FLAG_LAST_AUTHENTICATION_TIME)
public void testGetLastAuthenticationTime_flagOff_throwsUnsupportedOperation() {
assertThrows(
"Should throw UnsupportedOperationException when flag is disabled",
UnsupportedOperationException.class,
() -> mBiometricManager.getLastAuthenticationTime(BIOMETRIC_STRONG));
}
@Test
@ApiTest(apis = {"android.hardware.biometrics.BiometricManager#getLastAuthenticationTime"})
@RequiresFlagsEnabled(Flags.FLAG_LAST_AUTHENTICATION_TIME)
public void testGetLastAuthenticationTime_allowsStrongAuthenticator() {
assertEquals("BIOMETRIC_STRONG should have no auth time",
BiometricManager.BIOMETRIC_NO_AUTHENTICATION,
mBiometricManager.getLastAuthenticationTime(BIOMETRIC_STRONG));
}
@Test
@ApiTest(apis = {"android.hardware.biometrics.BiometricManager#getLastAuthenticationTime"})
@RequiresFlagsEnabled(Flags.FLAG_LAST_AUTHENTICATION_TIME)
public void testGetLastAuthenticationTime_allowsDeviceCredentialAuthenticator() {
assertEquals("DEVICE_CREDENTIAL should have no auth time",
BiometricManager.BIOMETRIC_NO_AUTHENTICATION,
mBiometricManager.getLastAuthenticationTime(DEVICE_CREDENTIAL));
}
@Test
@ApiTest(apis = {"android.hardware.biometrics.BiometricManager#getLastAuthenticationTime"})
@RequiresFlagsEnabled(Flags.FLAG_LAST_AUTHENTICATION_TIME)
public void testGetLastAuthenticationTime_allowsDeviceCredentialAndStrongAuthenticator() {
assertEquals("DEVICE_CREDENTIAL and BIOMETRIC_STRONG should have no auth time",
BiometricManager.BIOMETRIC_NO_AUTHENTICATION,
mBiometricManager.getLastAuthenticationTime(DEVICE_CREDENTIAL | BIOMETRIC_STRONG));
}
@Test
@ApiTest(apis = {"android.hardware.biometrics.BiometricManager#getLastAuthenticationTime"})
public void testGetLastAuthenticationTime_throwsNoAuthenticator() {
assertThrows("0 should not be allowed",
IllegalArgumentException.class,
() -> mBiometricManager.getLastAuthenticationTime(0));
}
@Test
@ApiTest(apis = {"android.hardware.biometrics.BiometricManager#getLastAuthenticationTime"})
public void testGetLastAuthenticationTime_throwsBogusAuthenticator() {
assertThrows("42 should not be allowed",
IllegalArgumentException.class,
() -> mBiometricManager.getLastAuthenticationTime(42));
}
@Test
@ApiTest(apis = {"android.hardware.biometrics.BiometricManager#getLastAuthenticationTime"})
public void testGetLastAuthenticationTime_throwsWeakAuthenticator() {
assertThrows("BIOMETRIC_WEAK should not be allowed",
IllegalArgumentException.class,
() -> mBiometricManager.getLastAuthenticationTime(BIOMETRIC_WEAK));
}
@Test
@ApiTest(apis = {"android.hardware.biometrics.BiometricManager#getLastAuthenticationTime"})
public void testGetLastAuthenticationTime_throwsConvenienceAuthenticator() {
assertThrows("BIOMETRIC_CONVENIENCE should not be allowed",
IllegalArgumentException.class,
() -> mBiometricManager.getLastAuthenticationTime(BIOMETRIC_CONVENIENCE));
}
}