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