目录
背景
项目基于 RK3568 + Android 13 进行系统裁剪,目标是去除不必要的系统应用以减小镜像体积。在移除联系人应用后,发现蓝牙功能异常:能扫描到设备,但无法稳定开启,日志中大量出现 [BT_RFKILL]: bt shut off power 与 bt turn on power 的循环。
一开始怀疑是固件缺失、HAL 库被删或驱动问题,但检查后发现 rtl8822cs_fw、rtl8822cs_config、libbt-vendor-realtek.so、android.hardware.bluetooth@1.0-impl.so 等文件全部存在,SELinux 策略、UART 权限也正常。问题显然不在底层。
怎么定位的
第一步:确认底层是否正常
抓取 dmesg 和 logcat,发现蓝牙 HAL 其实已经成功初始化:
-
固件下载成功:
vendor lib fwcfg completed -
HCI 初始化成功:
android.hardware.bluetooth@1.0-impl: OnFirmwareConfigured result: 0
说明蓝牙芯片、固件、HAL 都是好的。问题在上层。
第二步:抓崩溃日志
用以下命令过滤 AndroidRuntime 崩溃:
bash
logcat -b all -s AndroidRuntime:E | grep -i "BluetoothPbapService"
日志中反复出现同一个异常:
bash
java.lang.RuntimeException: Unable to start service com.android.bluetooth.pbap.BluetoothPbapService ...
java.lang.SecurityException: Failed to find provider com.android.contacts for user 0;
expected to find a valid ContentProvider for this authority
根因清晰了 :BluetoothPbapService 启动时需要访问 com.android.contacts 的 ContentProvider,但联系人应用被裁剪掉了,Provider 不存在,服务启动失败,抛出的 SecurityException 直接导致整个蓝牙进程崩溃。
第三步:确认崩溃链路
继续抓 BluetoothManagerService 的日志:
bash
logcat -b all -s BluetoothManagerService:D
可以看到完整的崩溃链路:
-
蓝牙服务启动 →
MESSAGE_BLUETOOTH_SERVICE_CONNECTED -
PBAP 服务启动失败,进程崩溃 →
MESSAGE_BLUETOOTH_SERVICE_DISCONNECTED -
系统尝试重启蓝牙服务 →
MESSAGE_RESTART_BLUETOOTH_SERVICE: retry count=1 -
再次崩溃,循环往复
这就是蓝牙状态反复跳变的根本原因。
第四步:尝试修复
第一次尝试(失败) :设置 persist.bluetooth.pbap.server.enabled=false。
结果重启后无效。用 getprop | grep -i pbap 检查发现:
bash
[bluetooth.profile.pbap.server.enabled]: [true] ← 真正生效的属性
[persist.bluetooth.pbap.server.enabled]: [false] ← 无效属性
原来 Android 13 蓝牙栈读取的是 bluetooth.profile.pbap.server.enabled,而不是带 persist. 前缀的那个。
第二次尝试(成功):设置正确的属性:
bash
setprop bluetooth.profile.pbap.server.enabled false
重启蓝牙后,日志中不再出现 BluetoothPbapService 崩溃,蓝牙状态稳定停留在 ON,问题解决。
怎么修改的
setprop 只在当前运行有效,因为 bluetooth.profile.pbap.server.enabled 不带 persist. 前缀,重启后会恢复默认值 true。要永久生效,必须固化到 SDK 源码。
在设备的 init.rc(如 device/rockchip/rk356x/init.rk356x.rc)中添加:
bash
on boot
setprop bluetooth.profile.pbap.server.enabled false
on boot 触发时,所有 build.prop 都已加载完毕,此时 setprop 的值不会再被覆盖,蓝牙服务后续读取到的就是 false。
总结
这次问题的本质是系统裁剪时移除了联系人应用,但没有同步关闭依赖它的蓝牙 PBAP Profile,导致上层 Java 服务启动失败并拖垮整个蓝牙进程。
几个关键经验:
-
裁剪系统应用时要检查依赖链。删除一个应用可能影响其他模块,尤其是蓝牙、通话、短信这类有 Profile 依赖的系统服务。
-
Android 属性前缀很重要 。
persist.前缀的属性才会持久化,不带前缀的只影响当前运行。设置属性前先用getprop | grep确认属性名是否正确。 -
定位问题要分层 。先从
dmesg确认底层(驱动、固件、HAL),再看logcat上层(Java 服务、系统进程),避免在错误的方向浪费时间。 -
setprop只适合临时验证,固化配置要改源码 。找到正确的属性名后,最终方案是写进设备的init.rc中,保证每次开机默认值正确。 -
崩溃日志里的异常栈是最直接的线索 。
SecurityException: Failed to find provider com.android.contacts这一行就已经指明了根因,剩下的只是顺着它往下查。