Skip to content

Commit

Permalink
Support other result in canAuthenticate function
Browse files Browse the repository at this point in the history
  • Loading branch information
charlesng authored and hoisie committed Sep 24, 2022
1 parent 6507fc6 commit 72cabf2
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 24 deletions.
@@ -1,6 +1,12 @@
package org.robolectric.shadows;

import static android.hardware.biometrics.BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
import static android.hardware.biometrics.BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED;
import static android.hardware.biometrics.BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE;
import static android.hardware.biometrics.BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED;
import static android.hardware.biometrics.BiometricManager.BIOMETRIC_SUCCESS;
import static android.os.Build.VERSION_CODES.Q;
import static android.os.Build.VERSION_CODES.R;
import static com.google.common.truth.Truth.assertThat;

import android.hardware.biometrics.BiometricManager;
Expand All @@ -21,22 +27,78 @@ public class ShadowBiometricManagerTest {
@Before
public void setUp() {
biometricManager =
ApplicationProvider.getApplicationContext().getSystemService(BiometricManager.class);
ApplicationProvider.getApplicationContext().getSystemService(BiometricManager.class);
assertThat(biometricManager).isNotNull();
}

@Test
public void testCanAuthenticate_serviceNotConnected_canNotAuthenticate() {
((ShadowBiometricManager) Shadow.extract(biometricManager)).setCanAuthenticate(false);
ShadowBiometricManager shadowBiometricManager = Shadow.extract(biometricManager);
shadowBiometricManager.setCanAuthenticate(false);

assertThat(biometricManager.canAuthenticate())
.isEqualTo(BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE);
assertThat(biometricManager.canAuthenticate()).isEqualTo(BIOMETRIC_ERROR_NO_HARDWARE);
}

@Test
public void testCanAuthenticate_serviceConnected_canAuthenticate() {
((ShadowBiometricManager) Shadow.extract(biometricManager)).setCanAuthenticate(true);
ShadowBiometricManager shadowBiometricManager = Shadow.extract(biometricManager);
shadowBiometricManager.setCanAuthenticate(true);

assertThat(biometricManager.canAuthenticate()).isEqualTo(BIOMETRIC_SUCCESS);
}

@Test
public void testCanAuthenticate_serviceNotConnected_noEnrolledBiometric_biometricNotEnrolled() {
ShadowBiometricManager shadowBiometricManager = Shadow.extract(biometricManager);
shadowBiometricManager.setCanAuthenticate(false);
shadowBiometricManager.setAuthenticatorType(BIOMETRIC_ERROR_NONE_ENROLLED);

assertThat(biometricManager.canAuthenticate()).isEqualTo(BIOMETRIC_ERROR_NONE_ENROLLED);
}

@Test
public void testCanAuthenticate_serviceNotConnected_noHardware_biometricHwUnavailable() {
ShadowBiometricManager shadowBiometricManager = Shadow.extract(biometricManager);
shadowBiometricManager.setCanAuthenticate(false);
shadowBiometricManager.setAuthenticatorType(BIOMETRIC_ERROR_HW_UNAVAILABLE);

assertThat(biometricManager.canAuthenticate()).isEqualTo(BIOMETRIC_ERROR_HW_UNAVAILABLE);
}

@Test
@Config(minSdk = R)
public void
testCanAuthenticate_serviceNotConnected_securityUpdateRequired_biometricErrorSecurityUpdateRequired() {
ShadowBiometricManager shadowBiometricManager = Shadow.extract(biometricManager);
shadowBiometricManager.setCanAuthenticate(false);
shadowBiometricManager.setAuthenticatorType(BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED);

assertThat(biometricManager.canAuthenticate())
.isEqualTo(BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED);
}

@Test
@Config(minSdk = R)
public void
testCanAuthenticateBiometricWeak_serviceConnected_noWeakButHaveStrongEntrolled_canAuthenticate() {
ShadowBiometricManager shadowBiometricManager = Shadow.extract(biometricManager);
shadowBiometricManager.setCanAuthenticate(true);
shadowBiometricManager.setAuthenticatorType(BiometricManager.Authenticators.BIOMETRIC_STRONG);

assertThat(biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK))
.isEqualTo(BIOMETRIC_SUCCESS);
}

@Test
@Config(minSdk = R)
public void testCanAuthenticateBiometricWeakDeviceCredential_serviceConnected_canAuthenticate() {
final int authenticators =
BiometricManager.Authenticators.BIOMETRIC_WEAK
| BiometricManager.Authenticators.DEVICE_CREDENTIAL;
ShadowBiometricManager shadowBiometricManager = Shadow.extract(biometricManager);
shadowBiometricManager.setCanAuthenticate(true);
shadowBiometricManager.setAuthenticatorType(authenticators);

assertThat(biometricManager.canAuthenticate()).isEqualTo(BiometricManager.BIOMETRIC_SUCCESS);
assertThat(biometricManager.canAuthenticate(authenticators)).isEqualTo(BIOMETRIC_SUCCESS);
}
}
@@ -1,9 +1,6 @@
package org.robolectric.shadows;

