☰
Android R生物识别传感器通用配置:config_biometric_sensors的overlay定制与避坑指南
2026/10/16 5:21:58 网站建设 项目流程

最近有好几个做系统定制的朋友问了我同一个问题:Android R 上把config_biometric_sensors做成默认通用定制,放一套 common 公共配置,到底行不行?这个问题的背后其实是一类很常见的需求——多设备共用一套代码,想减少重复配置,但又怕通用配置把生物识别搞坏。我先给结论:可行,但能不能“通用”取决于你对 Android 资源覆盖机制、传感器字段含义和 BiometricService 消费逻辑的理解程度。这篇文章就把这套配置的里里外外拆开讲清楚,包含原理、实操和踩坑记录,适合 ROM 定制工程师、设备适配工程师,以及想搞懂 Android 生物识别框架的开发者。

1. 先看channel:config_biometric_sensors 在这台机器里到底管什么

1.1 默认配置长什么样、三段数字各代表什么意思

config_biometric_sensors定义在frameworks/base/core/res/res/values/config.xml,它是一个string-array,每一项的格式是固定的三段式:

<string-array name="config_biometric_sensors" translatable="false"> <item>0:1:15</item> </string-array>

三段中间用英文冒号分隔,含义分别是id : modality : strength。

  • 第一段id:给传感器编的唯一数字 ID,这个 ID 不暴露给上层 API,只用于框架内部区分传感器实例。它没有全局统一样式,完全跟着驱动和 HAL 的实现走。
  • 第二段modality:传感器类型。1 代表指纹,2 代表人脸,4 代表虹膜。
  • 第三段strength:生物识别强度。Android R 上常见有效值是 1、2、15,分别对应 Convenience、Weak、Strong。实际项目里最常写的是 15,代表强生物特征,能够参与密钥保护和锁屏强验证;写 1 或 2 的传感器只能做便捷确认,不能保护 Keyguard 密钥。

很多刚接触的人会把这三段数字当成随便填的参数,其实不是。它是 BiometricService 里唯一的“传感器声明来源”,上层所有关于“这台设备有哪些生物识别能力”的判断,都是从这一个数组里来的。数组里写了什么,系统就认为设备有什么,即使 HAL 侧根本没有对应的传感器。

1.2 BiometricService 是怎么消费这段配置的

在 Android R(API 30)上,BiometricService在系统服务启动时,会通过context.getResources().getStringArray(com.android.internal.R.array.config_biometric_sensors)把整个数组读出来,然后逐条解析,构造成内部的BiometricSensor对象列表。

解析过程大致是:

