OpenHarmony源码解析之耗电统计服务功能

系统 OpenHarmony
本文主要和大家分享了OpenHarmony电源管理子系统中关于耗电量服务的实现细节,包括NAPI接口、部分重要类的接口的实现等,做了较为详细的代码说明。

​想了解更多关于开源的内容,请访问:​

​51CTO 开源基础软件社区​

​https://ost.51cto.com​

1、耗电统计服务简介

(1)统计耗电量分两个方面

软件耗电统计:统计每个应用或者软件的耗电情况,包括不限于下面几项:cpu的耗电、持锁运行带来的耗电、移动无线的耗电 、wifi耗电 、gps耗电、传感器的耗电 、相机耗电、 闪光灯耗电等。

硬件耗电统计:软件耗电之外的耗电都归属到硬件耗电,包括不限于如下几项:用户功耗 、通话功耗、屏幕功耗 、Wifi功耗 、蓝牙消耗等等。

(2)耗电统计基本流程:

耗电统计服务启动时注册监听回调,当软件或硬件状态发生变化时更新耗电量,如手电筒开关,wifi搜索等。

耗电服务启动后 解析"/system/etc/profile/power_average.json"文件内容,将一些耗电行为平均值保存起来,如蓝牙扫描的电流为5ma,为以后的电量计算提供基本的数据。

耗电量服务在收到关机通知时将本次记录的信息保存在 /data/system/battery_stats.json。

再次开机启动耗电量服务后load “/data/system/battery_stats.json “值进行初始化耗电量数据。

依赖监听函数,更新耗电量,提供数据给上层应用。

本文重点分析耗电统计服务功能,包括NAPI接口、耗电统计服务重要类接口的实现。

(3)耗电服务子系统架构图

OpenHarmony源码解析之耗电统计服务功能-开源基础软件社区

2、源代码目录结构

OpenHarmony源码解析之耗电统计服务功能-开源基础软件社区

3、整体流程代码

(1)NAPI接口

本文以GetAppStatsPercent() 和GetAppStatsMah()接口为例子,分析调用过程。

//init
static napi_value BatteryStatsInit(napi_env env, napi_value exports)
{
STATS_HILOGD(STATS_MODULE_JS_NAPI, "Enter");
napi_property_descriptor desc[] = {
DECLARE_NAPI_FUNCTION("getBatteryStats", GetBatteryStats),
DECLARE_NAPI_FUNCTION("getAppPowerValue", GetAppStatsMah),//获取app消耗的电量,mah是毫安时的缩写
DECLARE_NAPI_FUNCTION("getAppPowerPercent", GetAppStatsPercent),//获取app消耗的电量在总耗电量中的百分比
DECLARE_NAPI_FUNCTION("getHardwareUnitPowerValue", GetPartStatsMah),
DECLARE_NAPI_FUNCTION("getHardwareUnitPowerPercent", GetPartStatsPercent),
};
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
CreateEnumStatsType(env, exports);
STATS_HILOGD(STATS_MODULE_JS_NAPI, "Exit");
return exports;
}
EXTERN_C_END
// GetAppStatsMah 接口的实现
static napi_value GetAppStatsMah(napi_env env, napi_callback_info info)
{
STATS_HILOGD(STATS_MODULE_JS_NAPI, "Enter");
size_t argc = 1;
napi_value argv[1];
napi_value thisVar;
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
NAPI_ASSERT(env, argc == 1, "Wrong number of arguments");
napi_valuetype type1;
napi_typeof(env, argv[0], &type1);
NAPI_ASSERT(env, type1 == napi_number, "Wrong argument type. napi_number expected.");
int32_t uid;
napi_get_value_int32(env, argv[0], &uid);
//调用BatteryStatsClient的GetAppStatsMah
double appStatsMah = BatteryStatsClient::GetInstance().GetAppStatsMah(uid);
napi_value result;
napi_create_double(env, appStatsMah, &result);
STATS_HILOGD(STATS_MODULE_JS_NAPI, "Got stats mah: %{public}lf for uid: %{public}d", appStatsMah, uid);
return result;
}
// GetAppStatsPercent 接口的实现
static napi_value GetAppStatsPercent(napi_env env, napi_callback_info info)
{
STATS_HILOGD(STATS_MODULE_JS_NAPI, "Enter");
size_t argc = 1;
napi_value argv[1];
napi_value thisVar;
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
NAPI_ASSERT(env, argc == 1, "Wrong number of arguments");
napi_valuetype type1;
napi_typeof(env, argv[0], &type1);
NAPI_ASSERT(env, type1 == napi_number, "Wrong argument type. napi_number expected.");
int32_t uid;
napi_get_value_int32(env, argv[0], &uid);
//调用BatteryStatsClient的GetAppStatsPercent
double appStatsPercent = BatteryStatsClient::GetInstance().GetAppStatsPercent(uid);
napi_value result;
napi_create_double(env, appStatsPercent, &result);
STATS_HILOGD(STATS_MODULE_JS_NAPI, "Got stats percent: %{public}lf for uid: %{public}d", appStatsPercent, uid);
return result;
}

