RK3568 Android 13 屏蔽 healthd 电池日志经验分享

目录

​​​​背景

怎么屏蔽

为什么只屏蔽日志,而不是干掉整个电池服务/驱动

总结


背景

板子直接供电、没有电池,但 healthd 仍认为电池存在,于是每隔约 60 秒就往内核日志打印一行电池状态:

复制代码
[ 2056.816449][ T331] healthd> battery l=50 v=3 t=2.6 h=2 st=3 c=-1600 fc=100 chg=au

这类日志来自驱动上报的默认占位值,并非真实电池数据,持续刷屏会干扰其他调试信息。

设备树方案尝试后无效,因此改用最直接的办法:从 healthd 源码层屏蔽日志打印。


怎么屏蔽

修改文件 :system/core/healthd/BatteryMonitor.cpp

日志最终由 doLogValues() 函数中的 KLOG_WARNING 输出。由于该函数只负责打印、不参与任何业务逻辑,直接整体屏蔽即可:

cpp 复制代码
static void doLogValues(const HealthInfo& props, const struct healthd_config& healthd_config) {
    (void)props;
    (void)healthd_config;
#if(0)
    char dmesgline[256];
    size_t len;

    if (props.batteryPresent) {
        snprintf(dmesgline, sizeof(dmesgline), "battery l=%d v=%d t=%s%d.%d h=%d st=%d",
                 props.batteryLevel, props.batteryVoltageMillivolts,
                 props.batteryTemperatureTenthsCelsius < 0 ? "-" : "",
                 abs(props.batteryTemperatureTenthsCelsius / 10),
                 abs(props.batteryTemperatureTenthsCelsius % 10), props.batteryHealth,
                 props.batteryStatus);

        len = strlen(dmesgline);
        if (!healthd_config.batteryCurrentNowPath.isEmpty()) {
            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " c=%d",
                            props.batteryCurrentMicroamps);
        }

        if (!healthd_config.batteryFullChargePath.isEmpty()) {
            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " fc=%d",
                            props.batteryFullChargeUah);
        }

        if (!healthd_config.batteryCycleCountPath.isEmpty()) {
            len += snprintf(dmesgline + len, sizeof(dmesgline) - len, " cc=%d",
                            props.batteryCycleCount);
        }
    } else {
        len = snprintf(dmesgline, sizeof(dmesgline), "battery none");
    }

    snprintf(dmesgline + len, sizeof(dmesgline) - len, " chg=%s%s%s%s",
             props.chargerAcOnline ? "a" : "", props.chargerUsbOnline ? "u" : "",
             props.chargerWirelessOnline ? "w" : "", props.chargerDockOnline ? "d" : "");

    KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);
#endif
}

两个注意点:

  1. Android 编译开启了 -Wall -Werror,空函数体会报 unused parameter 错误,因此需加 (void)props; 和 (void)healthd_config; 消除警告。

  2. 用 #if(0) 而非直接删除代码,便于后续需要时快速恢复。

为什么只屏蔽日志,而不是干掉整个电池服务/驱动

这是本次修改中最关键的决策点。看似"直接把电池服务或驱动干掉"更彻底,实际上隐患很大:

1. 会触发"幽灵关机"风险

Android 13 的电池状态由 android.hardware.health AIDL HAL 提供,healthd 作为守护进程采集数据。如果直接移除服务,系统拿不到有效电量,在 SELinux Permissive 模式下极可能误判为低电量而自动关机,反而引入更严重的问题。

2. 上层 UI 和 API 会报错

SystemUI、Settings 等模块会持续查询电池状态。服务缺失会导致这些模块异常、报错甚至崩溃,问题比日志刷屏严重得多。

3. 日志本身无害,只是"吵"

doLogValues() 是纯打印函数,不修改任何全局状态、不返回任何值、不参与数据采集与上报。屏蔽它只是让内核日志安静下来,对系统功能零影响。

4. 改动范围最小

  • 干掉服务/驱动:涉及 HAL、SELinux、设备树、上层适配,牵一发动全身。

  • 屏蔽日志:改一个函数、三行代码,编译刷机即验证。

一句话总结:日志只是"噪音",不是"故障"。我们要消除的是噪音,而不是把发声的整个系统拆掉。


总结

  • 定位准确 :日志由 doLogValues() 中的 KLOG_WARNING 打印,该函数纯输出、无副作用,可安全屏蔽。

  • 方案最简 :一行 #if(0) 加两行 (void) 即可,无需改动设备树或驱动。

  • 风险为零:电池检测、状态上报、上层 API 均不受影响,只是不再往内核日志写内容。

  • 思路清晰:只消除"噪音"而非移除"服务",避免引入关机、UI 崩溃等连锁问题。

  • 适用场景:无电池设备、设备树方案无效、只需消除日志刷屏的调试场景。

相关推荐
Android系统攻城狮4 小时前
Linux Gstreamer深度解析之gst_audio_encoder_set_frame_max调用流程与实战(六十一)
android·linux·运维·音视频·gstreamer音视频·音视频进阶·gstreamer音视频进阶
传奇开心果编程7 小时前
【Compose Multiplatform 跨端开发学与练】第7课 平台适配与互操作
android·windows·学习·ui·ios·kotlin·composer
mmsx7 小时前
Android 地图数据链路:从 GDAL、KML 到多引擎适配
android·kotlin
2601_968900777 小时前
大模型版本回归评估实战:从能力指标到额度口径的完整链路
android·数据挖掘·回归
ao-weilai10 小时前
MySQL数据库:基本查询
android·数据库·mysql
事圆则缓10 小时前
Kotlin 泛型方差实战:out、in、星投影与类型擦除
android·开发语言·kotlin
传奇开心果编程12 小时前
【Compose Multiplatform 跨端开发学与练】第5课 网络与数据层
android·网络·学习·ui·ios·kotlin·composer
ao-weilai13 小时前
MySQL数据库:内置函数
android·数据库·mysql
SWAGGY..13 小时前
【C++进阶】:(7)红黑树的原理与 C++ 实现:结构设计、插入调整及性质验证
android·java·开发语言·c++·算法
Android打工仔13 小时前
CoroutineScheduler 设计解析(上)—— 为什么 Dispatchers.IO 会创建更多线程?
android·kotlin·源码阅读