OpenHarmony源码解析之耗电统计服务功能 原创 精华

深开鸿
发布于 2022-6-9 18:23
浏览
5收藏

作者:宋金山

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.耗电服务子系统架构图

OpenHarmony源码解析之耗电统计服务功能-鸿蒙开发者社区

2. 源代码目录结构

OpenHarmony源码解析之耗电统计服务功能-鸿蒙开发者社区

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开发技术,欢迎投稿和订阅,让我们一起携手前行共建鸿蒙生态。

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
4
收藏 5
回复
举报
1条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

没想到关于电量有这么多的知识。

回复
2022-6-10 10:27:49
回复
    相关推荐