(2)BatteryStatsClient接口实现

整体调用架构比较统一,在client里调用proxy接口。

double BatteryStatsClient::GetAppStatsMah(const int32_t& uid)
{
STATS_HILOGI(STATS_MODULE_INNERKIT, "Enter");
double appStatsMah = StatsUtils::DEFAULT_VALUE;
STATS_RETURN_IF_WITH_RET(Connect() != ERR_OK, appStatsMah);
//调用proxy GetAppStatsMah
appStatsMah = proxy_->GetAppStatsMah(uid);
STATS_HILOGI(STATS_MODULE_INNERKIT, "Calling GetAppStatsMah Success!");
return appStatsMah;
}
double BatteryStatsClient::GetAppStatsPercent(const int32_t& uid)
{
STATS_HILOGI(STATS_MODULE_INNERKIT, "Enter");
double appStatsPercent = StatsUtils::DEFAULT_VALUE;
STATS_RETURN_IF_WITH_RET(Connect() != ERR_OK, appStatsPercent);
//调用proxy GetAppStatsPercent
appStatsPercent = proxy_->GetAppStatsPercent(uid);
STATS_HILOGI(STATS_MODULE_INNERKIT, "Calling GetAppStatsPercent Success!");
return appStatsPercent;
}

(3)BatteryStatsProxy 接口实现

BatteryStatsProxy通过SendRequest发送消息到BatteryStatsStub。

double BatteryStatsProxy::GetAppStatsMah(const int32_t& uid)
{
STATS_HILOGD(STATS_MODULE_INNERKIT, "Enter");
sptr<IRemoteObject> remote = Remote();
STATS_RETURN_IF_WITH_RET(remote == nullptr, StatsUtils::DEFAULT_VALUE);
MessageParcel data;
MessageParcel reply;
MessageOption option;
if (!data.WriteInterfaceToken(BatteryStatsProxy::GetDescriptor())) {
STATS_HILOGE(STATS_MODULE_INNERKIT, "Write descriptor failed!");
return StatsUtils::DEFAULT_VALUE;
}
data.WriteInt32(uid);
//SendRequest BATTERY_STATS_GETAPPMAH 消息
int ret = remote->SendRequest(static_cast<int>(IBatteryStats::BATTERY_STATS_GETAPPMAH), data, reply, option);
if (ret != ERR_OK) {
STATS_HILOGE(STATS_MODULE_INNERKIT, "Transact is failed, error code: %{public}d", ret);
}
double appStatsMah = StatsUtils::DEFAULT_VALUE;
appStatsMah = reply.ReadDouble();
STATS_HILOGD(STATS_MODULE_INNERKIT, "Got stats mah: %{public}lf for uid: %{public}d", appStatsMah, uid);
return appStatsMah;
}
double BatteryStatsProxy::GetAppStatsPercent(const int32_t& uid)
{
STATS_HILOGD(STATS_MODULE_INNERKIT, "Enter");
sptr<IRemoteObject> remote = Remote();
STATS_RETURN_IF_WITH_RET(remote == nullptr, StatsUtils::DEFAULT_VALUE);
MessageParcel data;
MessageParcel reply;
MessageOption option;
if (!data.WriteInterfaceToken(BatteryStatsProxy::GetDescriptor())) {
STATS_HILOGE(STATS_MODULE_INNERKIT, "Write descriptor failed!");
return StatsUtils::DEFAULT_VALUE;
}
data.WriteInt32(uid);
//SendRequest BATTERY_STATS_GETAPPPER 消息
int ret = remote->SendRequest(static_cast<int>(IBatteryStats::BATTERY_STATS_GETAPPPER), data, reply, option);
if (ret != ERR_OK) {
STATS_HILOGE(STATS_MODULE_INNERKIT, "Transact is failed, error code: %{public}d", ret);
}
double appStatsPercent = StatsUtils::DEFAULT_VALUE;
appStatsPercent = reply.ReadDouble();
STATS_HILOGD(STATS_MODULE_INNERKIT, "Got stats percent: %{public}lf for uid: %{public}d", appStatsPercent, uid);
return appStatsPercent
}

