blob: 0e21aaf33ec76ee8b0bcae21427978d350a71023 [file] [log] [blame]
/*
* 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