blob: 1beede93095f13ff4a07ffd71354c07ad59569dd [file] [log] [blame]
/*
* Copyright (C) 2021 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.server.biometrics;
import static android.server.biometrics.Components.CLASS_2_BIOMETRIC_ACTIVITY;
import static android.server.biometrics.Components.CLASS_3_BIOMETRIC_ACTIVITY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
import android.content.ComponentName;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricManager.Authenticators;
import android.hardware.biometrics.BiometricTestSession;
import android.hardware.biometrics.SensorProperties;
import android.platform.test.annotations.Presubmit;
import android.server.wm.TestJournalProvider.TestJournal;
import android.server.wm.TestJournalProvider.TestJournalContainer;
import android.util.Log;
import com.android.compatibility.common.util.ApiTest;
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.List;
/**
* Tests for security related functionality such as downgrading and upgrading biometric strength.
*/
@Presubmit
public class BiometricSecurityTests extends BiometricTestBase {
private static final String TAG = "BiometricTests/Security";
private static final String DEVICE_CONFIG_NAMESPACE = "biometrics";
private static final String DEVICE_CONFIG_BIO_STRENGTH_KEY = "biometric_strengths";
@BeforeClass
public static void ensureReset() throws Exception {
revertSensorStrengths();
}
@After
public void teardown() throws Exception {
revertSensorStrengths();
}
/**
* A strong biometric should be able to perform auth with any requested strength, since it is
* the highest biometric class. For example,
* +-------------------+--------------------+----------+
* | Original Strength | Requested Strength | Result |
* +-------------------+--------------------+----------+
* | BIOMETRIC_STRONG | BIOMETRIC_STRONG | Accepted |
* +-------------------+--------------------+----------+
* | BIOMETRIC_STRONG | BIOMETRIC_WEAK | Accepted |
* +-------------------+--------------------+----------+
* Note that since BiometricPrompt does not support Convenience biometrics, currently we don't
* have a way to test cases where the requested strength is BIOMETRIC_CONVENIENCE.
*/
@ApiTest(apis = {
"android.hardware.biometrics."
+ "BiometricManager#canAuthenticate",
"android.hardware.biometrics."
+ "BiometricPrompt.Builder#setAllowedAuthenticators",
"android.hardware.biometrics."
+ "BiometricPrompt#authenticate"})
@Test
public void testBiometricStrength_StrongSensor() throws Exception {
assumeTrue(Utils.isFirstApiLevel29orGreater());
final List<Integer> sensors = getSensorsOfTargetStrength(SensorProperties.STRENGTH_STRONG);
assumeTrue("testBiometricStrength_StrongSensor: numSensors=" + sensors.size(),
sensors.size() > 0);
// Tuple of originalStrength and requestedStrength
final int[][] testCases = {
// Request Strong auth
{Authenticators.BIOMETRIC_STRONG, Authenticators.BIOMETRIC_STRONG},
// Request Weak auth
{Authenticators.BIOMETRIC_STRONG, Authenticators.BIOMETRIC_WEAK}
};
for (Integer sensorId : sensors) {
for (int i = 0; i < testCases.length; i++) {
testBiometricStrength_forSensor_authAllowed(sensorId,
testCases[i][0] /* originalStrength */,
testCases[i][1] /* requestedStrength */);
}
}
}
/**
* A weak biometric may or may not be able to perform auth, depending on the requested strength.
* For example,
* +-------------------+--------------------+----------+
* | Original Strength | Requested Strength | Result |
* +-------------------+--------------------+----------+
* | BIOMETRIC_WEAK | BIOMETRIC_STRONG | Error |
* +-------------------+--------------------+----------+
* | BIOMETRIC_WEAK | BIOMETRIC_WEAK | Accepted |
* +-------------------+--------------------+----------+
* Note that since BiometricPrompt does not support Convenience biometrics, currently we don't
* have a way to test cases where the requested strength is BIOMETRIC_CONVENIENCE.
*/
@ApiTest(apis = {
"android.hardware.biometrics."
+ "BiometricManager#canAuthenticate",
"android.hardware.biometrics."
+ "BiometricPrompt.Builder#setAllowedAuthenticators",
"android.hardware.biometrics."
+ "BiometricPrompt#authenticate"})
@Test
public void testBiometricStrength_WeakSensor() throws Exception {
assumeTrue(Utils.isFirstApiLevel29orGreater());
final List<Integer> sensors = getSensorsOfTargetStrength(SensorProperties.STRENGTH_WEAK);
assumeTrue("testBiometricStrength_WeakSensor: numSensors: " + sensors.size(),
sensors.size() > 0);
for (Integer sensorId : sensors) {
testBiometricStrength_forSensor_authDisallowed(sensorId,
Authenticators.BIOMETRIC_WEAK /* originalStrength */,
Authenticators.BIOMETRIC_STRONG /* requestedStrength */,
mSensorProperties.size() > 1 /* hasMultiSensors */);
testBiometricStrength_forSensor_authAllowed(sensorId,
Authenticators.BIOMETRIC_WEAK /* originalStrength */,
Authenticators.BIOMETRIC_WEAK /* requestedStrength */);
}
}
/**
* A convenience biometric should not be able to perform auth with the following requested
* strength, due to insufficient strength.
* +-----------------------+--------------------+--------+
* | Original Strength | Requested Strength | Result |
* +-----------------------+--------------------+--------+
* | BIOMETRIC_CONVENIENCE | BIOMETRIC_STRONG | Error |
* +-----------------------+--------------------+--------+
* | BIOMETRIC_CONVENIENCE | BIOMETRIC_WEAK | Error |
* +-----------------------+--------------------+--------+
* Note that since BiometricPrompt does not support Convenience biometrics, currently we don't
* have a way to test cases where the requested strength is BIOMETRIC_CONVENIENCE.
*/
@ApiTest(apis = {
"android.hardware.biometrics."
+ "BiometricManager#canAuthenticate",
"android.hardware.biometrics."
+ "BiometricPrompt.Builder#setAllowedAuthenticators",
"android.hardware.biometrics."
+ "BiometricPrompt#authenticate"})
@Test
public void testBiometricStrength_ConvenienceSensor() throws Exception {
assumeTrue(Utils.isFirstApiLevel29orGreater());
final List<Integer> sensors =
getSensorsOfTargetStrength(SensorProperties.STRENGTH_CONVENIENCE);
assumeTrue("testBiometricStrength_ConvenienceSensor: numSensors=" + sensors.size(),
sensors.size() > 0);
// Tuple of originalStrength and requestedStrength
final int[][] testCases = {
// Request Strong auth
{Authenticators.BIOMETRIC_CONVENIENCE, Authenticators.BIOMETRIC_STRONG},
// Request Weak auth
{Authenticators.BIOMETRIC_CONVENIENCE, Authenticators.BIOMETRIC_WEAK}
};
for (Integer sensorId : sensors) {
for (int i = 0; i < testCases.length; i++) {
testBiometricStrength_forSensor_authDisallowed(sensorId,
testCases[i][0] /* originalStrength */,
testCases[i][1] /* requestedStrength */,
mSensorProperties.size() > 1 /* hasMultiSensors */);
}
}
}
private void testBiometricStrength_forSensor_authAllowed(int sensorId, int originalStrength,
int requestedStrength) throws Exception {
Log.d(TAG, "testBiometricStrength_forSensor_authAllowed: "
+ ", sensorId=" + sensorId
+ ", originalStrength=" + originalStrength
+ ", requestedStrength=" + requestedStrength);
final ComponentName componentName = getComponentName(requestedStrength);
// Reset to the original strength in case it's ever changed before the test
updateStrengthAndVerify(sensorId, originalStrength);
try (BiometricTestSession session = mBiometricManager.createTestSession(sensorId);
ActivitySession activitySession = new ActivitySession(this, componentName)) {
final int userId = Utils.getUserId();
waitForAllUnenrolled();
enrollForSensor(session, sensorId);
final TestJournal journal =
TestJournalContainer.get(activitySession.getComponentName());
// No error code should be returned for the requested strength
int errCode = mBiometricManager.canAuthenticate(requestedStrength);
assertEquals("Device should allow auth with the requested biometric",
BiometricManager.BIOMETRIC_SUCCESS, errCode);
// Launch test activity
launchActivityAndWaitForResumed(activitySession);
BiometricCallbackHelper.State callbackState = getCallbackState(journal);
assertNotNull(callbackState);
BiometricServiceState state = getCurrentState();
assertTrue(state.toString(), state.mSensorStates.sensorStates.get(sensorId).isBusy());
// Auth should work
successfullyAuthenticate(session, userId);
mInstrumentation.waitForIdleSync();
callbackState = getCallbackState(journal);
assertNotNull(callbackState);
assertEquals(callbackState.toString(), 0, callbackState.mNumAuthRejected);
assertEquals(callbackState.toString(), 1, callbackState.mNumAuthAccepted);
assertEquals(callbackState.toString(), 0, callbackState.mAcquiredReceived.size());
assertEquals(callbackState.toString(), 0, callbackState.mErrorsReceived.size());
}
}
private void testBiometricStrength_forSensor_authDisallowed(int sensorId, int originalStrength,
int requestedStrength, boolean hasMultiSensors) throws Exception {
Log.d(TAG, "testBiometricStrength_forSensor_authDisallowed: "
+ ", sensorId=" + sensorId
+ ", originalStrength=" + originalStrength
+ ", requestedStrength=" + requestedStrength
+ ", hasMultiSensors=" + hasMultiSensors);
final ComponentName componentName = getComponentName(requestedStrength);
// Reset to the original strength in case it's ever changed before the test
updateStrengthAndVerify(sensorId, originalStrength);
try (BiometricTestSession session = mBiometricManager.createTestSession(sensorId);
ActivitySession activitySession = new ActivitySession(this, componentName)) {
waitForAllUnenrolled();
enrollForSensor(session, sensorId);
final TestJournal journal =
TestJournalContainer.get(activitySession.getComponentName());
// Error code should be returned for the requested strength due to insufficient strength
int errCode = mBiometricManager.canAuthenticate(requestedStrength);
checkErrCode("Device shouldn't allow auth with biometrics that have insufficient"
+ " strength. errCode: " + errCode,
errCode, BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE,
hasMultiSensors);
// Launch test activity
launchActivityAndWaitForResumed(activitySession);
// Auth shouldn't work and error code should be returned
mInstrumentation.waitForIdleSync();
BiometricCallbackHelper.State callbackState = getCallbackState(journal);
assertNotNull(callbackState);
assertEquals(callbackState.toString(), 0, callbackState.mNumAuthRejected);
assertEquals(callbackState.toString(), 0, callbackState.mNumAuthAccepted);
assertEquals(callbackState.toString(), 0, callbackState.mAcquiredReceived.size());
assertEquals(callbackState.toString(), 1, callbackState.mErrorsReceived.size());
checkErrCode(callbackState.toString(), (int) callbackState.mErrorsReceived.get(0),
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE, hasMultiSensors);
}
}
/**
* The strength of a Strong biometric may need to be downgraded to a weaker one if the biometric
* requires a security update. After downgrading, the biometric may or may not be able to
* perform auth with the requested strength. For example,
* +-------------------+-----------------------+--------------------+----------+
* | Original Strength | Target Strength | Requested Strength | Result |
* +-------------------+-----------------------+--------------------+----------+
* | BIOMETRIC_STRONG | BIOMETRIC_WEAK | BIOMETRIC_STRONG | Error |
* +-------------------+-----------------------+--------------------+----------+
* | BIOMETRIC_STRONG | BIOMETRIC_WEAK | BIOMETRIC_WEAK | Accepted |
* +-------------------+-----------------------+--------------------+----------+
* | BIOMETRIC_STRONG | BIOMETRIC_CONVENIENCE | BIOMETRIC_STRONG | Error |
* +-------------------+-----------------------+--------------------+----------+
* | BIOMETRIC_STRONG | BIOMETRIC_CONVENIENCE | BIOMETRIC_WEAK | Error |
* +-------------------+-----------------------+--------------------+----------+
* Note that since BiometricPrompt does not support Convenience biometrics, currently we don't
* have a way to test cases where the requested strength is BIOMETRIC_CONVENIENCE.
*/
@Test
public void testBiometricStrengthDowngraded_StrongSensor() throws Exception {
assumeTrue(Utils.isFirstApiLevel29orGreater());
final List<Integer> sensors = getSensorsOfTargetStrength(SensorProperties.STRENGTH_STRONG);
assumeTrue("testBiometricStrengthDowngraded_StrongSensor: numSensors=" + sensors.size(),
sensors.size() > 0);
// Tuple of originalStrength, targetStrength, and requestedStrength
final int[][] testCases = {
// Downgrade Strong to Weak, and request Strong auth
{Authenticators.BIOMETRIC_STRONG, Authenticators.BIOMETRIC_WEAK,
Authenticators.BIOMETRIC_STRONG},
// Downgrade Strong to Weak, and request Weak auth
{Authenticators.BIOMETRIC_STRONG, Authenticators.BIOMETRIC_WEAK,
Authenticators.BIOMETRIC_WEAK},
// Downgrade Strong to Convenience, and request Strong auth
{Authenticators.BIOMETRIC_STRONG, Authenticators.BIOMETRIC_CONVENIENCE,
Authenticators.BIOMETRIC_STRONG},
// Downgrade Strong to Convenience, and request Weak auth
{Authenticators.BIOMETRIC_STRONG, Authenticators.BIOMETRIC_CONVENIENCE,
Authenticators.BIOMETRIC_WEAK}
};
for (Integer sensorId : sensors) {
for (int i = 0; i < testCases.length; i++) {
testBiometricStrengthDowngraded_forSensor(sensorId,
testCases[i][0] /* originalStrength */,
testCases[i][1] /* targetStrength */,
testCases[i][2] /* requestedStrength */,
mSensorProperties.size() > 1 /* hasMultiSensors */);
}
}
}
/**
* The strength of a Weak biometric may need to be downgraded to a weaker one if the biometric
* requires a security update. After downgrading, the biometric may or may not be able to
* perform auth with the requested strength. For example,
* +-------------------+-----------------------+--------------------+--------+
* | Original Strength | Target Strength | Requested Strength | Result |
* +-------------------+-----------------------+--------------------+--------+
* | BIOMETRIC_WEAK | BIOMETRIC_CONVENIENCE | BIOMETRIC_WEAK | Error |
* +-------------------+-----------------------+--------------------+--------+
* Note that since BiometricPrompt does not support Convenience biometrics, currently we don't
* have a way to test cases where the requested strength is BIOMETRIC_CONVENIENCE.
*/
@Test
public void testBiometricStrengthDowngraded_WeakSensor() throws Exception {
assumeTrue(Utils.isFirstApiLevel29orGreater());
final List<Integer> sensors = getSensorsOfTargetStrength(SensorProperties.STRENGTH_WEAK);
assumeTrue("testBiometricStrengthDowngraded_WeakSensor: numSensors: " + sensors.size(),
sensors.size() > 0);
// Tuple of originalStrength, targetStrength, and requestedStrength
final int[][] testCases = {
// Downgrade Weak to Convenience, and request Weak auth
{Authenticators.BIOMETRIC_WEAK, Authenticators.BIOMETRIC_CONVENIENCE,
Authenticators.BIOMETRIC_WEAK}
};
for (Integer sensorId : sensors) {
for (int i = 0; i < testCases.length; i++) {
testBiometricStrengthDowngraded_forSensor(sensorId,
testCases[i][0] /* originalStrength */,
testCases[i][1] /* targetStrength */,
testCases[i][2] /* requestedStrength */,
mSensorProperties.size() > 1 /* hasMultiSensors */);
}
}
}
private void testBiometricStrengthDowngraded_forSensor(int sensorId, int originalStrength,
int targetStrength, int requestedStrength, boolean hasMultiSensors) throws Exception {
Log.d(TAG, "testBiometricStrengthDowngraded_forSensor: "
+ ", sensorId=" + sensorId
+ ", originalStrength=" + originalStrength
+ ", targetStrength=" + targetStrength
+ ", requestedStrength=" + requestedStrength
+ ", hasMultiSensors=" + hasMultiSensors);
final ComponentName componentName = getComponentName(requestedStrength);
try (BiometricTestSession session = mBiometricManager.createTestSession(sensorId);
ActivitySession activitySession = new ActivitySession(this, componentName)) {
final int userId = Utils.getUserId();
waitForAllUnenrolled();
enrollForSensor(session, sensorId);
final TestJournal journal =
TestJournalContainer.get(activitySession.getComponentName());
BiometricCallbackHelper.State callbackState;
BiometricServiceState state;
// Downgrade the biometric strength to the target strength
updateStrengthAndVerify(sensorId, targetStrength);
// After downgrading, check whether auth works
// TODO: should check if targetStrength is at least as strong as the requestedStrength,
// but some strength constants that are needed for the calculation are not exposed in
// BiometricManager.
if (targetStrength == requestedStrength) {
Log.d(TAG, "The targetStrength is as strong as the requestedStrength");
// No error code should be returned since biometric has sufficient strength if
// request weak auth
int errCode = mBiometricManager.canAuthenticate(requestedStrength);
assertEquals("Device should allow auth with the requested biometric",
BiometricManager.BIOMETRIC_SUCCESS, errCode);
// Launch test activity
launchActivityAndWaitForResumed(activitySession);
state = getCurrentState();
assertTrue(state.toString(),
state.mSensorStates.sensorStates.get(sensorId).isBusy());
// Auth should work
successfullyAuthenticate(session, userId);
mInstrumentation.waitForIdleSync();
callbackState = getCallbackState(journal);
assertNotNull(callbackState);
assertEquals(callbackState.toString(), 0, callbackState.mNumAuthRejected);
assertEquals(callbackState.toString(), 1, callbackState.mNumAuthAccepted);
assertEquals(callbackState.toString(), 0, callbackState.mAcquiredReceived.size());
assertEquals(callbackState.toString(), 0, callbackState.mErrorsReceived.size());
} else {
Log.d(TAG, "The targetStrength is not strong enough");
// Error code should be returned
int errCode = mBiometricManager.canAuthenticate(requestedStrength);
checkErrCode("Device shouldn't allow auth with biometrics that require security"
+ " update. errCode: " + errCode,
errCode, BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED,
hasMultiSensors);
// Launch test activity
launchActivityAndWaitForResumed(activitySession);
// Auth shouldn't work and error code should be returned
mInstrumentation.waitForIdleSync();
callbackState = getCallbackState(journal);
assertNotNull(callbackState);
assertEquals(callbackState.toString(), 0, callbackState.mNumAuthRejected);
assertEquals(callbackState.toString(), 0, callbackState.mNumAuthAccepted);
assertEquals(callbackState.toString(), 0, callbackState.mAcquiredReceived.size());
assertEquals(callbackState.toString(), 1, callbackState.mErrorsReceived.size());
checkErrCode(callbackState.toString(), (int) callbackState.mErrorsReceived.get(0),
BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED, hasMultiSensors);
}
}
}
/**
* Trying to upgrade the strength of a Weak biometric to a stronger strength will not
* succeed (ie, it's no-op and the biometric strength is still Weak), since the biometric's
* actual strength can't go past its original strength. After upgrading, the biometric without
* sufficient strength should not be able to perform the requested auth. For example,
* +-------------------+------------------+--------------------+--------+
* | Original Strength | Target Strength | Requested Strength | Result |
* +-------------------+------------------+--------------------+--------+
* | BIOMETRIC_WEAK | BIOMETRIC_STRONG | BIOMETRIC_STRONG | Error |
* +-------------------+------------------+--------------------+--------+
*/
@Test
public void testBiometricStrengthUpgraded_WeakSensor() throws Exception {
assumeTrue(Utils.isFirstApiLevel29orGreater());
final List<Integer> sensors = getSensorsOfTargetStrength(SensorProperties.STRENGTH_WEAK);
assumeTrue("testBiometricStrengthUpgraded_WeakSensor: numSensors: " + sensors.size(),
sensors.size() > 0);
// Tuple of originalStrength, targetStrength, and requestedStrength
final int[][] testCases = {
// Upgrade Weak to Strong, and request Strong auth
{Authenticators.BIOMETRIC_WEAK, Authenticators.BIOMETRIC_STRONG,
Authenticators.BIOMETRIC_STRONG}
};
for (Integer sensorId : sensors) {
for (int i = 0; i < testCases.length; i++) {
testBiometricStrengthUpgraded_forSensor(sensorId,
testCases[i][0] /* originalStrength */,
testCases[i][1] /* targetStrength */,
testCases[i][2] /* requestedStrength */,
mSensorProperties.size() > 1 /* hasMultiSensors */);
}
}
}
/**
* Trying to upgrade the strength of a Convenience biometric to a stronger strength will not
* succeed (ie, it's no-op and the biometric strength is still Convenience), since the
* biometric's actual strength can't go past its original strength. After upgrading, the
* biometric without sufficient strength should not be able to perform the requested auth.
* For example,
* +-----------------------+------------------+--------------------+--------+
* | Original Strength | Target Strength | Requested Strength | Result |
* +-----------------------+------------------+--------------------+--------+
* | BIOMETRIC_CONVENIENCE | BIOMETRIC_STRONG | BIOMETRIC_STRONG | Error |
* +-----------------------+------------------+--------------------+--------+
* | BIOMETRIC_CONVENIENCE | BIOMETRIC_STRONG | BIOMETRIC_WEAK | Error |
* +-----------------------+------------------+--------------------+--------+
* | BIOMETRIC_CONVENIENCE | BIOMETRIC_WEAK | BIOMETRIC_WEAK | Error |
* +-----------------------+------------------+--------------------+--------+
*/
@Test
public void testBiometricStrengthUpgraded_ConvenienceSensor() throws Exception {
assumeTrue(Utils.isFirstApiLevel29orGreater());
final List<Integer> sensors =
getSensorsOfTargetStrength(SensorProperties.STRENGTH_CONVENIENCE);
assumeTrue("testBiometricStrengthUpgraded_ConvenienceSensor: numSensors=" + sensors.size(),
sensors.size() > 0);
// Tuple of originalStrength, targetStrength, and requestedStrength
final int[][] testCases = {
// Upgrade Convenience to Strong, and request Strong auth
{Authenticators.BIOMETRIC_CONVENIENCE, Authenticators.BIOMETRIC_STRONG,
Authenticators.BIOMETRIC_STRONG},
// Upgrade Convenience to Strong, and request Weak auth
{Authenticators.BIOMETRIC_CONVENIENCE, Authenticators.BIOMETRIC_STRONG,
Authenticators.BIOMETRIC_WEAK},
// Upgrade Convenience to Weak, and request Weak auth
{Authenticators.BIOMETRIC_CONVENIENCE, Authenticators.BIOMETRIC_WEAK,
Authenticators.BIOMETRIC_WEAK}
};
for (Integer sensorId : sensors) {
for (int i = 0; i < testCases.length; i++) {
testBiometricStrengthUpgraded_forSensor(sensorId,
testCases[i][0] /* originalStrength */,
testCases[i][1] /* targetStrength */,
testCases[i][2] /* requestedStrength */,
mSensorProperties.size() > 1 /* hasMultiSensors */);
}
}
}
private void testBiometricStrengthUpgraded_forSensor(int sensorId, int originalStrength,
int targetStrength, int requestedStrength, boolean hasMultiSensors) throws Exception {
Log.d(TAG, "testBiometricStrengthUpgraded_forSensor: "
+ "sensorId=" + sensorId
+ ", originalStrength=" + originalStrength
+ ", targetStrength=" + targetStrength
+ ", requestedStrength=" + requestedStrength
+ ", hasMultiSensors=" + hasMultiSensors);
final ComponentName componentName = getComponentName(requestedStrength);
// Reset to the original strength in case it's ever changed before the test
updateStrengthAndVerify(sensorId, originalStrength);
try (BiometricTestSession session = mBiometricManager.createTestSession(sensorId);
ActivitySession activitySession = new ActivitySession(this, componentName)) {
waitForAllUnenrolled();
enrollForSensor(session, sensorId);
final TestJournal journal =
TestJournalContainer.get(activitySession.getComponentName());
// Try to upgrade the biometric strength to the target strength. The upgrading operation
// is no-op since the biometric can't be upgraded past its original strength.
updateStrengthAndIdle(sensorId, targetStrength);
final int currentStrength = getCurrentStrength(sensorId);
assertTrue("currentStrength: " + currentStrength, currentStrength == originalStrength);
// After upgrading, check whether auth works
// Error code should be returned
int errCode = mBiometricManager.canAuthenticate(requestedStrength);
checkErrCode("Device shouldn't allow auth with biometrics without sufficient strength."
+ " errCode: " + errCode,
errCode, BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE, hasMultiSensors);
// Launch test activity
launchActivityAndWaitForResumed(activitySession);
// Auth shouldn't work and error code should be returned
mInstrumentation.waitForIdleSync();
BiometricCallbackHelper.State callbackState = getCallbackState(journal);
assertNotNull(callbackState);
assertEquals(callbackState.toString(), 0, callbackState.mNumAuthRejected);
assertEquals(callbackState.toString(), 0, callbackState.mNumAuthAccepted);
assertEquals(callbackState.toString(), 0, callbackState.mAcquiredReceived.size());
assertEquals(callbackState.toString(), 1, callbackState.mErrorsReceived.size());
checkErrCode(callbackState.toString(), (int) callbackState.mErrorsReceived.get(0),
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE, hasMultiSensors);
}
}
private void checkErrCode(String msg, int errCode, int expectedErrCode,
boolean hasMultiSensors) {
if (!hasMultiSensors) {
assertTrue(msg, errCode == expectedErrCode);
} else {
// In the multi-sensor case, error code for the first ineligible sensor may be
// returned so the following error codes are accepted
assertTrue(msg, errCode == expectedErrCode
|| errCode == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
|| errCode == BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE);
}
}
private static ComponentName getComponentName(int requestedStrength) {
assertTrue("requestedStrength: " + requestedStrength,
requestedStrength == Authenticators.BIOMETRIC_STRONG ||
requestedStrength == Authenticators.BIOMETRIC_WEAK);
if (requestedStrength == Authenticators.BIOMETRIC_STRONG) {
return CLASS_3_BIOMETRIC_ACTIVITY;
}
return CLASS_2_BIOMETRIC_ACTIVITY;
}
private static void revertSensorStrengths() throws Exception {
Log.d(TAG, "revertSensorStrengths");
Utils.executeShellCommand("device_config delete " +
DEVICE_CONFIG_NAMESPACE + " " +
DEVICE_CONFIG_BIO_STRENGTH_KEY);
// this is probably not needed, but there are not too many tests so pause to ensure
// the settings have settled
Thread.sleep(1000);
}
private void updateStrengthAndVerify(int sensorId, int targetStrength) throws Exception {
updateSensorStrength(sensorId, targetStrength, /* verify */ true);
}
private void updateStrengthAndIdle(int sensorId, int targetStrength) throws Exception {
updateSensorStrength(sensorId, targetStrength, /* verify */ false);
}
private void updateSensorStrength(int sensorId, int targetStrength, boolean verify)
throws Exception {
Log.d(TAG, "updateStrength: update sensorId=" + sensorId + " to targetStrength="
+ targetStrength);
Utils.executeShellCommand("device_config put " +
DEVICE_CONFIG_NAMESPACE + " " +
DEVICE_CONFIG_BIO_STRENGTH_KEY + " " +
String.format("%s:%s", sensorId, targetStrength));
final boolean matchesTarget = waitForSensorToBecomeStrength(sensorId, targetStrength);
if (verify && !matchesTarget) {
fail("Timed out waiting for sensorId " + sensorId + " to become target strength: "
+ targetStrength);
}
}
private boolean waitForSensorToBecomeStrength(int sensorId, int targetStrength)
throws Exception {
for (int i = 0; i < 20; i++) {
final int currentStrength = getCurrentStrength(sensorId);
if (currentStrength == targetStrength) {
return true;
}
Log.d(TAG, "Not at target strength yet, current: " + currentStrength);
Thread.sleep(300);
}
return false;
}
}