1. 测试基本信息描述
测试模块: CtsHardwareTestCases
测试case: android.hardware.fingerprint.cts.FingerprintManagerTest#test_hasFingerprintHardware
该问题CTS 测试pass,刷GSI 后测试fail。
报错信息为:
less
03-06 11:17:09 I/ModuleListener: [1/1] e87aea03 android.hardware.cts android.hardware.fingerprint.cts.FingerprintManagerTest#test_hasFingerprintHardware FAILURE: junit.framework.AssertionFailedError
at junit.framework.Assert.fail(Assert.java:48)
at junit.framework.Assert.assertTrue(Assert.java:20)
at junit.framework.Assert.assertTrue(Assert.java:27)
at android.hardware.fingerprint.cts.FingerprintManagerTest.test_hasFingerprintHardware(FingerprintManagerTest.java:86)
2. 问题分析
2.1 错误信息分析
- 根据测试用例可知是指纹解锁相关的case
- 根据测试报告的fail信息可知,在FingerprintManagerTest.java文件的第86执行test_hasFingerprintHardware方法时断言fail,导致测试fail
2.2 源码分析
scss
# FingerprintManagerTest.java
// 指纹服务管理类
private FingerprintManager mFingerprintManager;
// 是否支持指纹解释
boolean mHasFingerprintManager;
@Override
public void setUp() throws Exception {
super.setUp();
mAuthState = AuthState.AUTH_UNKNOWN;
PackageManager pm = getContext().getPackageManager();
// FEATURE_FINGERPRINT = "android.hardware.fingerprint"
if (pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
mHasFingerprintManager = true;
// 如果支持指纹解释,则拿到对应的服务
mFingerprintManager = (FingerprintManager)
getContext().getSystemService(Context.FINGERPRINT_SERVICE);
}
}
@Presubmit
public void test_hasFingerprintHardware() {
if (!mHasFingerprintManager) {
return; // skip test if no fingerprint feature
}
// 报错行
assertTrue(mFingerprintManager.isHardwareDetected());
}
-
- 报错行在 assertTrue 这句断言,需要 isHardwareDetected() 返回true ,但是实际上返回了false,导致fail
-
- 能执行到这一句的前提是 mHasFingerprintManager 为true,如果其值为false表示当前设备不支持指纹解释功能,当前case就会跳过
-
- 但是当前报错的是GSI,并且在CTS测试中该case没有跳过,说明当前设备是需要支持指纹解锁的, 从设置硬件设计也可以确定。 另外可以通过以下命令来获取feature的支持
arduinoadb shell "pm list features | grep -c "android.hardware.fingerprint""
也就是说当前case fail的原因是支持了指纹解锁的feature,但是isHardwareDetected()返回了false。 那么当前需要分析的点就是:为什么isHardwareDetected()返回了false ?
2.2.1 返回false的逻辑跟踪
java
# FingerprintManager.java
// 指纹服务 (FingerprintService)
private IFingerprintService mService;
/**
* Determine if fingerprint hardware is present and functional.
* 确定指纹硬件是否存在并正常工作。
* @return true if hardware is present and functional, false otherwise.
* 如果硬件存在并正常工作,则返回true,否则返回false
* @deprecated See {@link BiometricPrompt} and
* {@link FingerprintManager#FINGERPRINT_ERROR_HW_UNAVAILABLE}
*/
@Deprecated
@RequiresPermission(USE_FINGERPRINT)
public boolean isHardwareDetected() {
......
if (mService != null) {
try {
return mService.isHardwareDetectedDeprecated(
mContext.getOpPackageName(), mContext.getAttributionTag());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
} else {
Slog.w(TAG, "isFingerprintHardwareDetected(): Service not connected!");
}
return false;
}
-
- 根据注释,和fail的原因,知道当前返回 false, 也就说明硬件不存在,或者没有正常工作。硬件肯定是存在的,那fail的原因就是没有正常工作
-
- 返回有2种可能,一种是 mService为null, 一种是 isHardwareDetectedDeprecated方法返回false 如果是 mService 为null 返回false的话, 会有"isFingerprintHardwareDetected(): Service not connected!" 的log打印,但是实际上并没有这段log的打印。
所以是FingerprintService::isHardwareDetectedDeprecated方法返回了false导致测试的fail
java
# FingerprintService.java
@Override // Binder call
public boolean isHardwareDetectedDeprecated(String opPackageName, String attributionTag) {
if (!canUseFingerprint(
opPackageName,
attributionTag,
false /* foregroundOnly */,
Binder.getCallingUid(),
Binder.getCallingPid(),
UserHandle.getCallingUserId())) {
return false;
}
final long token = Binder.clearCallingIdentity();
try {
final Pair<Integer, ServiceProvider> provider = getSingleProvider();
if (provider == null) {
Slog.w(TAG, "Null provider for isHardwareDetectedDeprecated, caller: "
+ opPackageName);
// 这里返回的false
return false;
}
return provider.second.isHardwareDetected(provider.first);
} finally {
Binder.restoreCallingIdentity(token);
}
}
这里有2个return的地方, 但是第二个返回false的地方有log打印。 在测试log中可以看到如下日志, 说明返回false的地方是 provider 这个变量为null
yaml
03:39:05.206 1000 2206 6756 E FingerprintService: No providers found
03:39:05.207 1000 2206 6756 W FingerprintService: Null provider for isHardwareDetectedDeprecated, caller: android.hardware.cts
csharp
# FingerprintService.java
@Nullable
private Pair<Integer, ServiceProvider> getSingleProvider() {
final List<FingerprintSensorPropertiesInternal> properties = getSensorProperties();
if (properties.isEmpty()) {
// 打印log, 返回null
Slog.e(TAG, "No providers found");
return null;
}
......
}
可以看到这里打印的log,和返回null。
下一步看getSensorProperties 方法为什么返回null
java
# FingerprintService.java
// 列表
@NonNull private final List<FingerprintSensorPropertiesInternal> mSensorProps;
@NonNull
private List<FingerprintSensorPropertiesInternal> getSensorProperties() {
synchronized (mLock) {
return mSensorProps;
}
}
现在的问题变为: 为什么 mSensorProps 在刷了GSI 后会为null,搜到mSensorProps数据的添加在 FingerprintService::registerAuthenticators中。
scss
# FingerprintService.java
@NonNull private final List<FingerprintSensorPropertiesInternal> mSensorProps;
@Override // Binder call
public void registerAuthenticators(
@NonNull List<FingerprintSensorPropertiesInternal> hidlSensors) {
......
handler.post(() -> {
addHidlProviders(hidlSensors);
addAidlProviders();
......
synchronized (mLock) {
for (ServiceProvider provider : mServiceProviders) {
// 遍历mServiceProviders往mSensorProps添加数据
mSensorProps.addAll(provider.getSensorProperties());
}
}
......
broadcastAllAuthenticatorsRegistered(); // 这里会打印 FingerprintService: mSensorProps is empty, 也能说明确实是 mSensorProps没数据
});
}
private void addHidlProviders(List<FingerprintSensorPropertiesInternal> hidlSensors) {
for (FingerprintSensorPropertiesInternal hidlSensor : hidlSensors) {
final Fingerprint21 fingerprint21;
if ((Build.IS_USERDEBUG || Build.IS_ENG)
&& getContext().getResources().getBoolean(R.bool.allow_test_udfps)
&& Settings.Secure.getIntForUser(getContext().getContentResolver(),
Fingerprint21UdfpsMock.CONFIG_ENABLE_TEST_UDFPS, 0 /* default */,
UserHandle.USER_CURRENT) != 0) {
fingerprint21 = Fingerprint21UdfpsMock.newInstance(getContext(),
mBiometricStateCallback, hidlSensor,
mLockoutResetDispatcher, mGestureAvailabilityDispatcher,
BiometricContext.getInstance(getContext()));
} else {
fingerprint21 = Fingerprint21.newInstance(getContext(),
mBiometricStateCallback, hidlSensor, mHandler,
mLockoutResetDispatcher, mGestureAvailabilityDispatcher);
}
// 对mServiceProviders添加数据
mServiceProviders.add(fingerprint21);
}
}
-
- 分析的 mSensorProps 数据添加是开机就执行了的流程,不是测试中
-
- mSensorProps 的数据添加依赖 mServiceProviders变量
-
- mServiceProviders 的数据添加在 FingerprintService::addHidlProviders
-
- FingerprintService::addHidlProviders 又是在 FingerprintService:: registerAuthenticators 中
所以现在需要关注 FingerprintService:: registerAuthenticators 方法调用传递过来的参数为什么为null?
这个方法是public的,调用在AuthService中
2.2.2 开机过程指纹模块的处理
java
# AuthService.java
private void registerAuthenticators(@Nullable SensorConfig[] hidlSensors) {
List<FingerprintSensorPropertiesInternal> hidlFingerprintSensors = new ArrayList<>();
List<FaceSensorPropertiesInternal> hidlFaceSensors = new ArrayList<>();
// Iris doesn't have IrisSensorPropertiesInternal, using SensorPropertiesInternal instead.
List<SensorPropertiesInternal> hidlIrisSensors = new ArrayList<>();
if (hidlSensors != null) {
for (SensorConfig sensor : hidlSensors) {
// 打印log
Slog.d(TAG, "Registering HIDL ID: " + sensor.id + " Modality: " + sensor.modality
+ " Strength: " + sensor.strength);
switch (sensor.modality) {
case TYPE_FINGERPRINT:
// 处理数据
hidlFingerprintSensors.add(
getHidlFingerprintSensorProps(sensor.id, sensor.strength));
break;
......
}
}
}
final IFingerprintService fingerprintService = mInjector.getFingerprintService();
if (fingerprintService != null) {
try {
// 执行到上面的registerAuthenticators方法,传递hidlFingerprintSensors
fingerprintService.registerAuthenticators(hidlFingerprintSensors);
} catch (RemoteException e) {
Slog.e(TAG, "RemoteException when registering fingerprint authenticators", e);
}
} ......
}
如果参数 hidlSensors不为空,就会执行这里的逻辑,并且会打印log。 不刷GSI的设备抓取开机log是能看到这里的 "Registering HIDL ID: XX" 的日志, 刷了GSI的却没有。 说明参数传过来的hidlSensors就为null了。
registerAuthenticators方法的调用在 AuthService::onStart中。
ini
# AuthService.java
@Override
public void onStart() {
mBiometricService = mInjector.getBiometricService();
// 重点对象,存储着指纹相关的配置
final SensorConfig[] hidlConfigs;
// 确认过,这里返回true
if (!mInjector.isHidlDisabled(getContext())) {
final int firstApiLevel = SystemProperties.getInt(SYSPROP_FIRST_API_LEVEL, 0);
final int apiLevel = SystemProperties.getInt(SYSPROP_API_LEVEL, firstApiLevel);
// 关键,读取配置
String[] configStrings = mInjector.getConfiguration(getContext());
if (configStrings.length == 0 && apiLevel == Build.VERSION_CODES.R) {
......
Slog.w(TAG, "Found R vendor partition without config_biometric_sensors");
// 不走这, 没有上面的log,并且当前设备是T的 ,不是R
configStrings = generateRSdkCompatibleConfiguration();
}
// 走这
hidlConfigs = new SensorConfig[configStrings.length];
for (int i = 0; i < configStrings.length; ++i) {
hidlConfigs[i] = new SensorConfig(configStrings[i]);
}
} else {
// 不走这, 排除
hidlConfigs = null;
}
// Registers HIDL and AIDL authenticators, but only HIDL configs need to be provided.
// 关键函数
registerAuthenticators(hidlConfigs);
mInjector.publishBinderService(this, mImpl);
}
这里的关键在于末尾调用 registerAuthenticators方法传递是参数 hidlConfigs。 根据注释里的信息, 最终走的是下面这块。
ini
hidlConfigs = new SensorConfig[configStrings.length];
也就是说hidlConfigs的数据最终被configStrings影响。
这个 configStrings 的值是 Injector::getConfiguration 返回的。 这个Injector就是 AuthService 的内部类
typescript
# AuthService.java
public static class Injector {
...
@VisibleForTesting
public String[] getConfiguration(Context context) {
return context.getResources().getStringArray(R.array.config_biometric_sensors);
}
...
}
可以看到读取了配置config_biometric_sensors的值
经过确认,当前项目这个值在android/frameworks/base/core/res/res/values/config.xml下
xml
<!-- List of biometric sensors on the device, in decreasing strength. Consumed by AuthService
when registering authenticators with BiometricService. Format must be ID:Modality:Strength,
where: IDs are unique per device, Modality as defined in BiometricAuthenticator.java,
and Strength as defined in Authenticators.java -->
<string-array name="config_biometric_sensors" translatable="false" >
<item>0:2:15</item>
</string-array>
根据注释,这里是指纹传感器的强度,这3个值也是定义在代码里的。
也就是说当前问题是因为刷了system.img后没有这个值导致的fail。 在普通版本和GSI 版本分别执行下面命令获取这个配置也能确认
sql
adb shell cmd overlay lookup android android:array/config_biometric_sensors
2.3 正常和GSI版本开机log对比,以及调用链整理
正常开机log:
yaml
Line 12436: 02-06 01:40:17.849 2214 2214 D SystemServerTiming: StartAuthService
Line 12437: 02-06 01:40:17.849 2214 2214 I SystemServiceManager: Starting com.android.server.biometrics.AuthService
Line 12444: 02-06 01:40:17.851 2214 2214 D AuthService: Registering HIDL ID: 0 Modality: 2 Strength: 15
fail版本GSI的开机log:
yaml
01-09 13:59:46.272 2104 2104 D SystemServerTiming: StartAuthService
Line 13495: 01-09 13:59:46.276 2104 2520 E FingerprintService: mSensorProps is empty
调用链如下:
arduino
FingerprintManagerTest::test_hasFingerprintHardware
mHasFingerprintManager -- 是否支持指纹解锁feature, 不支持则 skip
FingerprintManager::isHardwareDetected -- 返回false导致测试fail
FingerprintService::isHardwareDetectedDeprecated
FingerprintService::getSingleProvider
FingerprintService::getSensorProperties
mSensorProps 数据的添加,这些流程在开机的时候执行
AuthService::onStart
AuthService::registerAuthenticators
FingerprintService::registerAuthenticators
FingerprintService::addHidlProviders
3. 解决方案
知道是 config_biometric_sensors 这个配置导致的,那就是要在vender下配置这个值,使刷完GSI后还能有这个值就好了。