(TODO)
android.hardware.health-service.qti
最近正好在做关机充电这个,就详细看看吧。还是本着保密的原则,项目里的代码也不能直接用,这里就用的Github的。https://github.com/aosp-mirror
具体位置是:https://github.com/aosp-mirror/platform_system_core/tree/main/healthd
核心代码压缩包只有3M,比起AOSP整包的极度臃肿真是对比强烈。
首先还是看BP,编译的产物是charger,但是在新版AOSP中服务却不是这个名字。
cc_binary {
name: "charger",
defaults: ["charger_defaults"],
recovery_available: true,
srcs: [
"charger.cpp",
"charger_utils.cpp",
],
shared_libs: [
"android.hardware.health@2.0",
"android.hardware.health@2.1",
],
target: {
recovery: {
// No UI and libsuspend for recovery charger.
cflags: [
"-DCHARGER_FORCE_NO_UI=1",
],
exclude_shared_libs: [
"libpng",
],
exclude_static_libs: [
"libhealthd_draw",
"libhealthd_charger",
"libhealthd_charger_ui",
"libminui",
"libsuspend",
],
}
}
}
模块里面有个测试程序,可以看看整个模块的用法。
int main(int /*argc*/, char** /*argv*/) {
const char* dumpFile = "/data/local/tmp/dump.txt";
auto config = std::make_unique<healthd_config>();
InitHealthdConfig(config.get());
healthd_board_init(config.get());
sp<IHealth> passthrough = new TestHealth(std::move(config));
std::thread bgThread([=] {
android::ChargerHidl charger(passthrough);
charger.StartLoop();
});
// wait for healthd_init to finish
if (!getUpdateNotifier().waitFor(1000 /* wait ms */, true /* updated */)) {
LOG_THIS("Time out.");
exit(1);
}
passthrough->debug(createHidlHandle(dumpFile), {} /* options */);
std::string content = openToString(dumpFile);
int status = expectContains(content, {
"status: 4",
"health: 6",
"present: 1",
"level: 47",
"voltage: 45",
"temp: 987",
"current now: 99000",
"current avg: 98000",
"charge counter: 600",
"current now: 99",
"cycle count: 77",
"Full charge: 3515547"
});
if (status == 0) {
LOG_THIS("Test success.");
} else {
LOG_THIS("Actual dump:\n%s", content.c_str());
}
exit(status); // force bgThread to exit
}
可以看到,就是
android::ChargerHidl charger(passthrough);
charger.StartLoop();
在Android的HIDL(HAL Interface Definition Language)框架中,
StartLoop
是一个用于启动HIDL服务端线程循环的方法。它允许HIDL服务端在一个独立的线程中运行,以便可以接收和处理来自客户端的请求。具体来说,StartLoop
方法会创建一个线程池,并在这个线程池中运行,使得服务端可以异步处理多个客户端请求
整个其实是一个提供Hidl接口的服务。提供的接口如下:
AIDL implementation | HIDL implementation |
---|---|
Health::getChargeCounterUah |
Health::getChargeCounter |
Health::getCurrentNowMicroamps |
Health::getCurrentNow |
Health::getCurrentAverageMicroamps |
Health::getCurrentAverage |
Health::getCapacity |
Health::getCapacity |
Health::getChargeStatus |
Health::getChargeStatus |
Health::getEnergyCounterNwh |
Health::getEnergyCounter |
Health::getDiskStats |
Health::getDiskStats |
Health::getStorageInfo |
Health::getStorageInfo |
Health::BinderEvent |
BinderHealth::BinderEvent |
Health::dump |
Health::debug |
Health::ShouldKeepScreenOn |
Health::shouldKeepScreenOn |
Health::UpdateHealthInfo |
Health::UpdateHealthInfo |
类的定义如下:
namespace android {
// An implementation of Charger backed by HIDL implementation. Uses HIDL health
// HAL's HalHealthLoop.
class ChargerHidl : public ::android::ChargerConfigurationInterface,
public ::android::hardware::health::V2_1::implementation::HalHealthLoop {
using HalHealthLoop = ::android::hardware::health::V2_1::implementation::HalHealthLoop;
using HealthInfo_2_1 = android::hardware::health::V2_1::HealthInfo;
public:
explicit ChargerHidl(const sp<android::hardware::health::V2_1::IHealth>& service);
std::optional<bool> ChargerShouldKeepScreenOn() override;
bool ChargerIsOnline() override { return HalHealthLoop::charger_online(); }
void ChargerInitConfig(healthd_config* config) override { return HalHealthLoop::Init(config); }
int ChargerRegisterEvent(int fd, BoundFunction func, EventWakeup wakeup) override {
return HalHealthLoop::RegisterEvent(fd, func, wakeup);
}
bool ChargerEnableSuspend() override;
// HealthLoop overrides
void Heartbeat() override { charger_->OnHeartbeat(); }
int PrepareToWait() override { return charger_->OnPrepareToWait(); }
void Init(struct healthd_config* config) override { charger_->OnInit(config); }
// HalHealthLoop overrides
void OnHealthInfoChanged(const HealthInfo_2_1& health_info) override;
private:
sp<android::hardware::health::V2_1::IHealth> service_;
std::unique_ptr<Charger> charger_;
};
主要的定义还是在Charger类中。。。
在 AOSP 中,charger
模块主要负责设备在关机状态下的充电管理,以及开机时与电池管理相关的逻辑。以下将详细介绍其代码组成和流程:
1. 代码组成
charger
模块的实现包括以下几个部分:
1.1 核心代码路径
AOSP 的 charger
模块主要位于以下路径中:
system/core/healthd/
- 包含
charger
的主要代码,负责实现关机充电逻辑。
- 包含
system/core/init/
- 涉及关机充电模式的初始化部分。
kernel/drivers/power/
- 电池与充电相关的驱动程序,直接与硬件交互。
1.2 关键文件
以下是 charger
相关的关键文件和其作用:
healthd/healthd_charger.cpp
- 主要实现关机充电模式的功能。
- 包括电池状态监控、屏幕显示逻辑等。
healthd/healthd.cpp
- 负责电池状态的采集和报告。
healthd/BatteryMonitor.cpp
- 用于从电池驱动程序读取数据,如电量、电压、温度等。
init/
charger
模式的启动和关闭由init
进程管理。
1.3 驱动程序代码
与充电相关的硬件驱动代码位于:
drivers/power/supply/
- 包含与电池和充电器的交互逻辑,如 PMIC 驱动。
- 关键文件示例:
battery.c
:用于获取电池状态。charger-manager.c
:负责充电管理。
2. 模块的工作流程
2.1 关机充电模式的启动流程
当设备关机后插入充电器时,charger
模块负责进入关机充电模式。流程如下:
检测是否插入充电器
healthd
通过uevent
检测电源插入事件(通过/sys/class/power_supply/
)。- 如果检测到充电器连接,设备不会完全关机,而是进入
charger
模式。
启动关机充电程序
init
进程读取设备参数并启动charger
:- 判断
/sys/class/power_supply/battery/
中的状态,如POWER_SUPPLY_STATUS
是否为Charging
。 - 执行
charger
程序(healthd
的关机模式)。
- 判断
进入
charger
主循环healthd_charger.cpp
的main()
函数负责充电逻辑:- 初始化
BatteryMonitor
,读取电池状态。 - 绘制充电动画(或显示简单的充电图标)。
- 根据电池状态,调整显示的充电状态(如充电百分比或完成)。
- 初始化
2.2 运行时的充电流程
在设备正常运行时,charger
的主要功能是监控电池状态,并通过 BatteryService
报告给系统。
BatteryMonitor 获取电池信息
- 从
/sys/class/power_supply/battery/
读取电池的属性,如:- 电量:
capacity
- 状态:
status
(Charging
、Discharging
等) - 温度:
temp
- 电压:
voltage_now
- 电量:
- 使用
BatteryProperties
类存储这些信息。
- 从
更新电池状态
BatteryService
通过binder
通信,将电池状态提供给系统。- 系统 UI(如状态栏的电池图标)使用这些数据更新显示。
充电动画
- 在关机充电模式下,
charger
模块会调用绘图逻辑(如GraphicBuffer
)来显示充电进度动画。
- 在关机充电模式下,
2.3 重要流程细节
启动检测流程
代码文件:healthd_charger.cpp
- 初始化:
- 通过
BatteryMonitor
初始化电池状态监控。 - 检查
/sys/class/power_supply/
目录,加载相关文件。
- 通过
- 进入主循环:
- 每隔一段时间(默认 1 秒),读取电池状态。
- 如果检测到电池充满或拔掉电源,退出
charger
模式。
电池状态更新
代码文件:BatteryMonitor.cpp
- 从硬件读取状态:
- 通过
sysfs
或uevent
机制,获取充电器和电池的状态。 - 调用
healthd_mode_charger_battery_update()
更新状态。
- 通过
- 状态变化通知:
- 通知
charger
主循环更新显示内容。
- 通知
3. 显示逻辑
关机充电模式下,charger
模块会显示充电动画,逻辑位于:
healthd_draw.cpp
:用于绘制充电动画。- 主要步骤:
- 初始化显示设备(如
/dev/fb0
或 DRM)。 - 加载充电动画的图片资源。
- 根据电池充电状态更新显示内容。
- 初始化显示设备(如
4. 示例代码分析
以下是一个简化的流程代码片段:
void ChargerModeMainLoop() {
BatteryMonitor batteryMonitor;
// 初始化电池监控
batteryMonitor.Init();
while (true) {
// 获取电池状态
BatteryProperties props = batteryMonitor.GetBatteryProperties();
// 检测是否充电完成
if (props.status == BATTERY_STATUS_FULL) {
ShowChargingComplete();
break;
}
// 更新充电动画
UpdateChargingAnimation(props.capacity);
// 延迟一段时间
sleep(1);
}
}
5. 常见问题排查
- 关机充电模式不启动:
- 检查
init.rc
中是否正确配置了charger
。 - 确保
/sys/class/power_supply/
下的节点存在且返回正确的状态。
- 检查
- 充电动画不显示:
- 检查
/dev/fb0
或 DRM 是否正常初始化。 - 确保
charger
模块加载了正确的图片资源。
- 检查
- 电池状态不准确:
- 确认驱动程序是否正确读取硬件信息。
- 检查
BatteryMonitor
的逻辑是否与硬件匹配。
通过以上分析可以看到,AOSP 中的 charger
模块功能涵盖了关机充电模式管理、运行时电池状态更新和 UI 显示等内容。完整的实现需要驱动层、系统服务层和应用层的协同工作。
代码中对于C++的高级应用还是很熟的。
template <typename T>
class Atomic {
public:
Atomic(T&& init) : mValue(std::move(init)) {}
void set(T&& newVal) {
{
std::lock_guard<std::mutex> lock(mMutex);
mValue = std::move(newVal);
}
mChanged.notify_all();
}
bool waitFor(long ms, const T& expectVal) {
std::unique_lock<std::mutex> lock(mMutex);
return mChanged.wait_for(lock, std::chrono::milliseconds(ms),
[this, &expectVal] { return mValue == expectVal; });
}
private:
std::mutex mMutex;
std::condition_variable mChanged;
T mValue;
};
Atomic<bool>& getUpdateNotifier() {
static Atomic<bool> val(false);
return val;
}
参考: