| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #define LOG_TAG "PowerStatsService" |
| |
| #include <android/hardware/power/stats/1.0/IPowerStats.h> |
| #include <jni.h> |
| #include <nativehelper/JNIHelp.h> |
| |
| #include <log/log.h> |
| |
| using android::hardware::hidl_vec; |
| using android::hardware::Return; |
| using android::hardware::power::stats::V1_0::EnergyData; |
| using android::hardware::power::stats::V1_0::RailInfo; |
| using android::hardware::power::stats::V1_0::Status; |
| |
| // Channel |
| static jclass class_C; |
| static jmethodID method_C_init; |
| static jfieldID field_C_id; |
| static jfieldID field_C_name; |
| static jfieldID field_C_subsystem; |
| |
| // EnergyMeasurement |
| static jclass class_EM; |
| static jmethodID method_EM_init; |
| static jfieldID field_EM_id; |
| static jfieldID field_EM_timestampMs; |
| static jfieldID field_EM_durationMs; |
| static jfieldID field_EM_energyUWs; |
| |
| // State |
| static jclass class_S; |
| static jmethodID method_S_init; |
| static jfieldID field_S_id; |
| static jfieldID field_S_name; |
| |
| // PowerEntity |
| static jclass class_PE; |
| static jmethodID method_PE_init; |
| static jfieldID field_PE_id; |
| static jfieldID field_PE_name; |
| static jfieldID field_PE_states; |
| |
| // StateResidency |
| static jclass class_SR; |
| static jmethodID method_SR_init; |
| static jfieldID field_SR_id; |
| static jfieldID field_SR_totalTimeInStateMs; |
| static jfieldID field_SR_totalStateEntryCount; |
| static jfieldID field_SR_lastEntryTimestampMs; |
| |
| // StateResidencyResult |
| static jclass class_SRR; |
| static jmethodID method_SRR_init; |
| static jfieldID field_SRR_id; |
| static jfieldID field_SRR_stateResidencyData; |
| |
| namespace android { |
| |
| static std::mutex gPowerStatsHalMutex; |
| static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0_ptr = nullptr; |
| |
| static void deinitPowerStats() { |
| gPowerStatsHalV1_0_ptr = nullptr; |
| } |
| |
| struct PowerStatsHalDeathRecipient : virtual public hardware::hidl_death_recipient { |
| virtual void serviceDied(uint64_t cookie, |
| const wp<android::hidl::base::V1_0::IBase> &who) override { |
| // The HAL just died. Reset all handles to HAL services. |
| std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); |
| deinitPowerStats(); |
| } |
| }; |
| |
| sp<PowerStatsHalDeathRecipient> gPowerStatsHalDeathRecipient = new PowerStatsHalDeathRecipient(); |
| |
| static bool connectToPowerStatsHal() { |
| if (gPowerStatsHalV1_0_ptr == nullptr) { |
| gPowerStatsHalV1_0_ptr = android::hardware::power::stats::V1_0::IPowerStats::getService(); |
| |
| if (gPowerStatsHalV1_0_ptr == nullptr) { |
| ALOGE("Unable to get power.stats HAL service."); |
| return false; |
| } |
| |
| // Link death recipient to power.stats service handle |
| hardware::Return<bool> linked = |
| gPowerStatsHalV1_0_ptr->linkToDeath(gPowerStatsHalDeathRecipient, 0); |
| if (!linked.isOk()) { |
| ALOGE("Transaction error in linking to power.stats HAL death: %s", |
| linked.description().c_str()); |
| deinitPowerStats(); |
| return false; |
| } else if (!linked) { |
| ALOGW("Unable to link to power.stats HAL death notifications"); |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| static bool checkResult(const Return<void> &ret, const char *function) { |
| if (!ret.isOk()) { |
| ALOGE("%s failed: requested HAL service not available. Description: %s", function, |
| ret.description().c_str()); |
| if (ret.isDeadObject()) { |
| deinitPowerStats(); |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| static jobjectArray nativeGetPowerEntityInfo(JNIEnv *env, jclass clazz) { |
| std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); |
| |
| if (!connectToPowerStatsHal()) { |
| ALOGE("nativeGetPowerEntityInfo failed to connect to power.stats HAL"); |
| return nullptr; |
| } |
| |
| jobjectArray powerEntityArray = nullptr; |
| Return<void> ret = gPowerStatsHalV1_0_ptr->getPowerEntityInfo( |
| [&env, &powerEntityArray](auto infos, auto status) { |
| if (status != Status::SUCCESS) { |
| ALOGE("Error getting power entity info"); |
| } else { |
| powerEntityArray = env->NewObjectArray(infos.size(), class_PE, nullptr); |
| for (int i = 0; i < infos.size(); i++) { |
| jstring name = env->NewStringUTF(infos[i].powerEntityName.c_str()); |
| jobject powerEntity = env->NewObject(class_PE, method_PE_init); |
| env->SetIntField(powerEntity, field_PE_id, infos[i].powerEntityId); |
| env->SetObjectField(powerEntity, field_PE_name, name); |
| env->SetObjectArrayElement(powerEntityArray, i, powerEntity); |
| env->DeleteLocalRef(name); |
| env->DeleteLocalRef(powerEntity); |
| } |
| } |
| }); |
| if (!checkResult(ret, __func__)) { |
| return nullptr; |
| } |
| |
| ret = gPowerStatsHalV1_0_ptr |
| ->getPowerEntityStateInfo({}, [&env, &powerEntityArray](auto infos, auto status) { |
| if (status != Status::SUCCESS) { |
| ALOGE("Error getting power entity state info"); |
| } else { |
| for (int i = 0; i < infos.size(); i++) { |
| jobjectArray stateArray = |
| env->NewObjectArray(infos[i].states.size(), class_S, nullptr); |
| for (int j = 0; j < infos[i].states.size(); j++) { |
| jstring name = env->NewStringUTF( |
| infos[i].states[j].powerEntityStateName.c_str()); |
| jobject state = env->NewObject(class_S, method_S_init); |
| env->SetIntField(state, field_S_id, |
| infos[i].states[j].powerEntityStateId); |
| env->SetObjectField(state, field_S_name, name); |
| env->SetObjectArrayElement(stateArray, j, state); |
| env->DeleteLocalRef(name); |
| env->DeleteLocalRef(state); |
| } |
| |
| for (int j = 0; j < env->GetArrayLength(powerEntityArray); j++) { |
| jobject powerEntity = |
| env->GetObjectArrayElement(powerEntityArray, j); |
| if (env->GetIntField(powerEntity, field_PE_id) == |
| infos[i].powerEntityId) { |
| env->SetObjectField(powerEntity, field_PE_states, stateArray); |
| env->SetObjectArrayElement(powerEntityArray, j, powerEntity); |
| break; |
| } |
| } |
| } |
| } |
| }); |
| if (!checkResult(ret, __func__)) { |
| return nullptr; |
| } |
| |
| return powerEntityArray; |
| } |
| |
| static jobjectArray nativeGetStateResidency(JNIEnv *env, jclass clazz, jintArray powerEntityIds) { |
| std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); |
| |
| if (!connectToPowerStatsHal()) { |
| ALOGE("nativeGetStateResidency failed to connect to power.stats HAL"); |
| return nullptr; |
| } |
| |
| size_t powerEntityIdCount = env->GetArrayLength(powerEntityIds); |
| hidl_vec<uint32_t> powerEntityIdVector(powerEntityIdCount); |
| |
| jint *powerEntityIdElements = env->GetIntArrayElements(powerEntityIds, 0); |
| for (int i = 0; i < powerEntityIdCount; i++) { |
| powerEntityIdVector[i] = powerEntityIdElements[i]; |
| } |
| env->ReleaseIntArrayElements(powerEntityIds, powerEntityIdElements, 0); |
| |
| jobjectArray stateResidencyResultArray = nullptr; |
| Return<void> ret = gPowerStatsHalV1_0_ptr->getPowerEntityStateResidencyData( |
| powerEntityIdVector, [&env, &stateResidencyResultArray](auto results, auto status) { |
| stateResidencyResultArray = env->NewObjectArray(results.size(), class_SRR, nullptr); |
| for (int i = 0; i < results.size(); i++) { |
| jobjectArray stateResidencyArray = |
| env->NewObjectArray(results[i].stateResidencyData.size(), class_SR, |
| nullptr); |
| for (int j = 0; j < results[i].stateResidencyData.size(); j++) { |
| jobject stateResidency = env->NewObject(class_SR, method_SR_init); |
| env->SetIntField(stateResidency, field_SR_id, |
| results[i].stateResidencyData[j].powerEntityStateId); |
| env->SetLongField(stateResidency, field_SR_totalTimeInStateMs, |
| results[i].stateResidencyData[j].totalTimeInStateMs); |
| env->SetLongField(stateResidency, field_SR_totalStateEntryCount, |
| results[i].stateResidencyData[j].totalStateEntryCount); |
| env->SetLongField(stateResidency, field_SR_lastEntryTimestampMs, |
| results[i].stateResidencyData[j].lastEntryTimestampMs); |
| env->SetObjectArrayElement(stateResidencyArray, j, stateResidency); |
| env->DeleteLocalRef(stateResidency); |
| } |
| jobject stateResidencyResult = env->NewObject(class_SRR, method_SRR_init); |
| env->SetIntField(stateResidencyResult, field_SRR_id, results[i].powerEntityId); |
| env->SetObjectField(stateResidencyResult, field_SRR_stateResidencyData, |
| stateResidencyArray); |
| env->SetObjectArrayElement(stateResidencyResultArray, i, stateResidencyResult); |
| env->DeleteLocalRef(stateResidencyResult); |
| } |
| }); |
| if (!checkResult(ret, __func__)) { |
| return nullptr; |
| } |
| |
| return stateResidencyResultArray; |
| } |
| |
| static jobjectArray nativeGetEnergyMeterInfo(JNIEnv *env, jclass clazz) { |
| std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); |
| |
| if (!connectToPowerStatsHal()) { |
| ALOGE("nativeGetEnergyMeterInfo failed to connect to power.stats HAL"); |
| return nullptr; |
| } |
| |
| jobjectArray channelArray = nullptr; |
| Return<void> ret = |
| gPowerStatsHalV1_0_ptr->getRailInfo([&env, &channelArray](auto railInfo, auto status) { |
| if (status != Status::SUCCESS) { |
| ALOGW("Error getting rail info"); |
| } else { |
| channelArray = env->NewObjectArray(railInfo.size(), class_C, nullptr); |
| for (int i = 0; i < railInfo.size(); i++) { |
| jstring name = env->NewStringUTF(railInfo[i].railName.c_str()); |
| jstring subsystem = env->NewStringUTF(railInfo[i].subsysName.c_str()); |
| jobject channel = env->NewObject(class_C, method_C_init); |
| env->SetIntField(channel, field_C_id, railInfo[i].index); |
| env->SetObjectField(channel, field_C_name, name); |
| env->SetObjectField(channel, field_C_subsystem, subsystem); |
| env->SetObjectArrayElement(channelArray, i, channel); |
| env->DeleteLocalRef(name); |
| env->DeleteLocalRef(subsystem); |
| env->DeleteLocalRef(channel); |
| } |
| } |
| }); |
| |
| if (!checkResult(ret, __func__)) { |
| ALOGE("getRailInfo failed"); |
| return nullptr; |
| } |
| |
| return channelArray; |
| } |
| |
| static jobjectArray nativeReadEnergyMeters(JNIEnv *env, jclass clazz, jintArray channelIds) { |
| std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); |
| |
| if (!connectToPowerStatsHal()) { |
| ALOGE("nativeGetEnergy failed to connect to power.stats HAL"); |
| } |
| |
| size_t channelIdCount = env->GetArrayLength(channelIds); |
| hidl_vec<uint32_t> channelIdVector(channelIdCount); |
| |
| jint *channelIdElements = env->GetIntArrayElements(channelIds, 0); |
| for (int i = 0; i < channelIdCount; i++) { |
| channelIdVector[i] = channelIdElements[i]; |
| } |
| env->ReleaseIntArrayElements(channelIds, channelIdElements, 0); |
| |
| jobjectArray energyMeasurementArray = nullptr; |
| Return<void> ret = |
| gPowerStatsHalV1_0_ptr |
| ->getEnergyData(channelIdVector, |
| [&env, &energyMeasurementArray](auto energyData, auto status) { |
| energyMeasurementArray = |
| env->NewObjectArray(energyData.size(), class_EM, |
| nullptr); |
| for (int i = 0; i < energyData.size(); i++) { |
| jobject energyMeasurement = |
| env->NewObject(class_EM, method_EM_init); |
| env->SetIntField(energyMeasurement, field_EM_id, |
| energyData[i].index); |
| env->SetLongField(energyMeasurement, |
| field_EM_timestampMs, |
| energyData[i].timestamp); |
| env->SetLongField(energyMeasurement, |
| field_EM_durationMs, |
| energyData[i].timestamp); |
| env->SetLongField(energyMeasurement, field_EM_energyUWs, |
| energyData[i].energy); |
| env->SetObjectArrayElement(energyMeasurementArray, i, |
| energyMeasurement); |
| env->DeleteLocalRef(energyMeasurement); |
| } |
| }); |
| |
| if (!checkResult(ret, __func__)) { |
| ALOGE("getEnergyData failed"); |
| return nullptr; |
| } |
| |
| return energyMeasurementArray; |
| } |
| |
| static jboolean nativeInit(JNIEnv *env, jclass clazz) { |
| std::lock_guard<std::mutex> lock(gPowerStatsHalMutex); |
| |
| // Channel |
| jclass temp = env->FindClass("android/hardware/power/stats/Channel"); |
| class_C = (jclass)env->NewGlobalRef(temp); |
| method_C_init = env->GetMethodID(class_C, "<init>", "()V"); |
| field_C_id = env->GetFieldID(class_C, "id", "I"); |
| field_C_name = env->GetFieldID(class_C, "name", "Ljava/lang/String;"); |
| field_C_subsystem = env->GetFieldID(class_C, "subsystem", "Ljava/lang/String;"); |
| |
| // EnergyMeasurement |
| temp = env->FindClass("android/hardware/power/stats/EnergyMeasurement"); |
| class_EM = (jclass)env->NewGlobalRef(temp); |
| method_EM_init = env->GetMethodID(class_EM, "<init>", "()V"); |
| field_EM_id = env->GetFieldID(class_EM, "id", "I"); |
| field_EM_timestampMs = env->GetFieldID(class_EM, "timestampMs", "J"); |
| field_EM_durationMs = env->GetFieldID(class_EM, "durationMs", "J"); |
| field_EM_energyUWs = env->GetFieldID(class_EM, "energyUWs", "J"); |
| |
| // State |
| temp = env->FindClass("android/hardware/power/stats/State"); |
| class_S = (jclass)env->NewGlobalRef(temp); |
| method_S_init = env->GetMethodID(class_S, "<init>", "()V"); |
| field_S_id = env->GetFieldID(class_S, "id", "I"); |
| field_S_name = env->GetFieldID(class_S, "name", "Ljava/lang/String;"); |
| |
| // PowerEntity |
| temp = env->FindClass("android/hardware/power/stats/PowerEntity"); |
| class_PE = (jclass)env->NewGlobalRef(temp); |
| method_PE_init = env->GetMethodID(class_PE, "<init>", "()V"); |
| field_PE_id = env->GetFieldID(class_PE, "id", "I"); |
| field_PE_name = env->GetFieldID(class_PE, "name", "Ljava/lang/String;"); |
| field_PE_states = env->GetFieldID(class_PE, "states", "[Landroid/hardware/power/stats/State;"); |
| |
| // StateResidency |
| temp = env->FindClass("android/hardware/power/stats/StateResidency"); |
| class_SR = (jclass)env->NewGlobalRef(temp); |
| method_SR_init = env->GetMethodID(class_SR, "<init>", "()V"); |
| field_SR_id = env->GetFieldID(class_SR, "id", "I"); |
| field_SR_totalTimeInStateMs = env->GetFieldID(class_SR, "totalTimeInStateMs", "J"); |
| field_SR_totalStateEntryCount = env->GetFieldID(class_SR, "totalStateEntryCount", "J"); |
| field_SR_lastEntryTimestampMs = env->GetFieldID(class_SR, "lastEntryTimestampMs", "J"); |
| |
| // StateResidencyResult |
| temp = env->FindClass("android/hardware/power/stats/StateResidencyResult"); |
| class_SRR = (jclass)env->NewGlobalRef(temp); |
| method_SRR_init = env->GetMethodID(class_SRR, "<init>", "()V"); |
| field_SRR_id = env->GetFieldID(class_SRR, "id", "I"); |
| field_SRR_stateResidencyData = |
| env->GetFieldID(class_SRR, "stateResidencyData", |
| "[Landroid/hardware/power/stats/StateResidency;"); |
| |
| if (!connectToPowerStatsHal()) { |
| ALOGE("nativeInit failed to connect to power.stats HAL"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| static const JNINativeMethod method_table[] = { |
| {"nativeInit", "()Z", (void *)nativeInit}, |
| {"nativeGetPowerEntityInfo", "()[Landroid/hardware/power/stats/PowerEntity;", |
| (void *)nativeGetPowerEntityInfo}, |
| {"nativeGetStateResidency", "([I)[Landroid/hardware/power/stats/StateResidencyResult;", |
| (void *)nativeGetStateResidency}, |
| {"nativeGetEnergyMeterInfo", "()[Landroid/hardware/power/stats/Channel;", |
| (void *)nativeGetEnergyMeterInfo}, |
| {"nativeReadEnergyMeters", "([I)[Landroid/hardware/power/stats/EnergyMeasurement;", |
| (void *)nativeReadEnergyMeters}, |
| }; |
| |
| int register_android_server_PowerStatsService(JNIEnv *env) { |
| return jniRegisterNativeMethods(env, |
| "com/android/server/powerstats/" |
| "PowerStatsHALWrapper$PowerStatsHAL10WrapperImpl", |
| method_table, NELEM(method_table)); |
| } |
| |
| }; // namespace android |