简介:这是面向瑞芯微(Rockchip)等Android平台的GNSS 2.0定位补丁资源,主要解决弱信号环境下的定位精度与响应速度问题,适合从事系统移植、驱动适配或应用层定位开发的工程师。补丁覆盖信号处理增强、多星系统兼容、定位算法优化、用户体验与节能改进等关键点,例如多频处理、抗干扰能力增强和信号捕获灵敏度提升,并对RK平台做了硬件加速与驱动层面的针对性优化。资源包共37个文件,以C++源码为主(16个cpp与15个头文件),辅以Android.bp构建脚本、xml服务配置、rc启动文件以及说明文档,整体约51KB,结构清晰,便于直接集成或二次修改。已有872人学习浏览,适合需要了解GNSS 2.0 HAL实现或适配RK平台定位服务的开发者,可从中获取具体接口实现、配置思路以及Android定位API的落地参考。
1. GNSS 2.0 补丁在 RK 平台上到底改了些什么
在 RK 平台的 Android 固件迭代里,GNSS HAL 长期停在 1.0 是常态。很多车机、Android TV 盒子、商显一体机上虽然装了一颗支持多星座的 u-blox 或国产 GNSS 模组,但 Android 定位框架能拿到的数据一直被限制在基本的 GPS 定位结果层面——卫星明细、双频观测、天线信息这些 2.0 才有的能力完全使不上。这次 GNSS2.0 补丁做的事情,就是把android.hardware.gnss的 HIDL 接口从 1.0 抬到 2.0,同时把 RK 平台这套 HAL 的兼容配置(manifest、SELinux、gnss.conf)一并升级到位。适合手里拿着 RK SDK、正被定位问题或多星座适配折磨的工程师——升级后最直观的变化是北斗、GLONASS 卫星开始出现在卫星列表里,冷启动 TTFF 也有改善。
2. 先看懂 GNSS HAL 2.0 与 1.0 的差异:接口演进与 RK 适配的入口
2.1 HIDL 接口从 1.0 到 2.0:多出的能力和必须动的代码
Android 9(API 28)真正引入了 GNSS HAL 2.0,后来 Android 11 又出了 2.1,再往后迁移到 AIDL 体系。但 RK SDK 定制程度深,很多分支至今还停在 1.0 或 1.1。想理解补丁的意义,得先看清楚 1.0 到 2.0 到底加了多少东西。
核心差异集中在三个层面:
第一个是IGnssCallback回调。2.0 的gnssLocationCb里,位置结构新增了elapsedRealtimeNs时间对齐字段。framework 的LocationManager拿到定位结果后,会核对这个时间戳与系统时间的关系。如果 HAL 上报的位置里这个字段缺失或偏差过大,定位结果会被标记为不可信,表现出来就是"能定位,但应用端一直提示位置过时"。
// IGnssCallback 1.0 的回调,只有基本字段 Return<void> gnssLocationCb(const GnssLocation& loc) { // loc 只有经纬度、时间戳、精度 } // IGnssCallback 2.0 的回调 Return<void> gnssLocationCb(const GnssLocation& loc) { // loc 里多了 elapsedRealtimeNs // HAL 必须把这个字段填对,framework 才会信任定位结果 // 2.0 还要求实现 setSignalDelay,上报信号注入延迟 }第二个是IGnssMeasurement测量数据。1.0 的测量回调只有信号强度和粗略的码相位;2.0 里每条测量都带constellation、carrierFrequencyHz、multipathIndicator字段。载波频率是双频定位的根基,L1+L5 双频融合如果没有它,framework 根本区分不了同一颗卫星的两个频点信号。
第三个是新增的IGnssAntennaInfo接口,通过回调上报天线增益方向图、相位中心偏移,是载波相位解算这类高精度方案的输入。普通米级导航场景感知不到它存在,但它标志着 2.0 的设计目标是覆盖从常规定位到高精度定位的完整链路。
| 能力 | GNSS 1.0 | GNSS 2.0 |
|---|---|---|
| 位置回调 | 基本字段 | 增加时间对齐字段 |
| 测量数据 | 信号强度、码相位 | 增加星座、载波频率、多径标志 |
| 天线信息 | 无 | 增益方向图、相位中心偏移 |
| 多星座支持 | GPS 为主 | 北斗/GLONASS/Galileo 完整字段 |
从代码量看,1.0 到 2.0 不是改个版本号字符串那么简单。hardware/interfaces/gnss/2.0/default/的实现代码比 1.0 多出近一倍,多出来的部分主要是 2.0 回调代理、测量数据结构转换、extension 接口注册逻辑。所以拿到 RK 平台的补丁时,如果发现只是几个小文件的 diff,基本可以断定它只改了 service 注册名,测量数据增强没有真正落地——验收时要专门盯这条。
2.2 RK 平台默认的 GNSS 代码路径与这次补丁的接触面
RK 的 SoC 在车机、Android TV 盒子、商显领域出货量很大,RK3288、RK3399、RK3568、RK3588 都是常见平台。但 Rockchip 和高通、联发科不一样,SoC 内部一般没有成熟的内置 GNSS 射频,绝大多数产品走外挂模组。
最常见的是 UART 接口方案:模组通过串口每秒吐 NMEA 0183 语句,HAL 解析语句,再转换成 HIDL 接口定义的数据结构上报给 framework。u-blox NEO-M8N 是这类方案里最经典的模组,很多 RK SDK 的gnss.conf默认配置就是按它的行为调的。也有一部分板子走 USB 接口接和芯星通或中科微的模组,枚举出来是一个 CDC ACM 虚拟串口。
这套架构决定了补丁实际要碰的文件路径:
hardware/rockchip/gps/:HAL 源码主体,NMEA 解析、卫星状态管理、HIDL service 注册全在这device/rockchip/rkXXX/manifest.xml:HIDL service 版本声明,framework 靠它才能找到 2.0device/rockchip/common/gnss.conf:模组行为开关和多星座配置device/rockchip/common/sepolicy/:SELinux 规则,决定 HAL 进程能访问哪些节点和属性
一份合格的补丁至少要动其中三个方向。只改 HAL 源码和 manifest 的补丁,刷进去之后通常会出现"版本显示 2.0 但定位反而不如从前"的怪象——不是 HAL 代码的问题,而是 SELinux 和 conf 的配套没跟上。
2.3 动手合入前先做的三件事
拿到补丁包,先静态检查,不要急着 apply。
第一,核对分支。RK SDK 的分支很碎,rk3399_android10和rk3568_android13目录结构差异很大。补丁头里的文件路径如果在你本地不存在,说明分支对不上,用git apply --check几秒钟就能验证。
第二,清点补丁文件列表。把 .patch 文件打开,扫一遍diff --git后面的路径,对比你预期它该动的文件。只改了Android.bp和GnssHal.cpp的补丁,通常会在运行时被 manifest 版本不匹配绊住;看到清单里有manifest.xml和.te策略文件,才算比较完整。
第三,确认 HIDL 定义存在。RK SDK 为了减体积偶尔会裁剪hardware/interfaces里用不到的包:
ls hardware/interfaces/gnss/ # 期望至少看到 1.0/ 与 2.0/ 两个目录2.0/目录不存在的话,先补回这个 HIDL 包再谈编译。这个检查 30 秒做完,但能省掉一次两三个小时的完整编译,值回票。
3. 在 RK 源码里合入 GNSS2.0 补丁:环境准备与具体步骤
3.1 补丁合入操作与增量编译验证
进入 RK SDK 根目录,先检查工作区状态,确认没有未提交的修改干扰补丁:
cd ~/rk3399_android10 git status --short git stash # 有本地修改就先暂存 git apply --check gnss2.0.patch git apply gnss2.0.patch git status --shortgit apply --check是干跑不落盘,返回空表示补丁上下文完全匹配。任何报错都不要用git apply --force硬打,那会把冲突直接写进工作区,后面定位问题会更难。报错时我会打开补丁里失败 hunk 的上下文,到本地源码搜对应函数,手动合入改动。慢是慢一点,但每处都过脑子,比强打后排查半天的总成本低。
合入后第一件事是检查Android.bp里的模块名。RK 平台 2.0 HIDL service 的模块名一般是android.hardware.gnss@2.0-service,如果补丁改动后还是 1.0 的模块名,说明 service 注册逻辑没跟上。然后编译这个模块:
source build/envsetup.sh lunch rk3399-userdebug make android.hardware.gnss@2.0-service -j16独立 HIDL service 的模块编译很快。验证行为时可以做增量 push,不需要整包刷机:
adb root adb remount adb push out/target/product/rk3399/vendor/lib64/hw/android.hardware.gnss@2.0-service.so \ /vendor/lib64/hw/ adb shell killall android.hardware.gnss@2.0-serviceadb remount只在 userdebug 或 eng 版本可用。杀掉进程后 init 会自动拉起新 so 对应的 service。这种增量部署方式比整机刷机高效得多,特别适合 HAL 代码逻辑的迭代验证。整包刷机留到最终回归阶段再做。
如果编译时碰到Cannot locate symbol或者undefined reference to android::hardware::gnss::V2_0::...,基本可以判断是接口包版本对不上:SDK 里的hardware/interfaces仓库过旧,或者 RK 的soong配置把 2.0 接口库排除在 vendor 分区之外。前者要整体更新hardware/interfaces到匹配版本,后者要在Android.bp里确认vendor: true和proprietary: true标记,这是 RK SDK 里最常见的编译失败来源之一。
3.2 VNDK 与 HIDL 版本声明:manifest、rc、SELinux 的联动
增量编译能过只是第一步。GNSS HAL 是 vendor 进程,它链接的android.hardware.gnss@2.0接口库必须放在 vendor 分区。补丁在Android.bp里如果加了vendor: true或者动了LOCAL_VINTF_FRAGMENTS,要特别留意 Android 10 之后的 VINTF 校验机制——开机时hwservicemanager会对模块的 manifest 声明和实际编译产物做比对,不一致就直接拒绝注册。
对应检查device/rockchip/rkXXX/manifest.xml里的声明:
<hal format="hidl"> <name>android.hardware.gnss</name> <version>2.0</version> <version>1.0</version> <interface> <instance>default</instance> </interface> </hal>这个顺序有讲究。hwservicemanager按声明顺序匹配客户端请求,把 2.0 写在 1.0 前面,framework 按 2.0 的 fqname 请求时优先拿到的就是 2.0 实现;反过来先声明 1.0,客户端就会绑到旧版本,升级白做。
rc 文件方面,确认 service 名是android.hardware.gnss@2.0-service,class是hal,seclabel指向正确的 SELinux domain。RK SDK 里如果有残留的 1.0 service 定义,而二进制已经是 2.0,init 会反复拉起失败,logcat 里的报错信息很迷惑——报的是 1.0 的错,但你要查的是 manifest。
SELinux 策略是另一个隐蔽坑。RK 平台默认的hal_gnss.te只给 1.0 的文件路径打了标签。升级 2.0 后 HAL 进程访问串口节点或 vendor 属性被拦截时,先看 dmesg:
adb shell dmesg | grep avc | grep -i gnss出现 avc denied 就在device/rockchip/common/sepolicy/vendor/hal_gnss.te里补对应规则,例如允许 HAL 访问通用串口节点:
allow hal_gnss tty_device:chr_file rw_file_perms;改完要重新编译 sepolicy 并刷入 boot 分区,光改源码不刷 boot,策略不会生效。这个坑在换过 UART 节点编号的板子上特别常见,dts 里串口换了,内核驱动 ko 的加载顺序和 SELinux 标签往往也变了,三条线要一起查。
3.3 GNSS conf 参数调整:多星座、北斗与模组适配
gnss.conf是 HAL 和模组之间的翻译层。升级 2.0 之后 framework 会向 HAL 要更丰富的卫星数据,HAL 就得让模组把多星座的 NMEA 语句吐出来。
RK SDK 的配置文件通常路径是/vendor/etc/gnss.conf,采用简单的 key-value 形式。几个关键参数:
| 参数 | 默认值 | 作用 |
|---|---|---|
| NMEA_PROTOCOL_V2 | 0 | NMEA 协议版本,老款 u-blox 模组要按固件调整 |
| GPS_MULTI_CONSTELLATION | 1 | 多星座总开关 |
| BEIDOU_CONSTELLATION | 1 | 北斗上报开关 |
| GLONASS_CONSTELLATION | 1 | GLONASS 上报开关 |
| GALILEO_CONSTELLATION | 0 | Galileo 开关,国内模组很多不支持 |
| NTP_SERVER | pool.ntp.org | 时间同步服务器,国内网络建议更换 |
快速验证时可以直接改设备上的文件:
adb shell "sed -i 's/BEIDOU_CONSTELLATION=0/BEIDOU_CONSTELLATION=1/' /vendor/etc/gnss.conf" adb shell killall android.hardware.gnss@2.0-servicesed直接改设备文件的方式只适合调试。正式改版一定要把改动合并回 SDK 源码的device/rockchip/common/gnss.conf,否则下次整机刷机或恢复出厂设置就丢了。
改完看 HAL 日志确认多星座数据真正在跑:
adb logcat -s GnssHAL -d | grep "NMEA:.*GGA"出现$GNGGA说明 GPS 和其他星座的融合数据已经进入 NMEA 输出;只有$GPGGA则说明模组侧没有接受多星座配置,需要直接用串口连模组发配置指令(u-blox 是CFG-GNSS)。这一步已经超出 Android 层,属于模组固件适配的范畴,补丁本身解决不了。
4. 兼容性升级常见问题:5 个高频坑与排查方法
GNSS 2.0 升级在 RK 平台上的坑,很大一部分和 HAL 代码本身无关,是版本声明、分区策略、模组配置这些"周边零件"没跟上。下面按 RK3399 和 RK3568 平台上的高频坑来写。
4.1 HAL service 起不来
现象:开机后adb shell lshal里没有 gnss 2.0,logcat 反复出现service android.hardware.gnss@2.0::IGnss does not exist,dumpsys location里没有 GNSS provider。
原因:两种最常见。一是 HAL 的 so 没装进/vendor/lib64/hw/;二是 manifest.xml 里没声明 2.0 版本。还有种隐蔽情况是 init 脚本和Android.bp里的模块名不一致——Android.bp生成的是android.hardware.gnss@2.0-service,rc 文件里却还在等android.hardware.gnss@1.0-service,init 找不到对应的 service entry。
排查路径:
adb shell ls /vendor/lib64/hw/ | grep gnss adb shell ps -A | grep gnss adb shell lshal | grep -i gnss解决:so 缺失就重新编译并 push;manifest 漏了就补声明;rc 文件里 service 名没跟着改就手动改,然后重刷 boot 分区。这个坑最费时间的是第三种,因为 logcat 报 1.0 的错,很容易把人引到旧代码上去。
4.2 定位能跑,但 framework 侧一直走 1.0
现象:定位功能不受影响,卫星状态也正常,但dumpsys报告 HAL 版本还是 1.0。用户嘴上不说,但多星座能力根本没透传上去。
原因:manifest.xml 里 1.0 和 2.0 两个 version 同时存在,且 1.0 写在前面。hwservicemanager解析客户端请求时按声明顺序匹配,framework 拿到的永远是先匹配到的 1.0。
解决:把<version>2.0</version>挪到<version>1.0</version>之前,重新编译 vendor 分区刷机。验证方法:
adb shell dumpsys activity service com.android.location.provider | grep -i "gnss\|interface"看到IGnss 2.0才算真正生效。这个问题容易被忽略,因为定位功能没坏,表面上没有任何异常信号——典型的"黑匣子"现象,必须主动去查版本号。
4.3 北斗卫星上报数量明显偏少
现象:开阔天空下 GPS 可见 15 颗,北斗只有 4 到 5 颗,且北斗的 C 编号不连续。多星座等于没开。
原因:一个是 HAL 解析 NMEA 时语句过滤太狠,只放行GP前缀的语句,GB(北斗)、GL(GLONASS)前缀被直接丢弃。另一个是模组固件里 BDS 根本没打开,HAL 侧把天线开关掰断了也没用。
解决:先用串口直接连模组,按模组手册的指令打开 BDS(u-blox 是CFG-GNSS,国产模组一般有自己的 AT 指令集),确认模组能吐出GBGSV语句。再回到 HAL 源码,检查 NMEA 解析器的白名单逻辑,把GP、GB、GL、GN前缀全部包含进去。最后验证回归:
adb logcat -s GnssHAL -d | grep "GBGSV"看到北斗卫星的语句持续输出,才算真正通了。
4.4 SELinux avc 拒绝,HAL 进程读不到串口
现象:HAL 进程还活着,lshal 也能看到 service,但定位结果一直是空。dmesg里刷avc: denied { read write } for name="ttyS4"。
原因:dts 配置里 UART 节点换了编号(比如从 ttyS4 换成 ttyS5),但file_contexts没有给新节点打 label。SELinux 把所有读写请求拦在 HAL 进程之外。Android 10 之后的 vendor 分区有独立 policy,漏掉一处就可能让 HAL 完全丧失与外设通信的能力。
解决:在device/rockchip/common/sepolicy/vendor/file_contexts里给串口节点头上通用标签:
/dev/ttyS[0-9]+ u:object_r:tty_device:s0重新编译 vendor 分区刷入。排查时优先看 dmesg 里的具体 avc 行,一条一条补规则比把hal_gnss.te里的权限全放开安全得多——直接放开权限能解决问题,但等于给系统开了一扇后门,车机这类连续运行的产品不建议这么干。
4.5 升级后冷启动首次定位极慢
现象:刷机后第一次冷启动定位要 90 秒以上,第二次定位就恢复正常。用户反馈"定位半天转圈"。
原因:升级补丁可能重置了/data/vendor/gps下的辅助定位文件(星历、位置缓存)。冷启动时如果等不到星历注入,接收机只能等卫星广播星历,这个过程受天空遮挡影响很大,30 秒到两三分钟都有可能。
解决:确认gnss.conf里NTP_SERVER指向的服务器在当前网络环境可用。国内网络访问pool.ntp.org经常超时,换成国内可达的 NTP 服务器或厂商自建服务器更稳。支持 XTRA 的模组(如 u-blox M8 系列)还要在 HAL 层确认 XTRA 数据能正常注入,dumpsys location里如果显示XTRA not available,多半是网络或数据源的问题。
改完清缓存重测:
adb shell rm -rf /data/vendor/gps/* adb shell killall android.hardware.gnss@2.0-service冷启动 60 秒内算合格,温启动 10 秒内,热启动 5 秒内。这一项必须在补丁验证清单里固定下来,否则用户感知差异会非常明显。
5. 验证补丁是否真正生效:日志、接口与实测指标
5.1 版本确认三步走
验证补丁,我从三条命令开始:
adb shell lshal | grep -i gnss adb shell dumpsys location | grep -i "gnss\|provider" adb logcat -s GnssHAL -d | grep "NMEA:.*GGA"期望结果分别是android.hardware.gnss@2.0::IGnss/default、framework 绑定到 2.0、以及$GNGGA前缀的多系统 NMEA 数据。三条命令同时通过,HAL 版本、framework 绑定、模组数据三层才算全通,不是其中一两条对了就能收工的。
5.2 TTFF 与定位精度实测
版本确认后再测体验。TTFF 是最直观的指标——清缓存测冷启动,看 logcat 里首次 fix 的时间戳。精度方面,GNSS 2.0 升级真正的收益是多星座融合带来的城市峡谷信号可用率提升,而不是把 GPS 误差从 5 米收窄到 2 米。如果产品目标只是普通导航,评估重点放在信号可用率变化和 TTFF 上就好,不要被模组厂商宣传的厘米级精度带偏。
5.3 回归验证清单与长期监控
我把版本检查、TTFF、SELinux、AGPS 注入这几项固定成一个清单,每次改完 GNSS 相关代码都过一遍。补丁升级后的头一周,拿不同批次的两块板子跑长稳,每天拉一次定位日志看卫星数和 TTFF 的漂移。这样一来有问题还能及时返工,不至于等批量出货之后才发现偶发定位失效。这也是我这些年做 RK 平台定位适配养成的习惯——先看接口,再看日志,最后拿数据说话。希望这套验证思路能帮到你。
本文还有配套的精品资源,点击获取