【谷歌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后还能有这个值就好了。

相关推荐
移动开发者1号几秒前
深入解析内存抖动:定位与修复实战(Kotlin版)
android·kotlin
whysqwhw2 分钟前
OkHttp深度架构缺陷分析与革命性演进方案
android
Digitally2 小时前
如何将文件从 iPhone 传输到 Android(新指南)
android·ios·iphone
whysqwhw3 小时前
OkHttp深度架构缺陷分析与演进规划
android
用户7093722538513 小时前
Android14 SystemUI NotificationShadeWindowView 加载显示过程
android
木叶丸4 小时前
跨平台方案该如何选择?
android·前端·ios
顾林海4 小时前
Android ClassLoader加载机制详解
android·面试·源码
用户2018792831674 小时前
🎨 童话:Android画布王国的奇妙冒险
android
whysqwhw5 小时前
OkHttp框架的全面深入架构分析
android
你过来啊你5 小时前
Android App冷启动流程详解
android