作者:宋金山
1.耗電統計服務簡介:
1.1.統計耗電量分兩個方面:
1.1.1軟體耗電統計:統計每個應用或者軟體的耗電情況,包括不限于下面幾項:cpu的耗電、持鎖運作帶來的耗電、移動無線的耗電 、wifi耗電 、gps耗電、傳感器的耗電 、相機耗電、 閃光燈耗電等。
1.1.2硬體耗電統計:軟體耗電之外的耗電都歸屬到硬體耗電,包括不限于如下幾項:使用者功耗 、通話功耗、螢幕功耗 、Wifi功耗 、藍牙消耗等等
1.2.耗電統計基本流程:
1.2.1.耗電統計服務啟動時注冊監聽回調,當軟體或硬體狀态發生變化時更新耗電量,如手電筒開關,wifi搜尋等。
1.2.2.耗電服務啟動後 解析"/system/etc/profile/power_average.json"檔案内容,将一些耗電行為平均值儲存起來,如藍牙掃描的電流為5ma,為以後的電量計算提供基本的資料
1.2.3. 耗電量服務在收到關機通知時将本次記錄的資訊儲存在 /data/system/battery_stats.json
1.2.4. 再次開機啟動耗電量服務後load “/data/system/battery_stats.json “值進行初始化耗電量資料
1.2.5. 依賴監聽函數,更新耗電量,提供資料給上層應用
本文重點分析耗電統計服務功能,包括NAPI接口、耗電統計服務重要類接口的實作。
1.3.耗電服務子系統架構圖

2. 源代碼目錄結構
3. 整體流程代碼
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;
}
3.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.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
}
3.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;
}
3.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);
}
3.6.BatteryStatsCore 接口實作
3.6.1 ComputePower()函數分别調用不同的Entity 計算耗電量,以phoneEntity_為例計算耗電量的過程為如下:
3.6.1.1. 在1.2.2中提到電話的平均電流儲存在 /system/etc/profile/power_average.json中,值為 "radio_active": 50,
3.6.1.2. phoneEntity的Calculate()函數先得到Call時的電流為 50ma
3.6.1.3. phone activie的時間乘以電流值就計算出打電話時的耗電量
3.6.1.4. 計算出的耗電量儲存到totalPowerMah_ 全局變量中
3.6.1.5. 按照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接口、部分重要類的接口的實作等,做了較為詳細的代碼說明,希望通過本文您能初步掌握電源管理子系統中耗電量服務子產品的關鍵功能和核心流程。關于OpenHarmony其它子系統的分析,請關注後續文章。
更多原創内容請關注:深開鴻技術團隊
入門到精通、技巧到案例,系統化分享HarmonyOS開發技術,歡迎投稿和訂閱,讓我們一起攜手前行共建鴻蒙生态。