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(…) 主要流程:

  1. 打开 /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() 主要流程:

  1. 调用 epoll_create1(…) 创建 epoll 文件描述符,并设置执行时关闭 close-on-exec(EPOLL_CLOEXEC)标志;
  2. 调用 healthd_mode_ops 内 init 函数指针指向的实际实现;
  3. 调用 wakealarm_init() 初始化闹钟唤醒;
  4. 调用 uevent_init() 初始化 uevent。

wakealarm_init(void) 主要流程:

  1. 调用 timerfd_create(…) 创建定时器文件描述符,以 clockid:CLOCK_BOOTTIME_ALARM 和 flags:TFD_NONBLOCK 作为入参。 CLOCK_BOOTTIME_ALARM 表示单调的系统级时钟,包括停留在挂起中的时间,还能唤醒挂起的系统。TFD_NONBLOCK 表示非阻塞模式。
  2. 调用 healthd_register_event(…) 注册闹钟事件处理相关。
  3. 调用 wakealarm_set_interval(…) 设置闹钟间隔。

uevent_init(void) 主要流程:

  1. 调用 uevent_open_socket(…) 创建 netlink socket,用于监听 uevent 事件,返回 socket 文件描述符。64 * 1024 作为 socket 接收缓存区 buffer 大小。passcred 为 true,表示允许 SCM_CREDENTIALS(发送或接收 UNIX 凭证,可以用来身份验证) 控制消息的接收。
  2. 调用 fcntl 设置文件描述符为非阻塞模式。
  3. 调用 healthd_register_event(…) 注册 uevent 事件处理相关。

healthd_register_event(…) 主要流程:

  1. 创建 epoll_event 结构并设置其 events 和 data.ptr 字段,events 赋值 EPOLLIN,EPOLLIN 表示对应的文件描述符可以读。当唤醒设备的事件发生时,设备驱动会保持唤醒状态,直到事件进入排队状态。为了保持设备唤醒直到事件处理完成,必须使用epoll EPOLLWAKEUP 标记。
  2. 调用 epoll_ctl(…) 注册 fd 到 epollfd。
  3. eventct 加 1,每注册成功一个 fd 就加 1,记录注册成功 fd 的总次数。

wakealarm_event(…) 主要流程:

  1. 调用 read(…) 获取 wakeups 变量次数(read 会把参数 fd 所指的文件传送 count 个字节到 buf 指针所指的内存中);
  2. 调用 periodic_chores() 处理定期事务,此处实现 periodic_chores() 内部仅仅调用了 healthd_battery_update(),而 healthd_battery_update() 内部只是委托给 Health 实现中的 update() 方法。

wakealarm_set_interval(…) 主要流程:

  1. 创建 itimerspec 结构,并给其 it_interval 和 it_value 字段赋值;
  2. 调用 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(…) 主要流程:

  1. 调用 uevent_kernel_multicast_recv(…) 接收 uevent 消息,如果获取的消息长度小于等于 0 直接退出,如果大最大长度也直接丢弃退出;
  2. 查找消息中是否存在 SUBSYSTEM=power_supply 字符串,存在的话表示供电系统触发的消息,此时就会调用 healthd_battery_update() 更新电池信息。

healthd_mainloop() 主要流程:

  1. 进入一个无限循环中;
  2. 创建 epoll_event 结构数组用来接收 epoll 事件;
  3. 第一次进入循环直接运行一次 periodic_chores() 函数;
  4. 调用 healthd_mode_ops->heartbeat() 处理心跳;
  5. 调用 healthd_mode_ops->preparetowait() 获取 epoll_wait(…) 使用的超时间隔;
  6. 调用 epoll_wait(…) 等待 epoll 事件;
  7. 如果 epoll_wait(…) 返回 -1,错误类型为 EINTR(代表方法被中断),则 continue 执行 while 循环,否则打印错误日志退出循环;
  8. for 循环遍历获取的 epoll 事件,并逐个调用其注册的事件处理函数,也就是 wakealarm_event(…) 和 uevent_event(…)。

