【谷歌G认证-XTS问题整理】[GSI] CtsHardwareTestCases - test_hasFingerprintHardware 指纹相关fail

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 错误信息分析

  1. 根据测试用例可知是指纹解锁相关的case
  2. 根据测试报告的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());
    }
    1. 报错行在 assertTrue 这句断言,需要 isHardwareDetected() 返回true ,但是实际上返回了false,导致fail
    1. 能执行到这一句的前提是 mHasFingerprintManager 为true,如果其值为false表示当前设备不支持指纹解释功能,当前case就会跳过
    1. 但是当前报错的是GSI,并且在CTS测试中该case没有跳过,说明当前设备是需要支持指纹解锁的, 从设置硬件设计也可以确定。 另外可以通过以下命令来获取feature的支持
    arduino 复制代码
    adb  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;
    }
    1. 根据注释,和fail的原因,知道当前返回 false, 也就说明硬件不存在,或者没有正常工作。硬件肯定是存在的,那fail的原因就是没有正常工作
    1. 返回有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);
            }
        }
    1. 分析的 mSensorProps 数据添加是开机就执行了的流程,不是测试中
    1. mSensorProps 的数据添加依赖 mServiceProviders变量
    1. mServiceProviders 的数据添加在 FingerprintService::addHidlProviders
    1. 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后还能有这个值就好了。

相关推荐
ChinaDragonDreamer2 小时前
Kotlin:2.0.20 的新特性
android·开发语言·kotlin
网络研究院4 小时前
Android 安卓内存安全漏洞数量大幅下降的原因
android·安全·编程·安卓·内存·漏洞·技术
凉亭下4 小时前
android navigation 用法详细使用
android
小比卡丘7 小时前
C语言进阶版第17课—自定义类型:联合和枚举
android·java·c语言
前行的小黑炭8 小时前
一篇搞定Android 实现扫码支付:如何对接海外的第三方支付;项目中的真实经验分享;如何高效对接,高效开发
android
落落落sss9 小时前
MybatisPlus
android·java·开发语言·spring·tomcat·rabbitmq·mybatis
代码敲上天.10 小时前
数据库语句优化
android·数据库·adb
GEEKVIP12 小时前
手机使用技巧:8 个 Android 锁屏移除工具 [解锁 Android]
android·macos·ios·智能手机·电脑·手机·iphone
model200514 小时前
android + tflite 分类APP开发-2
android·分类·tflite
彭于晏68914 小时前
Android广播
android·java·开发语言