String[] config = context.getResources().getStringArray( com.android.internal.R.array.config_biometric_sensors); for (String entry : config) { String[] parts = entry.split(":"); int id = Integer.parseInt(parts[0]); int modality = Integer.parseInt(parts[1]); int strength = Integer.parseInt(parts[2]); // 构建 BiometricSensor 并加入列表 }

这个传感器列表会直接影响几件事:

  • 系统设置里“生物识别”相关入口是否显示,以及显示哪些类型;
  • BiometricPrompt判断是否还有可用的生物识别方式;
  • 锁屏界面选择用哪个传感器做解锁,以及解锁时的强度是否满足密钥保护要求;
  • 应用调用强生物特征认证时,框架能否把认证请求正确路由到对应 HAL。

也就是说,这个数组是“框架层能力地图”。地图画错了,上层功能再正常也会表现得乱七八糟。比如你在数组里写了指纹传感器,但实际设备只有人脸,系统设置里的“指纹”入口可能会显示,但点进去之后会报传感器错误,因为框架拿着指纹的 modality 去找 fingerprint HAL,而 HAL 侧并没有对应服务。

2. common通用定制会被哪些硬约束卡住

2.1 资源覆盖机制:数组整体替换,不支持逐项合并

想把这套配置做成 common,第一个要面对的硬约束就是 Android 资源覆盖机制。静态 overlay(比如PRODUCT_PACKAGE_OVERLAYS指向的目录)在做资源合并时,是按“资源项”进行覆盖的:同一个资源名,只要 overlay 里定义了,就整体替换原值;没有定义的不受影响。

问题来了:config_biometric_sensors是一个string-array,它整体就是一个资源项。你在 common overlay 里放了0:1:15,设备级别的 overlay 里如果只想多补一条人脸传感器,不能写“在原来基础上追加”,必须把整个数组重新写一遍:

<string-array name="config_biometric_sensors" translatable="false"> <item>0:1:15</item> <item>1:2:15</item> </string-array>

这个“整体替换”的规则,决定了 common 里的默认数组最好是产品线的最大公约数。如果 common 里直接定义了双传感器,而某台设备只有单传感器,设备 overlay 必须完整覆盖回单传感器;如果哪个设备忘了覆盖,就会出现“系统认为有人脸,实际没有”的假配置。

2.2 设备差异:id、modality、strength 没有可“通用”的标准

第二个卡点是传感器字段本身不具备跨设备的通用性。

id是跟着 HAL 和驱动走的。有的设备指纹 id 是 0,有的人脸 id 也是 0,有的传感器 id 从 1 开始。这个值不是你在 overlay 里随便编一个就能用的,它需要和 vendor 层 HAL 上报的 sensor 信息保持一致,否则上层框架分配会话时对不上号。

modality虽然是标准枚举(1 指纹、2 人脸、4 虹膜),但不同设备支持的传感器组合差异很大。指纹多的设备可能同时有屏下、侧边、后置多个 sensor;人脸设备有的带 3D 结构光,有的只是 2D 摄像头。common 里的 modality 组合一旦写的和硬件能力不一致,最典型的症状就是“设置界面多了个没用的入口,或者少了个该有的入口”。

strength更麻烦,它不是配置说了算,而是 HAL 和 TEE 的实际认证链路决定。你在 common 里统一写 15,如果某台设备的传感器实际只能做到 Weak,上层应用就会误以为它能保护密钥,真到了要解密 Keyguard 数据的时候,可能直接认证失败或者出现安全等级不匹配的问题。

2.3 强度字段不只是UI,它决定了密钥能不能用这个传感器保护

这里专门把strength拿出来说,因为它是最容易被“通用化”带偏的字段。

Android 的生物识别强度和Keystore的密钥保护策略是绑定的。一个传感器如果被声明为 STRONG(15),那么BiometricPrompt和Keyguard就可以用它的认证结果去解锁“强保护级”的密钥。这个判断不是 UI 层面的,而是安全体系的核心链路。

如果你在 common 里为了省事,把所有传感器都标成 15,但某台设备实际只能做到弱强度,从框架层的角度看,它是在“谎报”能力。应用的指纹支付、加密数据读取可能走得通,但底层其实没有满足相应的 security 要求。反过来说,如果传感器硬件确实支持强生物特征,你却在 common 里把它写成 1 或 2,那应用侧会认为设备没有强生物识别能力,功能入口直接灰掉。

所以,strength 字段不应该被无脑通用化。正确的做法是:common 里只放你确认整条产品线都能达到的强度值,拿不准的设备留给设备 overlay 单独覆盖。

3. 实操:在Android R上搭一个可行的common biometrics overlay

3.1 overlay资源目录的搭建方式

先给出一套我在 Android R 上实测可行的静态 overlay 结构。假设产品线的公共目录是device/common/biometric_overlay,整个目录层级如下:

device/common/biometric_overlay/ └── frameworks/ └── base/ └── core/ └── res/ └── res/ └── values/ └── config.xml

注意这里有个很容易搞错的地方:overlay 目录内的相对路径必须和目标资源包源码目录保持一致。目标资源包是frameworks/base/core/res,它的资源目录是res/values,所以 overlay 里就应该是frameworks/base/core/res/res/values/config.xml。少了这层res目录,编译期资源合并不会生效。

然后在产品级 mk 文件里加上:

PRODUCT_PACKAGE_OVERLAYS += device/common/biometric_overlay

PRODUCT_PACKAGE_OVERLAYS的优先级较高,适合放产品线级通用覆盖;如果只希望对某个具体设备生效,也可以放到 device mk 里用DEVICE_PACKAGE_OVERLAYS。我建议 common 配置统一走PRODUCT_PACKAGE_OVERLAYS,这样产品线内不同设备编译时都会被包含,单设备如果有特殊需求,再通过设备自身的 overlay 进一步覆盖。

config.xml的内容尽量精简,只保留这个数组:

<?xml version="1.0" encoding="utf-8"?> <resources> <string-array name="config_biometric_sensors" translatable="false"> <item>0:1:15</item> </string-array> </resources>

这里有个资源合并的细节值得强调:overlay 里的config.xml不是替换整个系统config.xml,而是逐项覆盖。你只定义了config_biometric_sensors这个 key,其他config_*配置全部不受影响。所以common overlay 里放的东西越少,越不容易和其他设备 overlay 产生冲突。

3.2 默认值怎么选、字段怎么定

common 的默认数组我推荐一个保守方案:单指纹传感器,id 用 0,modality 写 1,strength 写 15。

为什么选单指纹?

  • 绝大多数 Android 设备至少有一个指纹传感器,这是产品线里最大的公约数;
  • 人脸、虹膜设备属于特殊形态,它们几乎都有自己的专属 sensor 配置,不适合塞进 common;
  • 单传感器逻辑最简单,不会出现多传感器并发、切换、优先级等衍生问题。

id 写 0 的前提是确认产品线内所有设备的指纹 HAL 都把第一个指纹 sensor 的 id 上报为 0。如果某些设备的指纹 id 是 1 或 5,common 里写死 0 就会导致框架和 HAL 对不上。这种情况你就得把所有设备的 id 拉出来对一遍,找到统一值;找不到的话,common 里干脆不写数组,让各设备自己填。

strength 写 15 同样要覆盖整条产品线。如果你的产品线里存在某个低端设备,指纹模组只实现了弱强度认证,那 common 里就不能统一写 15。建议先拉产品线规格表,逐项确认硬件和 HAL 能力,再决定 common 的默认值。

如果连单指纹都不能覆盖全部设备,那我会直接放弃在 common 里放这个数组,改用“设备必填”策略,common 只负责承载其他公共配置。这并不丢人,因为config_biometric_sensors本身就是一个强设备相关项,强行通用只会给后面留坑。

3.3 编译与验证的关键命令

配置写完后,先完整编译 framework-res 相关产物,再用命令验证。

编译层面的验证是看最终打入framework-res.apk的资源配置是否正确:

aapt2 dump resources $OUT/system/framework/framework-res.apk | grep -A5 "config_biometric_sensors"

输出里应该能看到你 overlay 覆盖后的数组,而不是系统默认值。如果看不到,先回头检查PRODUCT_PACKAGE_OVERLAYS是否在产品 mk 里生效,以及 overlay 目录“frameworks/base/core/res/res/values”这个路径是否准确。

设备起来之后,用 dumpsys 验证框架层的实际注册情况:

adb shell dumpsys biometric

正常输出会列出所有已注册的BiometricSensor,包括每个传感器的 ID、Modality、Strength。拿到这份输出后,和adb shell dumpsys fingerprint、adb shell dumpsys face里的 HAL 侧信息逐项比对,确认框架声明和硬件实际能力完全一致。

设置界面也可以作为快速验证入口:单指纹配置下,正常情况下只应出现指纹相关设置;如果出现人脸设置项,说明有 modality 为 2 的传感器被错误注册,或者 HAL 侧实际上是有人脸传感器但 common 里没配。

4. 踩坑实录与排查清单

4.1 高频问题速查表

我整理了项目里最容易踩到的几个问题,基本涵盖了 common 化定制的大部分异常:

现象可能原因排查与处理
设置界面完全没有生物识别入口数组为空、格式解析失败,或 overlay 未生效先查 framework-res 里的资源是否被覆盖;再看 dumpsys biometric 传感器列表是否为空
指纹模块硬件工作正常,但指纹入口消失config_biometric_sensors 里没写指纹传感器把指纹 sensor 按实际 HAL 信息补进数组
人脸能用,但设置里出现指纹入口且报传感器错误modality 写成 1,框架按 fingerprint HAL 去调用把该条目 modality 改成 2,并确认设备存在对应人脸 HAL 服务
指纹设置正常,但应用提示“无强生物特征”strength 被写成 1 或 2,框架认为不是强传感器确认 HAL 是否支持 Strong,若支持则改成 15
开机后服务打印解析异常或字段越界item 格式不是三段,或分割符不对检查每一条 item 是否严格为 “id:modality:strength”,不要有多余空格
编译后 framework-res 仍是系统默认值OVERLAYS 没配到当前产品、目录层级错误核对 overlay 路径和 “frameworks/base/core/res/res/values” 相对层级,重新 make

4.2 我的几条实在建议

第一,common 里不要贪多。曾经为了省事,我在公共 overlay 里一次性放了0:1:15和1:2:15两组双传感器默认值,想着指纹人脸都覆盖,结果有一台只有指纹的设备,开机后设置界面凭空多出“人脸”入口,点进去直接从框架层报错,因为对应的人脸 HAL 服务根本不存在。这类问题会绕很大一圈才查出来。

第二,在 Android R 上做 framework-res 的定制,优先用静态 overlay(PRODUCT_PACKAGE_OVERLAYS),别一上来就上 RRO。RRO 在 R 版本上对 framework-res 做运行时覆盖会有一堆优先级和签名限制,静态 overlay 是编译期直接写进 apk,行为直白可控,适用于系统级配置默认值的场景。

第三,strength 不要硬标 15。这是个安全属性,不是 UI 开关。如果设备实际只能做到 Weak,你硬标 Strong,短期看功能都能跑,但涉及密钥保护的关键路径会埋下隐患。遇到这类问题,我会建议设备和 HAL 团队对齐强度定义,而不是在配置层打补丁。

第四,验证不要只看编译日志。改完配置后,用 aapt2 dump 看 framework-res.apk 里的资源,再用 dumpsys biometric 看运行时注册结果,两层对上了才算真的生效。我见过好几次“编译过了就以为没问题”,结果发现是 overlay 路径写错,资源压根没打进去。

最后分享一个我在多次踩坑后固定下来的方法:项目的生物识别配置分两层维护,biometric_common_overlay只放确认过的最大公约数(通常是单指纹 0:1:15),biometric_device_overlay放各设备特有覆盖。每次新设备导入时,先跑一遍dumpsys biometric和 HAL 的 dumpsys 做比对,确认框架声明和硬件能力一致,再提测。这套流程走熟之后,生物识别相关的配置回归问题几乎不会再出现。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询