healthd_battery_update_internal(…) 主要流程:

  1. 根据入参 charger_online 充电是否在线 bool 值,设置新的闹钟唤醒时间间隔,充电时设置为较短唤醒间隔,电池放电时设置为较长唤醒间隔。
  2. 当闹钟设置为较快的唤醒时间间隔,就将 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 分析相关推荐

  1. 【Android 10 源码】healthd 模块 HAL 1.0 分析

    health@1.0:android.hardware.health@1.0 的缩写.指的是 Android 8.0 中发布的运行状况 HIDL 的 1.0 版 HAL. Android 8.x 中的 ...

  2. Ubuntu18.04 编译Android 10源码 并烧录源码到pixel3的避坑指南

    Ubuntu18.04 编译Android 10源码 并烧录源码到pixel3的避坑指南 实验环境 下载Android源码树 在pixel3上安装手机驱动版本 编译Android源码 Android ...

  3. 【Android 10 源码】深入理解 software Codec2 服务启动

    MediaCodec 系列文章: [Android 10 源码]深入理解 MediaCodec 硬解码初始化 [Android 10 源码]深入理解 Omx 初始化 [Android 10 源码]深入 ...

  4. 【Android 10 源码】深入理解 MediaCodec 组件分配

    MediaCodec 系列文章: [Android 10 源码]深入理解 MediaCodec 硬解码初始化 [Android 10 源码]深入理解 Omx 初始化 [Android 10 源码]深入 ...

  5. 【Android 10 源码】深入理解 Omx 初始化

    MediaCodec 系列文章: [Android 10 源码]深入理解 MediaCodec 硬解码初始化 [Android 10 源码]深入理解 Omx 初始化 [Android 10 源码]深入 ...

  6. 【Android 10 源码】MediaRecorder 录像流程:MediaRecorder 初始化

    MediaRecorder 用于录制音频和视频,录制控制基于一个简单的状态机.下面是典型的使用 camera2 API 录制,需要使用到的 MediaRecorder API. MediaRecord ...

  7. 【Android 10 源码】MediaRecorder 录像流程:MediaRecorder 配置

    MediaRecorder 录像配置主要涉及输出文件路径.音频来源.视频来源.输出格式.音频编码格式.视频编码格式.比特率.帧率和视频尺寸等. 我们假设视频输入源来自 Camera,Camera2 A ...

  8. 【Android 10 源码】Camera v1 startPreview 流程

    Camera v1 startPreview 起点位于 android.hardware 包下的 Camera 类中,这是老版本的 Camera 预览的起点. 上面这张相机架构图左边就是关于 Came ...

  9. 【Android 10 源码】healthd 模块 BatteryService 初始化

    BatteryService 是在 SystemServer 中启动的,BatteryService 监控设备电池的充电状态和充电水平.当这些值改变时,这个服务会将这些新值广播给所有正在监听 ACTI ...

最新文章

  1. Linux 的系统运行级别
  2. 人工智能终于能像人类一样学习,并通过了图灵测试
  3. 利用函数BAPI_PR_CREATE开发采购申请批导
  4. win10光驱位连接计算机,windows10不能识别光驱位硬盘
  5. boost::ReadablePropertyMapConcept用法的测试程序
  6. YUV420数据格式
  7. android中屏幕宽高显示不全,Android 获取屏幕宽度跟高度
  8. oracle告警日志备份,教你怎样用Oracle方便地查看报警日志错误
  9. 称重传感器知识:型号,认证,性能与选择
  10. 区块链入门教程(1)--概述
  11. 《中国传统文化》慕课期末试题及答案
  12. 视频教程-MMOARPG地下守护神_单机版实战视频课程(中部) -Unity3D
  13. Leetcode 第 201 场周赛 (2020 滴滴校招专场)
  14. python安装jupyterlab_Jupyter/JupyterLab安装使用
  15. 最喜欢突然说分手的星座,有TA吗?
  16. 用户注册+登录(下)
  17. 国内产线 OLED 良率低,产能释放缓慢
  18. NTSC色域(CIE1931)计算公式
  19. 前端批量下载七牛云文件
  20. Marvell深耕智能家居行业 赞同语音控制将成必然发展趋势

热门文章

  1. 贵阳个税系统代理服务器地址,贵阳金三个税服务器地址
  2. 【数据分析师_02_SQL+MySQL】022_MySQL的全文检索(MyISAM,MATCH AGAINST)
  3. 百度绿萝2.0上线后,新站该如何做SEO?
  4. Java集合的基础知识
  5. windows 10 打开任务管理器的快捷键
  6. 运放放大倍数计算公式_运算放大器的虚短、虚断,你都会了吗
  7. jenkins恢复assign roles
  8. Part 1: 3.1 时序逻辑~3.2 flip flop 触发器寄存器
  9. 一定要用Chrome吗?有了这些网页翻译插件,任意浏览器都能畅快玩耍
  10. golang fmt包中的占位符