【Android 10 源码】healthd 模块 HAL 2.0 分析
Android 9 引入了从 health@1.0 HAL 升级的主要版本 android.hardware.health HAL 2.0。这一新 HAL 具有以下优势:
- 框架代码和供应商代码之间的区别更清楚。
- 弃用了不必要的 healthd 守护程序。
- 供应商对运行状况信息报告进行自定义的自由度更高。
- 更多设备运行状况信息(不仅包括电池信息)。
Android 10 沿用了 android.hardware.health HAL 2.0。
术语
- health@1.0:android.hardware.health@1.0 的缩写。指的是 Android 8.0 中发布的运行状况 HIDL 的 1.0 版 HAL。
- health@2.0:android.hardware.health@2.0 的缩写。指的是 Android 9 中发布的运行状况 HIDL 的 2.0 版 HAL。
- charger:在关机模式充电过程中运行的可执行文件,用于显示手机充电动画。
- recovery:在恢复模式下运行的可执行文件,必须检索电池信息。
- healthd:在 Android 中运行的旧版守护进程,用于检索与运行状况相关的信息并将其提供给框架。
- storaged:在 Android 中运行的守护进程,用于检索存储信息并将其提供给框架。
Android 9 中的“运行状况”服务
在 Android 9 中,运行状况组件的工作原理详情如下图所示:
该框架尝试从 hwservicemanager 中检索 health@2.0 服务。如果检索失败,它将调用 health@1.0(在 Android 8.x 中提供)。同时,它还会保留原有代码路径,以便 Android 9 系统映像与 Android 8.x 供应商映像兼容。框架不会同时从两个 HAL 中检索信息,因为设备上只能存在一个服务版本(1.0 或 2.0)。
对于其他模式:
Android 9 中的运行状况、关机模式充电和恢复模式
HAL 接口 2.0
health@2.0 HAL 为框架提供了与旧版 healthd 守护程序相同的功能。此外,它还提供了与 healthd 以前作为 Binder 服务提供的 API 类似的 API(即 IBatteryPropertiesRegistrar)。
主界面 IHealth 提供以下函数:
registerCallback,用于替换 IBatteryPropertiesRegistrar.registerListener
unregisterCallback,用于替换 IBatteryPropertiesRegistrar.unregisterListener
update,用于替换 IBatteryPropertiesRegistrar.scheduleUpdate
IBatteryPropertiesRegistrar.getProperties 将由以下内容替换:
getChargeCountergetCurrentNowgetCurrentAveragegetCapacitygetEnergyCountergetChargeStatusgetHealthInfo
此外,IHealth 还为 storaged 提供了以下新 API,以检索特定于供应商的存储相关信息:
- getStorageInfo
- getDiskStats
通过回调和 getHealthInfo 返回一个新结构 @2.0::HealthInfo。此结构包含可通过 health@2.0 HAL 访问的所有设备运行状况信息,包括:
- 充电信息(交流电/USB/无线、电流、电压等)
- 电池信息(状态、电池电量、电流、电压、充电、技术等)
- 存储信息(存储设备信息、磁盘统计信息)
下面来详细查阅 health@2.0 HAL 中的定义。
hardware/interfaces/health/2.0/IHealth.hal
package android.hardware.health@2.0;import @1.0::BatteryStatus;import IHealthInfoCallback;/*** IHealth manages health info and posts events on registered callbacks.* IHealth 管理运行状况信息并在已注册的回调上发布事件。*/
interface IHealth {/*** Register a callback for any health info events.* 为任何运行状况信息事件注册回调。** Registering a new callback must not unregister the old one; the old* callback remains registered until one of the following happens:* 注册一个新的回调函数不能注销旧的回调函数;旧的回调函数保持注册状态,直到发生以下情况之一:* - A client explicitly calls {@link unregisterCallback} to unregister it.* - 客户端显式调用{@link unregisterCallback}来注销它。* - The client process that hosts the callback dies.* - 承载回调的客户端进程死亡。** @param callback the callback to register.* @return result SUCCESS if successful,* UNKNOWN for other errors.*/registerCallback(IHealthInfoCallback callback) generates (Result result);/*** Explicitly unregister a callback that is previously registered through* 显式地注销以前注册过的回调* {@link registerCallback}.** @param callback the callback to unregister* @return result SUCCESS if successful,* NOT_FOUND if callback is not registered previously,* UNKNOWN for other errors.*/unregisterCallback(IHealthInfoCallback callback) generates (Result result);/*** Schedule update.* 安排更新。** When update() is called, the service must notify all registered callbacks* with the most recent health info.* 当调用 update() 时,服务必须用最近的运行状况信息通知所有已注册的回调。** @return result SUCCESS if successful,* CALLBACK_DIED if any registered callback is dead,* UNKNOWN for other errors.*/update() generates (Result result);/*** Get battery capacity in microampere-hours(µAh).* 以微安时(µAh)计算电池容量。** @return result SUCCESS if successful,* NOT_SUPPORTED if this property is not supported* (e.g. the file that stores this property does not exist),* UNKNOWN for other errors.* @return value battery capacity, or 0 if not successful.*/getChargeCounter() generates (Result result, int32_t value);/*** Get instantaneous battery current in microamperes(µA).* 以微安(µA)计算电池瞬时电流。** Positive values indicate net current entering the battery from a charge* source, negative values indicate net current discharging from the* battery.* 正值表示从充电源进入电池的净电流,负值表示从电池放电的净电流。 ** @return result SUCCESS if successful,* NOT_SUPPORTED if this property is not supported* (e.g. the file that stores this property does not exist),* UNKNOWN for other errors.* @return value instantaneous battery current, or 0 if not* successful.*/getCurrentNow() generates (Result result, int32_t value);/*** Get average battery current in microamperes(µA).* 以微安(µA)计算平均电池电流。** Positive values indicate net current entering the battery from a charge* source, negative values indicate net current discharging from the* battery. The time period over which the average is computed may depend on* the fuel gauge hardware and its configuration.* 正值表示从充电源进入电池的净电流,负值表示从电池放电的净电流。* 平均值的计算时间可能取决于硬件及其配置。* @return result SUCCESS if successful,* NOT_SUPPORTED if this property is not supported* (e.g. the file that stores this property does not exist),* UNKNOWN for other errors.* @return value average battery current, or 0 if not successful.*/getCurrentAverage() generates (Result result, int32_t value);/*** Get remaining battery capacity percentage of total capacity* (with no fractional part).* 获取电池剩余容量占总容量的百分比(无小数部分)。* @return result SUCCESS if successful,* NOT_SUPPORTED if this property is not supported* (e.g. the file that stores this property does not exist),* UNKNOWN for other errors.* @return value remaining battery capacity, or 0 if not successful.*/getCapacity() generates (Result result, int32_t value);/*** Get battery remaining energy in nanowatt-hours.* 以毫瓦时计算电池剩余能量。** @return result SUCCESS if successful,* NOT_SUPPORTED if this property is not supported,* UNKNOWN for other errors.* @return value remaining energy, or 0 if not successful.*/getEnergyCounter() generates (Result result, int64_t value);/*** Get battery charge status.* 获取电池充电状态。** @return result SUCCESS if successful,* NOT_SUPPORTED if this property is not supported* (e.g. the file that stores this property does not exist),* UNKNOWN other errors.* @return value charge status, or UNKNOWN if not successful.*/getChargeStatus() generates (Result result, BatteryStatus value);/*** Get storage info.* 获取存储信息。** @return result SUCCESS if successful,* NOT_SUPPORTED if this property is not supported,* UNKNOWN other errors.* @return value vector of StorageInfo structs, to be ignored if result is not* SUCCESS.*/getStorageInfo() generates (Result result, vec<StorageInfo> value);/*** Gets disk statistics (number of reads/writes processed, number of I/O* operations in flight etc).* 获取磁盘统计信息(已处理的读/写次数、正在处理的I/O操作次数等)。** @return result SUCCESS if successful,* NOT_SUPPORTED if this property is not supported,* UNKNOWN other errors.* @return value vector of disk statistics, to be ignored if result is not SUCCESS.* The mapping is index 0->sda, 1->sdb and so on.*/getDiskStats() generates (Result result, vec<DiskStats> value);/*** Get Health Information.* 获得健康信息。** @return result SUCCESS if successful,* NOT_SUPPORTED if this API is not supported,* UNKNOWN for other errors.* @return value Health information, to be ignored if result is not* SUCCESS.*/getHealthInfo() generates (Result result, @2.0::HealthInfo value);
};
hardware/interfaces/health/2.0/IHealthInfoCallback.hal
package android.hardware.health@2.0;/*** IHealthInfoCallback is the callback interface to* {@link IHealthInfoBus.registerCallback}.* IHealthInfoCallback 是 IHealthInfoBus.registerCallback 的回调接口*/
interface IHealthInfoCallback {/*** An implementation of IHealthInfoBus must call healthInfoChanged on all* registered callbacks after health info changes.* IHealthInfoBus的实现必须在运行状况信息更改后,* 对所有注册回调函数调用healthInfoChanged。* @param info the updated HealthInfo*/oneway healthInfoChanged(HealthInfo info);
};
hardware/interfaces/health/2.0/types.hal
package android.hardware.health@2.0;import @1.0::HealthInfo;
import @1.0::Result;/*** Status values for HAL methods.* HAL方法的状态值。*/
enum Result : @1.0::Result {NOT_FOUND,CALLBACK_DIED,
};/** Identification attributes for a storage device.* 存储设备的标识属性。*/
struct StorageAttribute {/*** Set to true if internal storage* 如果是内部存储则设置为 true*/bool isInternal;/*** Set to true if this is the boot device.* 如果这是启动设备,则设置为true。*/bool isBootDevice;/*** Name of the storage device.* 存储设备的名称。*/string name;
};/** Information on storage device including life time estimates, end of life* information and other attributes.* 存储设备上的信息,包括寿命估计、寿命结束信息和其他属性。*/
struct StorageInfo {/*** Attributes of the storage device whose info is contained by the struct.* 存储设备的属性。*/StorageAttribute attr;/*** pre-eol (end of life) information. Follows JEDEC standard No.84-B50.* 生命结束前的信息。符合JEDEC 标准 No.84-B50*/uint16_t eol;/*** device life time estimation (type A). Follows JEDEC standard No.84-B50.* 器件寿命估算(A型)。符合JEDEC 标准 No.84-B50*/uint16_t lifetimeA;/*** device life time estimation (type B). Follows JEDEC standard No.84-B50.* 器件寿命估算(B型)。符合JEDEC 标准 No.84-B50*/uint16_t lifetimeB;/*** version string* 版本字符串*/string version;
};
/** Disk statistics since boot.* 启动后的磁盘统计信息。*/
struct DiskStats {/*** Number of reads processed.* 处理的读次数。*/uint64_t reads;/*** number of read I/Os merged with in-queue I/Os.* 与队列 I/O 合并的读 I/O 数。*/uint64_t readMerges;/*** number of sectors read.* 读取扇区数。*/uint64_t readSectors;/*** total wait time for read requests.* 读请求的总等待时间。*/uint64_t readTicks;/*** number of writes processed.* 处理的写次数。*/uint64_t writes;/*** number of writes merged with in-queue I/Os.* 与队列 I/O 合并的写数。*/uint64_t writeMerges;/*** number of sectors written.* 写入的扇区数。*/uint64_t writeSectors;/*** total wait time for write requests.* 写请求的总等待时间。*/uint64_t writeTicks;/*** number of I/Os currently in flight.* 正在处理的 I/O 数量。*/uint64_t ioInFlight;/*** total time this block device has been active.* 此块设备处于活跃状态的总时间。*/uint64_t ioTicks;/*** total wait time for all requests.* 所有请求的总等待时间。*/uint64_t ioInQueue;/*** Attributes of the memory device.* 内存设备的属性。*/StorageAttribute attr;
};/*** Combined Health Information.* 综合健康信息。*/
struct HealthInfo {/*** V1.0 HealthInfo.* If a member is unsupported, it is filled with:* 如果一个成员不受支持,它将被填充:* - 0 (for integers);* - false (for booleans);* - empty string (for strings);* - UNKNOWN (for BatteryStatus and BatteryHealth).*/@1.0::HealthInfo legacy;/*** Average battery current in uA. Will be 0 if unsupported.* 平均电池电流(uA)。如果不支持,则为 0。*/int32_t batteryCurrentAverage;/*** Disk Statistics. Will be an empty vector if unsupported.* 磁盘数据。如果不支持,则为空向量。*/vec<DiskStats> diskStats;/*** Information on storage devices. Will be an empty vector if* unsupported.* 存储设备信息。如果不支持,则为空向量。*/vec<StorageInfo> storageInfos;
};
health@2.0 HAL 部分使用了 health@1.0 HAL 中的定义。
现在来分析 health/2.0/default 内的实现,这些为厂商实现 HAL 2.0 提供了一些便利。
cc_defaults 模块可以被其他模块引用。android.hardware.health@2.0-impl_defaults 这个模块还使用到了静态库 libbatterymonitor 和 android.hardware.health@1.0-convert(参见 【Android 10 源码】healthd 模块 HAL 1.0 分析)。
cc_library_static 这个声明毫无疑问是编译静态库的,android.hardware.health@2.0-impl 库的作用是用于实现 health HAL 的 helper library。建议使用 health 服务或直通(passthrough) HAL 链接到此库。
cc_library_shared 当然是编译动态库的,android.hardware.health@2.0-impl-default 库的作用是用于 recovery 的默认直通(passthrough)实现。供应商可以实现 android.hardware.health@2.0-impl-recovery- 定制 HAL 在 recovery 中的行为。实现不会启动 uevent 循环进行轮询。
hardware/interfaces/health/2.0/default/Android.bp
cc_defaults {name: "android.hardware.health@2.0-impl_defaults",recovery_available: true,cflags: ["-Wall","-Werror",],shared_libs: ["libbase","libhidlbase","libhidltransport","libhwbinder","liblog","libutils","libcutils","android.hardware.health@2.0",],static_libs: ["libbatterymonitor","android.hardware.health@1.0-convert",],
}// Helper library for implementing health HAL. It is recommended that a health
// service or passthrough HAL link to this library.
cc_library_static {name: "android.hardware.health@2.0-impl",defaults: ["android.hardware.health@2.0-impl_defaults"],vendor_available: true,srcs: ["Health.cpp","healthd_common.cpp",],export_include_dirs: ["include"],
}// Default passthrough implementation for recovery. Vendors can implement
// android.hardware.health@2.0-impl-recovery-<device> to customize the behavior
// of the HAL in recovery.
// The implementation does NOT start the uevent loop for polling.
cc_library_shared {name: "android.hardware.health@2.0-impl-default",defaults: ["android.hardware.health@2.0-impl_defaults"],recovery_available: true,relative_install_path: "hw",static_libs: ["android.hardware.health@2.0-impl","libhealthstoragedefault",],srcs: ["HealthImplDefault.cpp",],
}
libbatterymonitor 主要使用了 BatteryMonitor.cpp 编译。
system/core/healthd/Android.bp
cc_library_static {name: "libbatterymonitor",srcs: ["BatteryMonitor.cpp"],cflags: ["-Wall", "-Werror"],vendor_available: true,recovery_available: true,export_include_dirs: ["include"],shared_libs: ["libutils","libbase",],header_libs: ["libhealthd_headers"],export_header_lib_headers: ["libhealthd_headers"],
}
从构造器开始阅读,构造器内部调用了 initBatteryProperties(…) 方法。initBatteryProperties(…) 方法初始化了 BatteryProperties 结构体。
BatteryMonitor::init(…) 主要流程:
- 打开 /sys/class/power_supply 路径,遍历子文件夹,在子文件下查找文件名为 type 的文件,type 文件内的字符串表征供电类型,并将非电池供电类型的子文件夹名称在其下存在 online 文件时,写入充电器名列表(mChargerNames)中;
下面的图片都是使用 NANO-PC T4 截取的,主控芯片采用的 RK3399,Android 10 系统。
2. 当为电池供电类型时,mBatteryDevicePresent 置为 true,将各种 path 赋值给 healthd_config 结构体的相应字段(包括 status、health、present、capacity、voltage_now 等);
3. 非电池供电时,也就是插上充电适配器修改 healthd_config 结构体中的 periodic_chores_interval_fast 和 periodic_chores_interval_slow 都为 -1,否则进入判断电池供电的相应路径分支,如果路径为空打印相应 LOG。
BatteryMonitor::update(void) 主要流程其实很简单,就是从各种文件节点中读出相应的值,然后再更新到 BatteryProperties 结构。
system/core/healthd/BatteryMonitor.cpp
struct sysfsStringEnumMap {const char* s;int val;
};static int mapSysfsString(const char* str,struct sysfsStringEnumMap map[]) {for (int i = 0; map[i].s; i++)if (!strcmp(str, map[i].s))return map[i].val;return -1;
}static void initBatteryProperties(BatteryProperties* props) {props->chargerAcOnline = false;props->chargerUsbOnline = false;props->chargerWirelessOnline = false;props->maxChargingCurrent = 0;props->maxChargingVoltage = 0;props->batteryStatus = BATTERY_STATUS_UNKNOWN;props->batteryHealth = BATTERY_HEALTH_UNKNOWN;props->batteryPresent = false;props->batteryLevel = 0;props->batteryVoltage = 0;props->batteryTemperature = 0;props->batteryCurrent = 0;props->batteryCycleCount = 0;props->batteryFullCharge = 0;props->batteryChargeCounter = 0;props->batteryTechnology.clear();
}BatteryMonitor::BatteryMonitor(): mHealthdConfig(nullptr),mBatteryDevicePresent(false),mBatteryFixedCapacity(0),mBatteryFixedTemperature(0) {initBatteryProperties(&props);
}struct BatteryProperties getBatteryProperties(BatteryMonitor* batteryMonitor) {return batteryMonitor->props;
}int BatteryMonitor::getBatteryStatus(const char* status) {int ret;struct sysfsStringEnumMap batteryStatusMap[] = {{ "Unknown", BATTERY_STATUS_UNKNOWN },{ "Charging", BATTERY_STATUS_CHARGING },{ "Discharging", BATTERY_STATUS_DISCHARGING },{ "Not charging", BATTERY_STATUS_NOT_CHARGING },{ "Full", BATTERY_STATUS_FULL },{ NULL, 0 },};ret = mapSysfsString(status, batteryStatusMap);if (ret < 0) {KLOG_WARNING(LOG_TAG, "Unknown battery status '%s'\n", status);ret = BATTERY_STATUS_UNKNOWN;}return ret;
}int BatteryMonitor::getBatteryHealth(const char* status) {int ret;struct sysfsStringEnumMap batteryHealthMap[] = {{ "Unknown", BATTERY_HEALTH_UNKNOWN },{ "Good", BATTERY_HEALTH_GOOD },{ "Overheat", BATTERY_HEALTH_OVERHEAT },{ "Dead", BATTERY_HEALTH_DEAD },{ "Over voltage", BATTERY_HEALTH_OVER_VOLTAGE },{ "Unspecified failure", BATTERY_HEALTH_UNSPECIFIED_FAILURE },{ "Cold", BATTERY_HEALTH_COLD },// battery health values from JEITA spec{ "Warm", BATTERY_HEALTH_GOOD },{ "Cool", BATTERY_HEALTH_GOOD },{ "Hot", BATTERY_HEALTH_OVERHEAT },{ NULL, 0 },};ret = mapSysfsString(status, batteryHealthMap);if (ret < 0) {KLOG_WARNING(LOG_TAG, "Unknown battery health '%s'\n", status);ret = BATTERY_HEALTH_UNKNOWN;}return ret;
}int BatteryMonitor::readFromFile(const String8& path, std::string* buf) {if (android::base::ReadFileToString(path.c_str(), buf)) {*buf = android::base::Trim(*buf);}return buf->length();
}BatteryMonitor::PowerSupplyType BatteryMonitor::readPowerSupplyType(const String8& path) {std::string buf;int ret;struct sysfsStringEnumMap supplyTypeMap[] = {{ "Unknown", ANDROID_POWER_SUPPLY_TYPE_UNKNOWN },{ "Battery", ANDROID_POWER_SUPPLY_TYPE_BATTERY },{ "UPS", ANDROID_POWER_SUPPLY_TYPE_AC },{ "Mains", ANDROID_POWER_SUPPLY_TYPE_AC },{ "USB", ANDROID_POWER_SUPPLY_TYPE_USB },{ "USB_DCP", ANDROID_POWER_SUPPLY_TYPE_AC },{ "USB_HVDCP", ANDROID_POWER_SUPPLY_TYPE_AC },{ "USB_CDP", ANDROID_POWER_SUPPLY_TYPE_AC },{ "USB_ACA", ANDROID_POWER_SUPPLY_TYPE_AC },{ "USB_C", ANDROID_POWER_SUPPLY_TYPE_AC },{ "USB_PD", ANDROID_POWER_SUPPLY_TYPE_AC },{ "USB_PD_DRP", ANDROID_POWER_SUPPLY_TYPE_USB },{ "Wireless", ANDROID_POWER_SUPPLY_TYPE_WIRELESS },{ NULL, 0 },};if (readFromFile(path, &buf) <= 0)return ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;ret = mapSysfsString(buf.c_str(), supplyTypeMap);if (ret < 0) {KLOG_WARNING(LOG_TAG, "Unknown power supply type '%s'\n", buf.c_str());ret = ANDROID_POWER_SUPPLY_TYPE_UNKNOWN;}return static_cast<BatteryMonitor::PowerSupplyType>(ret);
}bool BatteryMonitor::getBooleanField(const String8& path) {std::string buf;bool value = false;if (readFromFile(path, &buf) > 0)if (buf[0] != '0')value = true;return value;
}int BatteryMonitor::getIntField(const String8& path) {std::string buf;int value = 0;if (readFromFile(path, &buf) > 0)android::base::ParseInt(buf, &value);return value;
}bool BatteryMonitor::update(void) {bool logthis;initBatteryProperties(&props);if (!mHealthdConfig->batteryPresentPath.isEmpty())props.batteryPresent = getBooleanField(mHealthdConfig->batteryPresentPath);elseprops.batteryPresent = mBatteryDevicePresent;props.batteryLevel = mBatteryFixedCapacity ?mBatteryFixedCapacity :getIntField(mHealthdConfig->batteryCapacityPath);props.batteryVoltage = getIntField(mHealthdConfig->batteryVoltagePath) / 1000;if (!mHealthdConfig->batteryCurrentNowPath.isEmpty())props.batteryCurrent = getIntField(mHealthdConfig->batteryCurrentNowPath) / 1000;if (!mHealthdConfig->batteryFullChargePath.isEmpty())props.batteryFullCharge = getIntField(mHealthdConfig->batteryFullChargePath);if (!mHealthdConfig->batteryCycleCountPath.isEmpty())props.batteryCycleCount = getIntField(mHealthdConfig->batteryCycleCountPath);if (!mHealthdConfig->batteryChargeCounterPath.isEmpty())props.batteryChargeCounter = getIntField(mHealthdConfig->batteryChargeCounterPath);props.batteryTemperature = mBatteryFixedTemperature ?mBatteryFixedTemperature :getIntField(mHealthdConfig->batteryTemperaturePath);std::string buf;if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)props.batteryStatus = getBatteryStatus(buf.c_str());if (readFromFile(mHealthdConfig->batteryHealthPath, &buf) > 0)props.batteryHealth = getBatteryHealth(buf.c_str());if (readFromFile(mHealthdConfig->batteryTechnologyPath, &buf) > 0)props.batteryTechnology = String8(buf.c_str());double MaxPower = 0;for (size_t i = 0; i < mChargerNames.size(); i++) {String8 path;path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH,mChargerNames[i].string());if (getIntField(path)) {path.clear();path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH,mChargerNames[i].string());switch(readPowerSupplyType(path)) {case ANDROID_POWER_SUPPLY_TYPE_AC:props.chargerAcOnline = true;break;case ANDROID_POWER_SUPPLY_TYPE_USB:props.chargerUsbOnline = true;break;case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:props.chargerWirelessOnline = true;break;default:KLOG_WARNING(LOG_TAG, "%s: Unknown power supply type\n",mChargerNames[i].string());}path.clear();path.appendFormat("%s/%s/current_max", POWER_SUPPLY_SYSFS_PATH,mChargerNames[i].string());int ChargingCurrent =(access(path.string(), R_OK) == 0) ? getIntField(path) : 0;path.clear();path.appendFormat("%s/%s/voltage_max", POWER_SUPPLY_SYSFS_PATH,mChargerNames[i].string());int ChargingVoltage =(access(path.string(), R_OK) == 0) ? getIntField(path) :DEFAULT_VBUS_VOLTAGE;double power = ((double)ChargingCurrent / MILLION) *((double)ChargingVoltage / MILLION);if (MaxPower < power) {props.maxChargingCurrent = ChargingCurrent;props.maxChargingVoltage = ChargingVoltage;MaxPower = power;}}}logthis = !healthd_board_battery_update(&props);if (logthis) {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.batteryVoltage,props.batteryTemperature < 0 ? "-" : "",abs(props.batteryTemperature / 10),abs(props.batteryTemperature % 10), props.batteryHealth,props.batteryStatus);len = strlen(dmesgline);if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {len += snprintf(dmesgline + len, sizeof(dmesgline) - len," c=%d", props.batteryCurrent);}if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {len += snprintf(dmesgline + len, sizeof(dmesgline) - len," fc=%d", props.batteryFullCharge);}if (!mHealthdConfig->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",props.chargerAcOnline ? "a" : "",props.chargerUsbOnline ? "u" : "",props.chargerWirelessOnline ? "w" : "");KLOG_WARNING(LOG_TAG, "%s\n", dmesgline);}healthd_mode_ops->battery_update(&props);return props.chargerAcOnline | props.chargerUsbOnline |props.chargerWirelessOnline;
}int BatteryMonitor::getChargeStatus() {int result = BATTERY_STATUS_UNKNOWN;if (!mHealthdConfig->batteryStatusPath.isEmpty()) {std::string buf;if (readFromFile(mHealthdConfig->batteryStatusPath, &buf) > 0)result = getBatteryStatus(buf.c_str());}return result;
}status_t BatteryMonitor::getProperty(int id, struct BatteryProperty *val) {status_t ret = BAD_VALUE;std::string buf;val->valueInt64 = LONG_MIN;switch(id) {case BATTERY_PROP_CHARGE_COUNTER:if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {val->valueInt64 =getIntField(mHealthdConfig->batteryChargeCounterPath);ret = OK;} else {ret = NAME_NOT_FOUND;}break;case BATTERY_PROP_CURRENT_NOW:if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {val->valueInt64 =getIntField(mHealthdConfig->batteryCurrentNowPath);ret = OK;} else {ret = NAME_NOT_FOUND;}break;case BATTERY_PROP_CURRENT_AVG:if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {val->valueInt64 =getIntField(mHealthdConfig->batteryCurrentAvgPath);ret = OK;} else {ret = NAME_NOT_FOUND;}break;case BATTERY_PROP_CAPACITY:if (!mHealthdConfig->batteryCapacityPath.isEmpty()) {val->valueInt64 =getIntField(mHealthdConfig->batteryCapacityPath);ret = OK;} else {ret = NAME_NOT_FOUND;}break;case BATTERY_PROP_ENERGY_COUNTER:if (mHealthdConfig->energyCounter) {ret = mHealthdConfig->energyCounter(&val->valueInt64);} else {ret = NAME_NOT_FOUND;}break;case BATTERY_PROP_BATTERY_STATUS:val->valueInt64 = getChargeStatus();ret = OK;break;default:break;}return ret;
}void BatteryMonitor::dumpState(int fd) {int v;char vs[128];snprintf(vs, sizeof(vs), "ac: %d usb: %d wireless: %d current_max: %d voltage_max: %d\n",props.chargerAcOnline, props.chargerUsbOnline,props.chargerWirelessOnline, props.maxChargingCurrent,props.maxChargingVoltage);write(fd, vs, strlen(vs));snprintf(vs, sizeof(vs), "status: %d health: %d present: %d\n",props.batteryStatus, props.batteryHealth, props.batteryPresent);write(fd, vs, strlen(vs));snprintf(vs, sizeof(vs), "level: %d voltage: %d temp: %d\n",props.batteryLevel, props.batteryVoltage,props.batteryTemperature);write(fd, vs, strlen(vs));if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {v = getIntField(mHealthdConfig->batteryCurrentNowPath);snprintf(vs, sizeof(vs), "current now: %d\n", v);write(fd, vs, strlen(vs));}if (!mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {v = getIntField(mHealthdConfig->batteryCurrentAvgPath);snprintf(vs, sizeof(vs), "current avg: %d\n", v);write(fd, vs, strlen(vs));}if (!mHealthdConfig->batteryChargeCounterPath.isEmpty()) {v = getIntField(mHealthdConfig->batteryChargeCounterPath);snprintf(vs, sizeof(vs), "charge counter: %d\n", v);write(fd, vs, strlen(vs));}if (!mHealthdConfig->batteryCurrentNowPath.isEmpty()) {snprintf(vs, sizeof(vs), "current now: %d\n", props.batteryCurrent);write(fd, vs, strlen(vs));}if (!mHealthdConfig->batteryCycleCountPath.isEmpty()) {snprintf(vs, sizeof(vs), "cycle count: %d\n", props.batteryCycleCount);write(fd, vs, strlen(vs));}if (!mHealthdConfig->batteryFullChargePath.isEmpty()) {snprintf(vs, sizeof(vs), "Full charge: %d\n", props.batteryFullCharge);write(fd, vs, strlen(vs));}
}void BatteryMonitor::init(struct healthd_config *hc) {String8 path;char pval[PROPERTY_VALUE_MAX];mHealthdConfig = hc;std::unique_ptr<DIR, decltype(&closedir)> dir(opendir(POWER_SUPPLY_SYSFS_PATH), closedir);if (dir == NULL) {KLOG_ERROR(LOG_TAG, "Could not open %s\n", POWER_SUPPLY_SYSFS_PATH);} else {struct dirent* entry;while ((entry = readdir(dir.get()))) {const char* name = entry->d_name;std::vector<String8>::iterator itIgnoreName;if (!strcmp(name, ".") || !strcmp(name, ".."))continue;itIgnoreName = find(hc->ignorePowerSupplyNames.begin(),hc->ignorePowerSupplyNames.end(), String8(name));if (itIgnoreName != hc->ignorePowerSupplyNames.end())continue;// Look for "type" file in each subdirectorypath.clear();path.appendFormat("%s/%s/type", POWER_SUPPLY_SYSFS_PATH, name);switch(readPowerSupplyType(path)) {case ANDROID_POWER_SUPPLY_TYPE_AC:case ANDROID_POWER_SUPPLY_TYPE_USB:case ANDROID_POWER_SUPPLY_TYPE_WIRELESS:path.clear();path.appendFormat("%s/%s/online", POWER_SUPPLY_SYSFS_PATH, name);if (access(path.string(), R_OK) == 0)mChargerNames.add(String8(name));break;case ANDROID_POWER_SUPPLY_TYPE_BATTERY:mBatteryDevicePresent = true;if (mHealthdConfig->batteryStatusPath.isEmpty()) {path.clear();path.appendFormat("%s/%s/status", POWER_SUPPLY_SYSFS_PATH,name);if (access(path, R_OK) == 0)mHealthdConfig->batteryStatusPath = path;}if (mHealthdConfig->batteryHealthPath.isEmpty()) {path.clear();path.appendFormat("%s/%s/health", POWER_SUPPLY_SYSFS_PATH,name);if (access(path, R_OK) == 0)mHealthdConfig->batteryHealthPath = path;}if (mHealthdConfig->batteryPresentPath.isEmpty()) {path.clear();path.appendFormat("%s/%s/present", POWER_SUPPLY_SYSFS_PATH,name);if (access(path, R_OK) == 0)mHealthdConfig->batteryPresentPath = path;}if (mHealthdConfig->batteryCapacityPath.isEmpty()) {path.clear();path.appendFormat("%s/%s/capacity", POWER_SUPPLY_SYSFS_PATH,name);if (access(path, R_OK) == 0)mHealthdConfig->batteryCapacityPath = path;}if (mHealthdConfig->batteryVoltagePath.isEmpty()) {path.clear();path.appendFormat("%s/%s/voltage_now",POWER_SUPPLY_SYSFS_PATH, name);if (access(path, R_OK) == 0) {mHealthdConfig->batteryVoltagePath = path;}}if (mHealthdConfig->batteryFullChargePath.isEmpty()) {path.clear();path.appendFormat("%s/%s/charge_full",POWER_SUPPLY_SYSFS_PATH, name);if (access(path, R_OK) == 0)mHealthdConfig->batteryFullChargePath = path;}if (mHealthdConfig->batteryCurrentNowPath.isEmpty()) {path.clear();path.appendFormat("%s/%s/current_now",POWER_SUPPLY_SYSFS_PATH, name);if (access(path, R_OK) == 0)mHealthdConfig->batteryCurrentNowPath = path;}if (mHealthdConfig->batteryCycleCountPath.isEmpty()) {path.clear();path.appendFormat("%s/%s/cycle_count",POWER_SUPPLY_SYSFS_PATH, name);if (access(path, R_OK) == 0)mHealthdConfig->batteryCycleCountPath = path;}if (mHealthdConfig->batteryCurrentAvgPath.isEmpty()) {path.clear();path.appendFormat("%s/%s/current_avg",POWER_SUPPLY_SYSFS_PATH, name);if (access(path, R_OK) == 0)mHealthdConfig->batteryCurrentAvgPath = path;}if (mHealthdConfig->batteryChargeCounterPath.isEmpty()) {path.clear();path.appendFormat("%s/%s/charge_counter",POWER_SUPPLY_SYSFS_PATH, name);if (access(path, R_OK) == 0)mHealthdConfig->batteryChargeCounterPath = path;}if (mHealthdConfig->batteryTemperaturePath.isEmpty()) {path.clear();path.appendFormat("%s/%s/temp", POWER_SUPPLY_SYSFS_PATH,name);if (access(path, R_OK) == 0) {mHealthdConfig->batteryTemperaturePath = path;}}if (mHealthdConfig->batteryTechnologyPath.isEmpty()) {path.clear();path.appendFormat("%s/%s/technology",POWER_SUPPLY_SYSFS_PATH, name);if (access(path, R_OK) == 0)mHealthdConfig->batteryTechnologyPath = path;}break;case ANDROID_POWER_SUPPLY_TYPE_UNKNOWN:break;}}}// Typically the case for devices which do not have a battery and// and are always plugged into AC mains.if (!mBatteryDevicePresent) {KLOG_WARNING(LOG_TAG, "No battery devices found\n");hc->periodic_chores_interval_fast = -1;hc->periodic_chores_interval_slow = -1;} else {if (mHealthdConfig->batteryStatusPath.isEmpty())KLOG_WARNING(LOG_TAG, "BatteryStatusPath not found\n");if (mHealthdConfig->batteryHealthPath.isEmpty())KLOG_WARNING(LOG_TAG, "BatteryHealthPath not found\n");if (mHealthdConfig->batteryPresentPath.isEmpty())KLOG_WARNING(LOG_TAG, "BatteryPresentPath not found\n");if (mHealthdConfig->batteryCapacityPath.isEmpty())KLOG_WARNING(LOG_TAG, "BatteryCapacityPath not found\n");if (mHealthdConfig->batteryVoltagePath.isEmpty())KLOG_WARNING(LOG_TAG, "BatteryVoltagePath not found\n");if (mHealthdConfig->batteryTemperaturePath.isEmpty())KLOG_WARNING(LOG_TAG, "BatteryTemperaturePath not found\n");if (mHealthdConfig->batteryTechnologyPath.isEmpty())KLOG_WARNING(LOG_TAG, "BatteryTechnologyPath not found\n");if (mHealthdConfig->batteryCurrentNowPath.isEmpty())KLOG_WARNING(LOG_TAG, "BatteryCurrentNowPath not found\n");if (mHealthdConfig->batteryFullChargePath.isEmpty())KLOG_WARNING(LOG_TAG, "BatteryFullChargePath not found\n");if (mHealthdConfig->batteryCycleCountPath.isEmpty())KLOG_WARNING(LOG_TAG, "BatteryCycleCountPath not found\n");}if (property_get("ro.boot.fake_battery", pval, NULL) > 0&& strtol(pval, NULL, 10) != 0) {mBatteryFixedCapacity = FAKE_BATTERY_CAPACITY;mBatteryFixedTemperature = FAKE_BATTERY_TEMPERATURE;}
}
现在重点来看 android.hardware.health@2.0-impl 这个静态库,它使用了源文件 Health.cpp 和 healthd_common.cpp。
Health.cpp 内实现了 HAL2 接口,只看电池相关的接口不难发现主要使用了 BatteryMonitor 类,将实际实现都委托给了 BatteryMonitor。这部分是为了兼容旧版本安卓预留的。
hardware/interfaces/health/2.0/default/Health.cpp
#define LOG_TAG "android.hardware.health@2.0-impl"
#include <android-base/logging.h>#include <android-base/file.h>
#include <health2/Health.h>#include <hal_conversion.h>
#include <hidl/HidlTransportSupport.h>extern void healthd_battery_update_internal(bool);namespace android {namespace hardware {namespace health {namespace V2_0 {namespace implementation {sp<Health> Health::instance_;Health::Health(struct healthd_config* c) {// TODO(b/69268160): remove when libhealthd is removed.healthd_board_init(c);battery_monitor_ = std::make_unique<BatteryMonitor>();battery_monitor_->init(c);
}// Methods from IHealth follow.
Return<Result> Health::registerCallback(const sp<IHealthInfoCallback>& callback) {if (callback == nullptr) {return Result::SUCCESS;}{std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);callbacks_.push_back(callback);// unlock}auto linkRet = callback->linkToDeath(this, 0u /* cookie */);if (!linkRet.withDefault(false)) {LOG(WARNING) << __func__ << "Cannot link to death: "<< (linkRet.isOk() ? "linkToDeath returns false" : linkRet.description());// ignore the error}return updateAndNotify(callback);
}bool Health::unregisterCallbackInternal(const sp<IBase>& callback) {if (callback == nullptr) return false;bool removed = false;std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);for (auto it = callbacks_.begin(); it != callbacks_.end();) {if (interfacesEqual(*it, callback)) {it = callbacks_.erase(it);removed = true;} else {++it;}}(void)callback->unlinkToDeath(this).isOk(); // ignore errorsreturn removed;
}Return<Result> Health::unregisterCallback(const sp<IHealthInfoCallback>& callback) {return unregisterCallbackInternal(callback) ? Result::SUCCESS : Result::NOT_FOUND;
}template <typename T>
void getProperty(const std::unique_ptr<BatteryMonitor>& monitor, int id, T defaultValue,const std::function<void(Result, T)>& callback) {struct BatteryProperty prop;T ret = defaultValue;Result result = Result::SUCCESS;status_t err = monitor->getProperty(static_cast<int>(id), &prop);if (err != OK) {LOG(DEBUG) << "getProperty(" << id << ")"<< " fails: (" << err << ") " << strerror(-err);} else {ret = static_cast<T>(prop.valueInt64);}switch (err) {case OK:result = Result::SUCCESS;break;case NAME_NOT_FOUND:result = Result::NOT_SUPPORTED;break;default:result = Result::UNKNOWN;break;}callback(result, static_cast<T>(ret));
}Return<void> Health::getChargeCounter(getChargeCounter_cb _hidl_cb) {getProperty<int32_t>(battery_monitor_, BATTERY_PROP_CHARGE_COUNTER, 0, _hidl_cb);return Void();
}Return<void> Health::getCurrentNow(getCurrentNow_cb _hidl_cb) {getProperty<int32_t>(battery_monitor_, BATTERY_PROP_CURRENT_NOW, 0, _hidl_cb);return Void();
}Return<void> Health::getCurrentAverage(getCurrentAverage_cb _hidl_cb) {getProperty<int32_t>(battery_monitor_, BATTERY_PROP_CURRENT_AVG, 0, _hidl_cb);return Void();
}Return<void> Health::getCapacity(getCapacity_cb _hidl_cb) {getProperty<int32_t>(battery_monitor_, BATTERY_PROP_CAPACITY, 0, _hidl_cb);return Void();
}Return<void> Health::getEnergyCounter(getEnergyCounter_cb _hidl_cb) {getProperty<int64_t>(battery_monitor_, BATTERY_PROP_ENERGY_COUNTER, 0, _hidl_cb);return Void();
}Return<void> Health::getChargeStatus(getChargeStatus_cb _hidl_cb) {getProperty(battery_monitor_, BATTERY_PROP_BATTERY_STATUS, BatteryStatus::UNKNOWN, _hidl_cb);return Void();
}Return<Result> Health::update() {if (!healthd_mode_ops || !healthd_mode_ops->battery_update) {LOG(WARNING) << "health@2.0: update: not initialized. "<< "update() should not be called in charger";return Result::UNKNOWN;}// Retrieve all information and call healthd_mode_ops->battery_update, which calls// notifyListeners.bool chargerOnline = battery_monitor_->update();// adjust uevent / wakealarm periodshealthd_battery_update_internal(chargerOnline);return Result::SUCCESS;
}Return<Result> Health::updateAndNotify(const sp<IHealthInfoCallback>& callback) {std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);std::vector<sp<IHealthInfoCallback>> storedCallbacks{std::move(callbacks_)};callbacks_.clear();if (callback != nullptr) {callbacks_.push_back(callback);}Return<Result> result = update();callbacks_ = std::move(storedCallbacks);return result;
}void Health::notifyListeners(HealthInfo* healthInfo) {std::vector<StorageInfo> info;get_storage_info(info);std::vector<DiskStats> stats;get_disk_stats(stats);int32_t currentAvg = 0;struct BatteryProperty prop;status_t ret = battery_monitor_->getProperty(BATTERY_PROP_CURRENT_AVG, &prop);if (ret == OK) {currentAvg = static_cast<int32_t>(prop.valueInt64);}healthInfo->batteryCurrentAverage = currentAvg;healthInfo->diskStats = stats;healthInfo->storageInfos = info;std::lock_guard<decltype(callbacks_lock_)> lock(callbacks_lock_);for (auto it = callbacks_.begin(); it != callbacks_.end();) {auto ret = (*it)->healthInfoChanged(*healthInfo);if (!ret.isOk() && ret.isDeadObject()) {it = callbacks_.erase(it);} else {++it;}}
}Return<void> Health::debug(const hidl_handle& handle, const hidl_vec<hidl_string>&) {if (handle != nullptr && handle->numFds >= 1) {int fd = handle->data[0];battery_monitor_->dumpState(fd);getHealthInfo([fd](auto res, const auto& info) {android::base::WriteStringToFd("\ngetHealthInfo -> ", fd);if (res == Result::SUCCESS) {android::base::WriteStringToFd(toString(info), fd);} else {android::base::WriteStringToFd(toString(res), fd);}android::base::WriteStringToFd("\n", fd);});fsync(fd);}return Void();
}Return<void> Health::getStorageInfo(getStorageInfo_cb _hidl_cb) {std::vector<struct StorageInfo> info;get_storage_info(info);hidl_vec<struct StorageInfo> info_vec(info);if (!info.size()) {_hidl_cb(Result::NOT_SUPPORTED, info_vec);} else {_hidl_cb(Result::SUCCESS, info_vec);}return Void();
}Return<void> Health::getDiskStats(getDiskStats_cb _hidl_cb) {std::vector<struct DiskStats> stats;get_disk_stats(stats);hidl_vec<struct DiskStats> stats_vec(stats);if (!stats.size()) {_hidl_cb(Result::NOT_SUPPORTED, stats_vec);} else {_hidl_cb(Result::SUCCESS, stats_vec);}return Void();
}Return<void> Health::getHealthInfo(getHealthInfo_cb _hidl_cb) {using android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;updateAndNotify(nullptr);struct android::BatteryProperties p = getBatteryProperties(battery_monitor_.get());V1_0::HealthInfo batteryInfo;convertToHealthInfo(&p, batteryInfo);std::vector<StorageInfo> info;get_storage_info(info);std::vector<DiskStats> stats;get_disk_stats(stats);int32_t currentAvg = 0;struct BatteryProperty prop;status_t ret = battery_monitor_->getProperty(BATTERY_PROP_CURRENT_AVG, &prop);if (ret == OK) {currentAvg = static_cast<int32_t>(prop.valueInt64);}V2_0::HealthInfo healthInfo = {};healthInfo.legacy = std::move(batteryInfo);healthInfo.batteryCurrentAverage = currentAvg;healthInfo.diskStats = stats;healthInfo.storageInfos = info;_hidl_cb(Result::SUCCESS, healthInfo);return Void();
}void Health::serviceDied(uint64_t /* cookie */, const wp<IBase>& who) {(void)unregisterCallbackInternal(who.promote());
}sp<IHealth> Health::initInstance(struct healthd_config* c) {if (instance_ == nullptr) {instance_ = new Health(c);}return instance_;
}sp<Health> Health::getImplementation() {CHECK(instance_ != nullptr);return instance_;
}} // namespace implementation
} // namespace V2_0
} // namespace health
} // namespace hardware
} // namespace android
可以先从入口函数着手,也就是 healthd_main(),它首先判断 healthd_mode_ops(定义了 healthd 需要外部实现的一些函数指针接口,包括 init、preparetowait、heartbeat 和 battery_update) 这个结构体是否为空,为空直接调用 exit(1) 退出。接下来调用 healthd_init() 进行 healthd 的初始化动作,最后调动 healthd_mainloop() 进入主循环。
healthd_init() 主要流程:
- 调用 epoll_create1(…) 创建 epoll 文件描述符,并设置执行时关闭 close-on-exec(EPOLL_CLOEXEC)标志;
- 调用 healthd_mode_ops 内 init 函数指针指向的实际实现;
- 调用 wakealarm_init() 初始化闹钟唤醒;
- 调用 uevent_init() 初始化 uevent。
wakealarm_init(void) 主要流程:
- 调用 timerfd_create(…) 创建定时器文件描述符,以 clockid:CLOCK_BOOTTIME_ALARM 和 flags:TFD_NONBLOCK 作为入参。 CLOCK_BOOTTIME_ALARM 表示单调的系统级时钟,包括停留在挂起中的时间,还能唤醒挂起的系统。TFD_NONBLOCK 表示非阻塞模式。
- 调用 healthd_register_event(…) 注册闹钟事件处理相关。
- 调用 wakealarm_set_interval(…) 设置闹钟间隔。
uevent_init(void) 主要流程:
- 调用 uevent_open_socket(…) 创建 netlink socket,用于监听 uevent 事件,返回 socket 文件描述符。64 * 1024 作为 socket 接收缓存区 buffer 大小。passcred 为 true,表示允许 SCM_CREDENTIALS(发送或接收 UNIX 凭证,可以用来身份验证) 控制消息的接收。
- 调用 fcntl 设置文件描述符为非阻塞模式。
- 调用 healthd_register_event(…) 注册 uevent 事件处理相关。
healthd_register_event(…) 主要流程:
- 创建 epoll_event 结构并设置其 events 和 data.ptr 字段,events 赋值 EPOLLIN,EPOLLIN 表示对应的文件描述符可以读。当唤醒设备的事件发生时,设备驱动会保持唤醒状态,直到事件进入排队状态。为了保持设备唤醒直到事件处理完成,必须使用epoll EPOLLWAKEUP 标记。
- 调用 epoll_ctl(…) 注册 fd 到 epollfd。
- eventct 加 1,每注册成功一个 fd 就加 1,记录注册成功 fd 的总次数。
wakealarm_event(…) 主要流程:
- 调用 read(…) 获取 wakeups 变量次数(read 会把参数 fd 所指的文件传送 count 个字节到 buf 指针所指的内存中);
- 调用 periodic_chores() 处理定期事务,此处实现 periodic_chores() 内部仅仅调用了 healthd_battery_update(),而 healthd_battery_update() 内部只是委托给 Health 实现中的 update() 方法。
wakealarm_set_interval(…) 主要流程:
- 创建 itimerspec 结构,并给其 it_interval 和 it_value 字段赋值;
- 调用 timerfd_settime(…) 开启或关闭定时器,此处显然不是只运行一次的定时器。
int timerfd_settime(int tfd, int flags, const struct itimerspec *newValue, struct itimerspec *oldValue)
功能: 用于启动或关闭指定 fd 的定时器。
tfd: timerfd,由 timerfd_create 函数返回。
flags: 1 表示设置的是绝对时间;0 表示相对时间。
newValue: 指定新的超时时间,若 newValue.it_value 非 0 则启动定时器,否则关闭定时器。若 newValue.it_interval 为 0 则定时器只定时一次,否则之后每隔设定时间超时一次。
oldValue:不为 NULL 时则返回定时器这次设置之前的超时时间。
return:失败则返回-1。
struct timespec
{time_t tv_sec; //秒
long tv_nsec; //纳秒
}struct itimerspec
{struct timespec it_interval; //首次超时后,每隔 it_interval 超时一次
struct timespec it_value; //首次超时时间
}
uevent_event(…) 主要流程:
- 调用 uevent_kernel_multicast_recv(…) 接收 uevent 消息,如果获取的消息长度小于等于 0 直接退出,如果大最大长度也直接丢弃退出;
- 查找消息中是否存在 SUBSYSTEM=power_supply 字符串,存在的话表示供电系统触发的消息,此时就会调用 healthd_battery_update() 更新电池信息。
healthd_mainloop() 主要流程:
- 进入一个无限循环中;
- 创建 epoll_event 结构数组用来接收 epoll 事件;
- 第一次进入循环直接运行一次 periodic_chores() 函数;
- 调用 healthd_mode_ops->heartbeat() 处理心跳;
- 调用 healthd_mode_ops->preparetowait() 获取 epoll_wait(…) 使用的超时间隔;
- 调用 epoll_wait(…) 等待 epoll 事件;
- 如果 epoll_wait(…) 返回 -1,错误类型为 EINTR(代表方法被中断),则 continue 执行 while 循环,否则打印错误日志退出循环;
- for 循环遍历获取的 epoll 事件,并逐个调用其注册的事件处理函数,也就是 wakealarm_event(…) 和 uevent_event(…)。
healthd_battery_update_internal(…) 主要流程:
- 根据入参 charger_online 充电是否在线 bool 值,设置新的闹钟唤醒时间间隔,充电时设置为较短唤醒间隔,电池放电时设置为较长唤醒间隔。
- 当闹钟设置为较快的唤醒时间间隔,就将 epoll 超时设置为 -1(无限期阻塞);而当闹钟唤醒间隔设置为较慢时,epoll 超时间隔就设短。
hardware/interfaces/health/2.0/default/healthd_common.cpp
// Periodic chores fast interval in seconds
#define DEFAULT_PERIODIC_CHORES_INTERVAL_FAST (60 * 1)
// Periodic chores fast interval in seconds
#define DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW (60 * 10)static struct healthd_config healthd_config = {.periodic_chores_interval_fast = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST,.periodic_chores_interval_slow = DEFAULT_PERIODIC_CHORES_INTERVAL_SLOW,.batteryStatusPath = String8(String8::kEmptyString),.batteryHealthPath = String8(String8::kEmptyString),.batteryPresentPath = String8(String8::kEmptyString),.batteryCapacityPath = String8(String8::kEmptyString),.batteryVoltagePath = String8(String8::kEmptyString),.batteryTemperaturePath = String8(String8::kEmptyString),.batteryTechnologyPath = String8(String8::kEmptyString),.batteryCurrentNowPath = String8(String8::kEmptyString),.batteryCurrentAvgPath = String8(String8::kEmptyString),.batteryChargeCounterPath = String8(String8::kEmptyString),.batteryFullChargePath = String8(String8::kEmptyString),.batteryCycleCountPath = String8(String8::kEmptyString),.energyCounter = NULL,.boot_min_cap = 0,.screen_on = NULL,
};static int eventct;
static int epollfd;#define POWER_SUPPLY_SUBSYSTEM "power_supply"static int uevent_fd;
static int wakealarm_fd;// -1 for no epoll timeout
static int awake_poll_interval = -1;static int wakealarm_wake_interval = DEFAULT_PERIODIC_CHORES_INTERVAL_FAST;using ::android::hardware::health::V2_0::implementation::Health;struct healthd_mode_ops* healthd_mode_ops = nullptr;int healthd_register_event(int fd, void (*handler)(uint32_t), EventWakeup wakeup) {struct epoll_event ev;ev.events = EPOLLIN;if (wakeup == EVENT_WAKEUP_FD) ev.events |= EPOLLWAKEUP;ev.data.ptr = (void*)handler;if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) == -1) {KLOG_ERROR(LOG_TAG, "epoll_ctl failed; errno=%d\n", errno);return -1;}eventct++;return 0;
}static void wakealarm_set_interval(int interval) {struct itimerspec itval;if (wakealarm_fd == -1) return;wakealarm_wake_interval = interval;if (interval == -1) interval = 0;itval.it_interval.tv_sec = interval;itval.it_interval.tv_nsec = 0;itval.it_value.tv_sec = interval;itval.it_value.tv_nsec = 0;if (timerfd_settime(wakealarm_fd, 0, &itval, NULL) == -1)KLOG_ERROR(LOG_TAG, "wakealarm_set_interval: timerfd_settime failed\n");
}void healthd_battery_update_internal(bool charger_online) {// Fast wake interval when on charger (watch for overheat);// slow wake interval when on battery (watch for drained battery).int new_wake_interval = charger_online ? healthd_config.periodic_chores_interval_fast: healthd_config.periodic_chores_interval_slow;if (new_wake_interval != wakealarm_wake_interval) wakealarm_set_interval(new_wake_interval);// During awake periods poll at fast rate. If wake alarm is set at fast// rate then just use the alarm; if wake alarm is set at slow rate then// poll at fast rate while awake and let alarm wake up at slow rate when// asleep.if (healthd_config.periodic_chores_interval_fast == -1)awake_poll_interval = -1;elseawake_poll_interval = new_wake_interval == healthd_config.periodic_chores_interval_fast? -1: healthd_config.periodic_chores_interval_fast * 1000;
}static void healthd_battery_update(void) {Health::getImplementation()->update();
}static void periodic_chores() {healthd_battery_update();
}#define UEVENT_MSG_LEN 2048
static void uevent_event(uint32_t /*epevents*/) {char msg[UEVENT_MSG_LEN + 2];char* cp;int n;n = uevent_kernel_multicast_recv(uevent_fd, msg, UEVENT_MSG_LEN);if (n <= 0) return;if (n >= UEVENT_MSG_LEN) /* overflow -- discard */return;msg[n] = '\0';msg[n + 1] = '\0';cp = msg;while (*cp) {if (!strcmp(cp, "SUBSYSTEM=" POWER_SUPPLY_SUBSYSTEM)) {healthd_battery_update();break;}/* advance to after the next \0 */while (*cp++);}
}static void uevent_init(void) {uevent_fd = uevent_open_socket(64 * 1024, true);if (uevent_fd < 0) {KLOG_ERROR(LOG_TAG, "uevent_init: uevent_open_socket failed\n");return;}fcntl(uevent_fd, F_SETFL, O_NONBLOCK);if (healthd_register_event(uevent_fd, uevent_event, EVENT_WAKEUP_FD))KLOG_ERROR(LOG_TAG, "register for uevent events failed\n");
}static void wakealarm_event(uint32_t /*epevents*/) {unsigned long long wakeups;if (read(wakealarm_fd, &wakeups, sizeof(wakeups)) == -1) {KLOG_ERROR(LOG_TAG, "wakealarm_event: read wakealarm fd failed\n");return;}periodic_chores();
}static void wakealarm_init(void) {wakealarm_fd = timerfd_create(CLOCK_BOOTTIME_ALARM, TFD_NONBLOCK);if (wakealarm_fd == -1) {KLOG_ERROR(LOG_TAG, "wakealarm_init: timerfd_create failed\n");return;}if (healthd_register_event(wakealarm_fd, wakealarm_event, EVENT_WAKEUP_FD))KLOG_ERROR(LOG_TAG, "Registration of wakealarm event failed\n");wakealarm_set_interval(healthd_config.periodic_chores_interval_fast);
}static void healthd_mainloop(void) {int nevents = 0;while (1) {struct epoll_event events[eventct];int timeout = awake_poll_interval;int mode_timeout;/* Don't wait for first timer timeout to run periodic chores */if (!nevents) periodic_chores();healthd_mode_ops->heartbeat();mode_timeout = healthd_mode_ops->preparetowait();if (timeout < 0 || (mode_timeout > 0 && mode_timeout < timeout)) timeout = mode_timeout;nevents = epoll_wait(epollfd, events, eventct, timeout);if (nevents == -1) {if (errno == EINTR) continue;KLOG_ERROR(LOG_TAG, "healthd_mainloop: epoll_wait failed\n");break;}for (int n = 0; n < nevents; ++n) {if (events[n].data.ptr) (*(void (*)(int))events[n].data.ptr)(events[n].events);}}return;
}static int healthd_init() {epollfd = epoll_create1(EPOLL_CLOEXEC);if (epollfd == -1) {KLOG_ERROR(LOG_TAG, "epoll_create1 failed; errno=%d\n", errno);return -1;}healthd_mode_ops->init(&healthd_config);wakealarm_init();uevent_init();return 0;
}int healthd_main() {int ret;klog_set_level(KLOG_LEVEL);if (!healthd_mode_ops) {KLOG_ERROR("healthd ops not set, exiting\n");exit(1);}ret = healthd_init();if (ret) {KLOG_ERROR("Initialization failed, exiting\n");exit(2);}healthd_mainloop();KLOG_ERROR("Main loop terminated, exiting\n");return 3;
}
最后再来分析 android.hardware.health@2.0-impl-default 这个动态库,它主要使用了 HealthImplDefault.cpp。
这里面很多方法都是些默认空实现,并且给 healthd_mode_ops 结构体里的字段(函数指针)进行了赋值。另外实现了获取 HAL 对象的导出方法(HIDL_FETCH_IHealth),如果传递的入参 name 为 backup 字符串,返回 Health::initInstance(…) 初始化的单例 Health 对象。
hardware/interfaces/health/2.0/default/HealthImplDefault.cpp
static struct healthd_config gHealthdConfig = {.batteryStatusPath = android::String8(android::String8::kEmptyString),.batteryHealthPath = android::String8(android::String8::kEmptyString),.batteryPresentPath = android::String8(android::String8::kEmptyString),.batteryCapacityPath = android::String8(android::String8::kEmptyString),.batteryVoltagePath = android::String8(android::String8::kEmptyString),.batteryTemperaturePath = android::String8(android::String8::kEmptyString),.batteryTechnologyPath = android::String8(android::String8::kEmptyString),.batteryCurrentNowPath = android::String8(android::String8::kEmptyString),.batteryCurrentAvgPath = android::String8(android::String8::kEmptyString),.batteryChargeCounterPath = android::String8(android::String8::kEmptyString),.batteryFullChargePath = android::String8(android::String8::kEmptyString),.batteryCycleCountPath = android::String8(android::String8::kEmptyString),.energyCounter = nullptr,.boot_min_cap = 0,.screen_on = nullptr};void healthd_board_init(struct healthd_config*) {// use defaults
}int healthd_board_battery_update(struct android::BatteryProperties*) {// return 0 to log periodic polled battery status to kernel logreturn 0;
}void healthd_mode_default_impl_init(struct healthd_config*) {// noop
}int healthd_mode_default_impl_preparetowait(void) {return -1;
}void healthd_mode_default_impl_heartbeat(void) {// noop
}void healthd_mode_default_impl_battery_update(struct android::BatteryProperties*) {// noop
}static struct healthd_mode_ops healthd_mode_default_impl_ops = {.init = healthd_mode_default_impl_init,.preparetowait = healthd_mode_default_impl_preparetowait,.heartbeat = healthd_mode_default_impl_heartbeat,.battery_update = healthd_mode_default_impl_battery_update,
};extern "C" IHealth* HIDL_FETCH_IHealth(const char* name) {const static std::string providedInstance{"backup"};healthd_mode_ops = &healthd_mode_default_impl_ops;if (providedInstance == name) {// use defaults// Health class stores static instancereturn Health::initInstance(&gHealthdConfig).get();}return nullptr;
}
【Android 10 源码】healthd 模块 HAL 2.0 分析相关推荐
- 【Android 10 源码】healthd 模块 HAL 1.0 分析
health@1.0:android.hardware.health@1.0 的缩写.指的是 Android 8.0 中发布的运行状况 HIDL 的 1.0 版 HAL. Android 8.x 中的 ...
- Ubuntu18.04 编译Android 10源码 并烧录源码到pixel3的避坑指南
Ubuntu18.04 编译Android 10源码 并烧录源码到pixel3的避坑指南 实验环境 下载Android源码树 在pixel3上安装手机驱动版本 编译Android源码 Android ...
- 【Android 10 源码】深入理解 software Codec2 服务启动
MediaCodec 系列文章: [Android 10 源码]深入理解 MediaCodec 硬解码初始化 [Android 10 源码]深入理解 Omx 初始化 [Android 10 源码]深入 ...
- 【Android 10 源码】深入理解 MediaCodec 组件分配
MediaCodec 系列文章: [Android 10 源码]深入理解 MediaCodec 硬解码初始化 [Android 10 源码]深入理解 Omx 初始化 [Android 10 源码]深入 ...
- 【Android 10 源码】深入理解 Omx 初始化
MediaCodec 系列文章: [Android 10 源码]深入理解 MediaCodec 硬解码初始化 [Android 10 源码]深入理解 Omx 初始化 [Android 10 源码]深入 ...
- 【Android 10 源码】MediaRecorder 录像流程:MediaRecorder 初始化
MediaRecorder 用于录制音频和视频,录制控制基于一个简单的状态机.下面是典型的使用 camera2 API 录制,需要使用到的 MediaRecorder API. MediaRecord ...
- 【Android 10 源码】MediaRecorder 录像流程:MediaRecorder 配置
MediaRecorder 录像配置主要涉及输出文件路径.音频来源.视频来源.输出格式.音频编码格式.视频编码格式.比特率.帧率和视频尺寸等. 我们假设视频输入源来自 Camera,Camera2 A ...
- 【Android 10 源码】Camera v1 startPreview 流程
Camera v1 startPreview 起点位于 android.hardware 包下的 Camera 类中,这是老版本的 Camera 预览的起点. 上面这张相机架构图左边就是关于 Came ...
- 【Android 10 源码】healthd 模块 BatteryService 初始化
BatteryService 是在 SystemServer 中启动的,BatteryService 监控设备电池的充电状态和充电水平.当这些值改变时,这个服务会将这些新值广播给所有正在监听 ACTI ...
最新文章
- Linux 的系统运行级别
- 人工智能终于能像人类一样学习,并通过了图灵测试
- 利用函数BAPI_PR_CREATE开发采购申请批导
- win10光驱位连接计算机,windows10不能识别光驱位硬盘
- boost::ReadablePropertyMapConcept用法的测试程序
- YUV420数据格式
- android中屏幕宽高显示不全,Android 获取屏幕宽度跟高度
- oracle告警日志备份,教你怎样用Oracle方便地查看报警日志错误
- 称重传感器知识:型号,认证,性能与选择
- 区块链入门教程(1)--概述
- 《中国传统文化》慕课期末试题及答案
- 视频教程-MMOARPG地下守护神_单机版实战视频课程(中部) -Unity3D
- Leetcode 第 201 场周赛 (2020 滴滴校招专场)
- python安装jupyterlab_Jupyter/JupyterLab安装使用
- 最喜欢突然说分手的星座,有TA吗?
- 用户注册+登录(下)
- 国内产线 OLED 良率低,产能释放缓慢
- NTSC色域(CIE1931)计算公式
- 前端批量下载七牛云文件
- Marvell深耕智能家居行业 赞同语音控制将成必然发展趋势
热门文章
- 贵阳个税系统代理服务器地址,贵阳金三个税服务器地址
- 【数据分析师_02_SQL+MySQL】022_MySQL的全文检索(MyISAM,MATCH AGAINST)
- 百度绿萝2.0上线后,新站该如何做SEO?
- Java集合的基础知识
- windows 10 打开任务管理器的快捷键
- 运放放大倍数计算公式_运算放大器的虚短、虚断,你都会了吗
- jenkins恢复assign roles
- Part 1: 3.1 时序逻辑~3.2 flip flop 触发器寄存器
- 一定要用Chrome吗?有了这些网页翻译插件,任意浏览器都能畅快玩耍
- golang fmt包中的占位符