import static android.Manifest.permission.USE_BIOMETRIC;
import static android.hardware.biometrics.BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE;
import static android.hardware.biometrics.BiometricManager.BIOMETRIC_SUCCESS;
import static android.hardware.biometrics.BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE;
import static android.os.Build.VERSION_CODES.Q;
import static android.os.Build.VERSION_CODES.R;
import static org.robolectric.util.reflector.Reflector.reflector;
Expand All @@ -16,7 +13,6 @@
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
import org.robolectric.util.ReflectionHelpers;
import org.robolectric.util.ReflectionHelpers.ClassParameter;
import org.robolectric.util.reflector.Direct;
import org.robolectric.util.reflector.ForType;

Expand All @@ -28,6 +24,7 @@
public class ShadowBiometricManager {

protected boolean biometricServiceConnected = true;
private int authenticatorType = BiometricManager.Authenticators.EMPTY_SET;

@RealObject private BiometricManager realBiometricManager;

Expand All @@ -37,23 +34,60 @@ public class ShadowBiometricManager {
protected int canAuthenticate() {
if (RuntimeEnvironment.getApiLevel() >= R) {
return reflector(BiometricManagerReflector.class, realBiometricManager).canAuthenticate();
} else if (biometricServiceConnected) {
return BIOMETRIC_SUCCESS;
} else {
boolean hasBiomatrics =
ReflectionHelpers.callStaticMethod(
BiometricManager.class,
"hasBiometrics",
ClassParameter.from(
Context.class, RuntimeEnvironment.getApplication().getApplicationContext()));
if (!hasBiomatrics) {
return BIOMETRIC_ERROR_NO_HARDWARE;
int biometricResult =
canAuthenticateInternal(0, BiometricManager.Authenticators.BIOMETRIC_WEAK);
if (biometricServiceConnected) {
return BiometricManager.BIOMETRIC_SUCCESS;
} else if (biometricResult != BiometricManager.BIOMETRIC_SUCCESS) {
return biometricResult;
} else {
return BIOMETRIC_ERROR_HW_UNAVAILABLE;
boolean hasBiometrics =
ReflectionHelpers.callStaticMethod(
BiometricManager.class,
"hasBiometrics",
ReflectionHelpers.ClassParameter.from(
Context.class, RuntimeEnvironment.getApplication().getApplicationContext()));
if (!hasBiometrics) {
return BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE;
} else {
return BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE;
}
}
}
}

@RequiresPermission(USE_BIOMETRIC)
@Implementation(minSdk = R)
protected int canAuthenticate(int authenticators) {
return canAuthenticateInternal(0, authenticators);
}

@RequiresPermission(USE_BIOMETRIC)
@Implementation(minSdk = R)
protected int canAuthenticate(int userId, int authenticators) {
return canAuthenticateInternal(userId, authenticators);
}

private int canAuthenticateInternal(int userId, int authenticators) {
if (authenticatorType == BiometricManager.Authenticators.BIOMETRIC_STRONG
&& biometricServiceConnected) {
return BiometricManager.BIOMETRIC_SUCCESS;
}
if ((authenticatorType & BiometricManager.Authenticators.DEVICE_CREDENTIAL)
== BiometricManager.Authenticators.DEVICE_CREDENTIAL) {
return BiometricManager.BIOMETRIC_SUCCESS;
}
if (authenticatorType != BiometricManager.Authenticators.EMPTY_SET) {
return authenticatorType;
}
if (!biometricServiceConnected) {
return BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE;
} else {
return BiometricManager.BIOMETRIC_SUCCESS;
}
}

/**
* Sets the value {@code true} to allow {@link #canAuthenticate()} return {@link
* BIOMETRIC_SUCCESS} If sets the value to {@code false}, result will depend on {@link
Expand All @@ -65,9 +99,21 @@ public void setCanAuthenticate(boolean flag) {
biometricServiceConnected = flag;
}

@Implementation(minSdk = R)
protected int canAuthenticate(int userId, int authenticators) {
return biometricServiceConnected ? BIOMETRIC_SUCCESS : BIOMETRIC_ERROR_NO_HARDWARE;
/**
* Allow different result {@link #canAuthenticate(int)}, result will depend on the combination as
* described <a
* href="https://developer.android.com/reference/android/hardware/biometrics/BiometricManager#canAuthenticate(int)">here</a>
* For example, you can set the value {@code BiometricManager.Authenticators.BIOMETRIC_STRONG} to
* allow {@link #canAuthenticate(int)} return {@link BiometricManager#BIOMETRIC_SUCCESS} when you
* passed {@code BiometricManager.Authenticators.BIOMETRIC_WEAK} as parameter in {@link
* #canAuthenticate(int)}
*
* @param type to set the authenticatorType
* @see <a
* href="https://developer.android.com/reference/android/hardware/biometrics/BiometricManager#canAuthenticate(int)"
*/
public void setAuthenticatorType(int type) {
authenticatorType = type;
}

@ForType(BiometricManager.class)
Expand Down

0 comments on commit 72cabf2

Please sign in to comment.