(4)BatteryStatsStub 接口实现

BatteryStatsStub 直接调用BatteryStatsService的函数实现。

int32_t BatteryStatsStub::GetAppStatsMahStub(MessageParcel &data, MessageParcel& reply)
{
STATS_HILOGI(STATS_MODULE_SERVICE, "Enter");
int32_t uid = data.ReadInt32();
//调用 GetAppStatsMah
double ret = GetAppStatsMah(uid);
if (!reply.WriteDouble(ret)) {
STATS_HILOGE(STATS_MODULE_SERVICE, "Write ret failed.");
return false;
}
STATS_HILOGI(STATS_MODULE_SERVICE, "Exit");
return ERR_OK;
}
int32_t BatteryStatsStub::GetAppStatsPercentStub(MessageParcel &data, MessageParcel& reply)
{
STATS_HILOGI(STATS_MODULE_SERVICE, "Enter");
int32_t uid = data.ReadInt32();
//调用 GetAppStatsPercent
double ret = GetAppStatsPercent(uid);
if (!reply.WriteDouble(ret)) {
STATS_HILOGE(STATS_MODULE_SERVICE, "Write ret failed.");
return false;
}
STATS_HILOGI(STATS_MODULE_SERVICE, "Exit");
return ERR_OK;
}

(5)BatteryStatsService接口实现

double BatteryStatsService::GetAppStatsMah(const int32_t& uid)
{
STATS_HILOGI(STATS_MODULE_SERVICE, "Enter");
//调用 BatteryStatsCore ComputePower
core_->ComputePower();
return core_->GetAppStatsMah(uid);
}
double BatteryStatsService::GetAppStatsPercent(const int32_t& uid)
{
STATS_HILOGI(STATS_MODULE_SERVICE, "Enter");
core_->ComputePower();
return core_->GetAppStatsPercent(uid);
}

(6)BatteryStatsCore 接口实现

ComputePower()函数分别调用不同的Entity 计算耗电量,以phoneEntity_为例计算耗电量的过程为如下:

在前面提到电话的平均电流保存在 /system/etc/profile/power_average.json中,值为 “radio_active”: 50。

phoneEntity的Calculate()函数先得到Call时的电流为 50ma。

phone activie的时间乘以电流值就计算出打电话时的耗电量。

计算出的耗电量保存到totalPowerMah_ 全局变量中。

按照phone类型存储到 全局变量statsInfoList_,提供给get接口使用。

