1 package org.robolectric.shadows; 2 3 import static android.Manifest.permission.USE_BIOMETRIC; 4 import static android.os.Build.VERSION_CODES.Q; 5 import static android.os.Build.VERSION_CODES.R; 6 import static org.robolectric.util.reflector.Reflector.reflector; 7 8 import android.annotation.RequiresPermission; 9 import android.content.Context; 10 import android.hardware.biometrics.BiometricManager; 11 import org.robolectric.RuntimeEnvironment; 12 import org.robolectric.annotation.Implementation; 13 import org.robolectric.annotation.Implements; 14 import org.robolectric.annotation.RealObject; 15 import org.robolectric.annotation.Resetter; 16 import org.robolectric.util.ReflectionHelpers; 17 import org.robolectric.util.reflector.Direct; 18 import org.robolectric.util.reflector.ForType; 19 20 /** Provides testing APIs for {@link BiometricManager} */ 21 @Implements( 22 className = "android.hardware.biometrics.BiometricManager", 23 minSdk = Q, 24 isInAndroidSdk = false) 25 public class ShadowBiometricManager { 26 27 protected static boolean biometricServiceConnected = true; 28 private static int authenticatorType = BiometricManager.Authenticators.EMPTY_SET; 29 30 @RealObject private BiometricManager realBiometricManager; 31 32 @Resetter reset()33 public static void reset() { 34 biometricServiceConnected = true; 35 authenticatorType = BiometricManager.Authenticators.EMPTY_SET; 36 } 37 38 @SuppressWarnings("deprecation") 39 @RequiresPermission(USE_BIOMETRIC) 40 @Implementation canAuthenticate()41 protected int canAuthenticate() { 42 if (RuntimeEnvironment.getApiLevel() >= R) { 43 return reflector(BiometricManagerReflector.class, realBiometricManager).canAuthenticate(); 44 } else { 45 int biometricResult = 46 canAuthenticateInternal(0, BiometricManager.Authenticators.BIOMETRIC_WEAK); 47 if (biometricServiceConnected) { 48 return BiometricManager.BIOMETRIC_SUCCESS; 49 } else if (biometricResult != BiometricManager.BIOMETRIC_SUCCESS) { 50 return biometricResult; 51 } else { 52 boolean hasBiometrics = 53 ReflectionHelpers.callStaticMethod( 54 BiometricManager.class, 55 "hasBiometrics", 56 ReflectionHelpers.ClassParameter.from( 57 Context.class, RuntimeEnvironment.getApplication().getApplicationContext())); 58 if (!hasBiometrics) { 59 return BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE; 60 } else { 61 return BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE; 62 } 63 } 64 } 65 } 66 67 @RequiresPermission(USE_BIOMETRIC) 68 @Implementation(minSdk = R) canAuthenticate(int authenticators)69 protected int canAuthenticate(int authenticators) { 70 return canAuthenticateInternal(0, authenticators); 71 } 72 73 @RequiresPermission(USE_BIOMETRIC) 74 @Implementation(minSdk = R) canAuthenticate(int userId, int authenticators)75 protected int canAuthenticate(int userId, int authenticators) { 76 return canAuthenticateInternal(userId, authenticators); 77 } 78 canAuthenticateInternal(int userId, int authenticators)79 private int canAuthenticateInternal(int userId, int authenticators) { 80 if (authenticatorType == BiometricManager.Authenticators.BIOMETRIC_STRONG 81 && biometricServiceConnected) { 82 return BiometricManager.BIOMETRIC_SUCCESS; 83 } 84 if ((authenticatorType & BiometricManager.Authenticators.DEVICE_CREDENTIAL) 85 == BiometricManager.Authenticators.DEVICE_CREDENTIAL) { 86 return BiometricManager.BIOMETRIC_SUCCESS; 87 } 88 if (authenticatorType != BiometricManager.Authenticators.EMPTY_SET) { 89 return authenticatorType; 90 } 91 if (!biometricServiceConnected) { 92 return BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE; 93 } else { 94 return BiometricManager.BIOMETRIC_SUCCESS; 95 } 96 } 97 98 /** 99 * Sets the value {@code true} to allow {@link #canAuthenticate()} return {@link 100 * BIOMETRIC_SUCCESS} If sets the value to {@code false}, result will depend on {@link 101 * BiometricManager#hasBiometrics(Context context)} 102 * 103 * @param flag to set can authenticate or not 104 */ setCanAuthenticate(boolean flag)105 public void setCanAuthenticate(boolean flag) { 106 biometricServiceConnected = flag; 107 } 108 109 /** 110 * Allow different result {@link #canAuthenticate(int)}, result will depend on the combination as 111 * described <a 112 * href="https://developer.android.com/reference/android/hardware/biometrics/BiometricManager#canAuthenticate(int)">here</a> 113 * For example, you can set the value {@code BiometricManager.Authenticators.BIOMETRIC_STRONG} to 114 * allow {@link #canAuthenticate(int)} return {@link BiometricManager#BIOMETRIC_SUCCESS} when you 115 * passed {@code BiometricManager.Authenticators.BIOMETRIC_WEAK} as parameter in {@link 116 * #canAuthenticate(int)} 117 * 118 * @param type to set the authenticatorType 119 * @see <a 120 * href="https://developer.android.com/reference/android/hardware/biometrics/BiometricManager#canAuthenticate(int)">BiometricManager#canAuthenticate(int)</a> 121 */ setAuthenticatorType(int type)122 public void setAuthenticatorType(int type) { 123 authenticatorType = type; 124 } 125 126 @ForType(BiometricManager.class) 127 interface BiometricManagerReflector { 128 129 @Direct canAuthenticate()130 int canAuthenticate(); 131 } 132 } 133