void BatteryStatsCore::ComputePower()
{
STATS_HILOGI(STATS_MODULE_SERVICE, "Enter");
const int DFX_DELAY_MS = 10000;
int id = HiviewDFX::XCollie::GetInstance().SetTimer("BatteryStatsCoreComputePower", DFX_DELAY_MS, nullptr, nullptr,
HiviewDFX::XCOLLIE_FLAG_NOOP);
BatteryStatsEntity::ResetStatsEntity();
uidEntity_->Calculate();
bluetoothEntity_->Calculate();
idleEntity_->Calculate();
phoneEntity_->Calculate();
radioEntity_->Calculate();
screenEntity_->Calculate();
wifiEntity_->Calculate();
userEntity_->Calculate();
HiviewDFX::XCollie::GetInstance().CancelTimer(id);
STATS_HILOGI(STATS_MODULE_SERVICE, "Exit");
}
//GetAppStatsMah接口直接从保存的statsInfoList中取数据。
double BatteryStatsCore::GetAppStatsMah(const int32_t& uid)
{
double appStatsMah = StatsUtils::DEFAULT_VALUE;
auto statsInfoList = GetBatteryStats();
for (auto iter = statsInfoList.begin(); iter != statsInfoList.end(); iter++) {
if ((*iter)->GetConsumptionType() == BatteryStatsInfo::CONSUMPTION_TYPE_APP) {
if ((*iter)->GetUid() == uid) {
appStatsMah = (*iter)->GetPower();
break;
}
}
}
STATS_HILOGD(STATS_MODULE_SERVICE, "Got stats mah: %{public}lf for uid: %{public}d", appStatsMah, uid);
return appStatsMah;
}
//GetAppStatsPercent接口直接从保存的statsInfoList中取数据和总电流比较后得到百分比。
double BatteryStatsCore::GetAppStatsPercent(const int32_t& uid)
{
double appStatsPercent = StatsUtils::DEFAULT_VALUE;
auto statsInfoList = GetBatteryStats();
auto totalConsumption = BatteryStatsEntity::GetTotalPowerMah();
if (totalConsumption <= StatsUtils::DEFAULT_VALUE) {
STATS_HILOGE(STATS_MODULE_SERVICE, "No consumption got, return 0");
return appStatsPercent;
}
for (auto iter = statsInfoList.begin(); iter != statsInfoList.end(); iter++) {
if ((*iter)->GetConsumptionType() == BatteryStatsInfo::CONSUMPTION_TYPE_APP) {
if ((*iter)->GetUid() == uid && totalConsumption != StatsUtils::DEFAULT_VALUE) {
appStatsPercent = (*iter)->GetPower() / totalConsumption;
break;
}
}
}
STATS_HILOGD(STATS_MODULE_SERVICE, "Got stats percent: %{public}lf for uid: %{public}d", appStatsPercent, uid);
return appStatsPercent;
}

总结

本文主要和大家分享了OpenHarmony电源管理子系统中关于耗电量服务的实现细节,包括NAPI接口、部分重要类的接口的实现等,做了较为详细的代码说明,希望通过本文您能初步掌握电源管理子系统中耗电量服务模块的关键功能和核心流程。

​想了解更多关于开源的内容,请访问:​

​51CTO 开源基础软件社区​

​https://ost.51cto.com​​。

责任编辑:jianghua 来源: 鸿蒙社区
相关推荐

2022-08-12 19:07:58

电源管理子系统鸿蒙

2022-07-05 16:03:29

电源管理子系统鸿蒙

2022-02-17 20:57:07

OpenHarmon操作系统鸿蒙

2021-12-14 14:45:38

2021-12-14 10:16:00

2022-01-06 16:17:58

2021-11-10 16:10:18

2021-11-18 10:28:03

2021-11-08 15:04:47

2022-08-08 19:42:24

物联网开发鸿蒙

2022-04-29 14:56:40

通话应用源码剖析

2022-01-24 18:35:56

2022-01-12 14:45:26

2022-03-15 15:24:53

操作系统RTOSAT模块

2022-03-18 15:29:02

Harmony鸿蒙架构

2022-01-21 21:22:24

2022-05-10 11:02:02

电话子系统鸿蒙

2022-03-22 11:33:13

AT模块Harmony鸿蒙

2022-03-07 15:22:16

classHarmony鸿蒙

2021-06-06 16:05:31

点赞
收藏

51CTO技术栈公众号