1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "PowerStatsService"
18
19 #include <android/hardware/power/stats/1.0/IPowerStats.h>
20 #include <jni.h>
21 #include <nativehelper/JNIHelp.h>
22
23 #include <log/log.h>
24
25 using android::hardware::hidl_vec;
26 using android::hardware::Return;
27 using android::hardware::power::stats::V1_0::EnergyData;
28 using android::hardware::power::stats::V1_0::RailInfo;
29 using android::hardware::power::stats::V1_0::Status;
30
31 // Channel
32 static jclass class_C;
33 static jmethodID method_C_init;
34 static jfieldID field_C_id;
35 static jfieldID field_C_name;
36 static jfieldID field_C_subsystem;
37
38 // EnergyMeasurement
39 static jclass class_EM;
40 static jmethodID method_EM_init;
41 static jfieldID field_EM_id;
42 static jfieldID field_EM_timestampMs;
43 static jfieldID field_EM_durationMs;
44 static jfieldID field_EM_energyUWs;
45
46 // State
47 static jclass class_S;
48 static jmethodID method_S_init;
49 static jfieldID field_S_id;
50 static jfieldID field_S_name;
51
52 // PowerEntity
53 static jclass class_PE;
54 static jmethodID method_PE_init;
55 static jfieldID field_PE_id;
56 static jfieldID field_PE_name;
57 static jfieldID field_PE_states;
58
59 // StateResidency
60 static jclass class_SR;
61 static jmethodID method_SR_init;
62 static jfieldID field_SR_id;
63 static jfieldID field_SR_totalTimeInStateMs;
64 static jfieldID field_SR_totalStateEntryCount;
65 static jfieldID field_SR_lastEntryTimestampMs;
66
67 // StateResidencyResult
68 static jclass class_SRR;
69 static jmethodID method_SRR_init;
70 static jfieldID field_SRR_id;
71 static jfieldID field_SRR_stateResidencyData;
72
73 namespace android {
74
75 static std::mutex gPowerStatsHalMutex;
76 static sp<android::hardware::power::stats::V1_0::IPowerStats> gPowerStatsHalV1_0_ptr = nullptr;
77
deinitPowerStats()78 static void deinitPowerStats() {
79 gPowerStatsHalV1_0_ptr = nullptr;
80 }
81
82 struct PowerStatsHalDeathRecipient : virtual public hardware::hidl_death_recipient {
serviceDiedandroid::PowerStatsHalDeathRecipient83 virtual void serviceDied(uint64_t cookie,
84 const wp<android::hidl::base::V1_0::IBase> &who) override {
85 // The HAL just died. Reset all handles to HAL services.
86 std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
87 deinitPowerStats();
88 }
89 };
90
91 sp<PowerStatsHalDeathRecipient> gPowerStatsHalDeathRecipient = new PowerStatsHalDeathRecipient();
92
connectToPowerStatsHal()93 static bool connectToPowerStatsHal() {
94 if (gPowerStatsHalV1_0_ptr == nullptr) {
95 gPowerStatsHalV1_0_ptr = android::hardware::power::stats::V1_0::IPowerStats::getService();
96
97 if (gPowerStatsHalV1_0_ptr == nullptr) {
98 ALOGE("Unable to get power.stats HAL service.");
99 return false;
100 }
101
102 // Link death recipient to power.stats service handle
103 hardware::Return<bool> linked =
104 gPowerStatsHalV1_0_ptr->linkToDeath(gPowerStatsHalDeathRecipient, 0);
105 if (!linked.isOk()) {
106 ALOGE("Transaction error in linking to power.stats HAL death: %s",
107 linked.description().c_str());
108 deinitPowerStats();
109 return false;
110 } else if (!linked) {
111 ALOGW("Unable to link to power.stats HAL death notifications");
112 return false;
113 }
114 }
115 return true;
116 }
117
checkResult(const Return<void> & ret,const char * function)118 static bool checkResult(const Return<void> &ret, const char *function) {
119 if (!ret.isOk()) {
120 ALOGE("%s failed: requested HAL service not available. Description: %s", function,
121 ret.description().c_str());
122 if (ret.isDeadObject()) {
123 deinitPowerStats();
124 }
125 return false;
126 }
127 return true;
128 }
129
nativeGetPowerEntityInfo(JNIEnv * env,jclass clazz)130 static jobjectArray nativeGetPowerEntityInfo(JNIEnv *env, jclass clazz) {
131 std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
132
133 if (!connectToPowerStatsHal()) {
134 ALOGE("nativeGetPowerEntityInfo failed to connect to power.stats HAL");
135 return nullptr;
136 }
137
138 jobjectArray powerEntityArray = nullptr;
139 Return<void> ret = gPowerStatsHalV1_0_ptr->getPowerEntityInfo(
140 [&env, &powerEntityArray](auto infos, auto status) {
141 if (status != Status::SUCCESS) {
142 ALOGE("Error getting power entity info");
143 } else {
144 powerEntityArray = env->NewObjectArray(infos.size(), class_PE, nullptr);
145 for (int i = 0; i < infos.size(); i++) {
146 jstring name = env->NewStringUTF(infos[i].powerEntityName.c_str());
147 jobject powerEntity = env->NewObject(class_PE, method_PE_init);
148 env->SetIntField(powerEntity, field_PE_id, infos[i].powerEntityId);
149 env->SetObjectField(powerEntity, field_PE_name, name);
150 env->SetObjectArrayElement(powerEntityArray, i, powerEntity);
151 env->DeleteLocalRef(name);
152 env->DeleteLocalRef(powerEntity);
153 }
154 }
155 });
156 if (!checkResult(ret, __func__)) {
157 return nullptr;
158 }
159
160 ret = gPowerStatsHalV1_0_ptr
161 ->getPowerEntityStateInfo({}, [&env, &powerEntityArray](auto infos, auto status) {
162 if (status != Status::SUCCESS) {
163 ALOGE("Error getting power entity state info");
164 } else {
165 for (int i = 0; i < infos.size(); i++) {
166 jobjectArray stateArray =
167 env->NewObjectArray(infos[i].states.size(), class_S, nullptr);
168 for (int j = 0; j < infos[i].states.size(); j++) {
169 jstring name = env->NewStringUTF(
170 infos[i].states[j].powerEntityStateName.c_str());
171 jobject state = env->NewObject(class_S, method_S_init);
172 env->SetIntField(state, field_S_id,
173 infos[i].states[j].powerEntityStateId);
174 env->SetObjectField(state, field_S_name, name);
175 env->SetObjectArrayElement(stateArray, j, state);
176 env->DeleteLocalRef(name);
177 env->DeleteLocalRef(state);
178 }
179
180 for (int j = 0; j < env->GetArrayLength(powerEntityArray); j++) {
181 jobject powerEntity =
182 env->GetObjectArrayElement(powerEntityArray, j);
183 if (env->GetIntField(powerEntity, field_PE_id) ==
184 infos[i].powerEntityId) {
185 env->SetObjectField(powerEntity, field_PE_states, stateArray);
186 env->SetObjectArrayElement(powerEntityArray, j, powerEntity);
187 break;
188 }
189 }
190 }
191 }
192 });
193 if (!checkResult(ret, __func__)) {
194 return nullptr;
195 }
196
197 return powerEntityArray;
198 }
199
nativeGetStateResidency(JNIEnv * env,jclass clazz,jintArray powerEntityIds)200 static jobjectArray nativeGetStateResidency(JNIEnv *env, jclass clazz, jintArray powerEntityIds) {
201 std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
202
203 if (!connectToPowerStatsHal()) {
204 ALOGE("nativeGetStateResidency failed to connect to power.stats HAL");
205 return nullptr;
206 }
207
208 size_t powerEntityIdCount = env->GetArrayLength(powerEntityIds);
209 hidl_vec<uint32_t> powerEntityIdVector(powerEntityIdCount);
210
211 jint *powerEntityIdElements = env->GetIntArrayElements(powerEntityIds, 0);
212 for (int i = 0; i < powerEntityIdCount; i++) {
213 powerEntityIdVector[i] = powerEntityIdElements[i];
214 }
215 env->ReleaseIntArrayElements(powerEntityIds, powerEntityIdElements, 0);
216
217 jobjectArray stateResidencyResultArray = nullptr;
218 Return<void> ret = gPowerStatsHalV1_0_ptr->getPowerEntityStateResidencyData(
219 powerEntityIdVector, [&env, &stateResidencyResultArray](auto results, auto status) {
220 stateResidencyResultArray = env->NewObjectArray(results.size(), class_SRR, nullptr);
221 for (int i = 0; i < results.size(); i++) {
222 jobjectArray stateResidencyArray =
223 env->NewObjectArray(results[i].stateResidencyData.size(), class_SR,
224 nullptr);
225 for (int j = 0; j < results[i].stateResidencyData.size(); j++) {
226 jobject stateResidency = env->NewObject(class_SR, method_SR_init);
227 env->SetIntField(stateResidency, field_SR_id,
228 results[i].stateResidencyData[j].powerEntityStateId);
229 env->SetLongField(stateResidency, field_SR_totalTimeInStateMs,
230 results[i].stateResidencyData[j].totalTimeInStateMs);
231 env->SetLongField(stateResidency, field_SR_totalStateEntryCount,
232 results[i].stateResidencyData[j].totalStateEntryCount);
233 env->SetLongField(stateResidency, field_SR_lastEntryTimestampMs,
234 results[i].stateResidencyData[j].lastEntryTimestampMs);
235 env->SetObjectArrayElement(stateResidencyArray, j, stateResidency);
236 env->DeleteLocalRef(stateResidency);
237 }
238 jobject stateResidencyResult = env->NewObject(class_SRR, method_SRR_init);
239 env->SetIntField(stateResidencyResult, field_SRR_id, results[i].powerEntityId);
240 env->SetObjectField(stateResidencyResult, field_SRR_stateResidencyData,
241 stateResidencyArray);
242 env->SetObjectArrayElement(stateResidencyResultArray, i, stateResidencyResult);
243 env->DeleteLocalRef(stateResidencyResult);
244 }
245 });
246 if (!checkResult(ret, __func__)) {
247 return nullptr;
248 }
249
250 return stateResidencyResultArray;
251 }
252
nativeGetEnergyMeterInfo(JNIEnv * env,jclass clazz)253 static jobjectArray nativeGetEnergyMeterInfo(JNIEnv *env, jclass clazz) {
254 std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
255
256 if (!connectToPowerStatsHal()) {
257 ALOGE("nativeGetEnergyMeterInfo failed to connect to power.stats HAL");
258 return nullptr;
259 }
260
261 jobjectArray channelArray = nullptr;
262 Return<void> ret =
263 gPowerStatsHalV1_0_ptr->getRailInfo([&env, &channelArray](auto railInfo, auto status) {
264 if (status != Status::SUCCESS) {
265 ALOGW("Error getting rail info");
266 } else {
267 channelArray = env->NewObjectArray(railInfo.size(), class_C, nullptr);
268 for (int i = 0; i < railInfo.size(); i++) {
269 jstring name = env->NewStringUTF(railInfo[i].railName.c_str());
270 jstring subsystem = env->NewStringUTF(railInfo[i].subsysName.c_str());
271 jobject channel = env->NewObject(class_C, method_C_init);
272 env->SetIntField(channel, field_C_id, railInfo[i].index);
273 env->SetObjectField(channel, field_C_name, name);
274 env->SetObjectField(channel, field_C_subsystem, subsystem);
275 env->SetObjectArrayElement(channelArray, i, channel);
276 env->DeleteLocalRef(name);
277 env->DeleteLocalRef(subsystem);
278 env->DeleteLocalRef(channel);
279 }
280 }
281 });
282
283 if (!checkResult(ret, __func__)) {
284 ALOGE("getRailInfo failed");
285 return nullptr;
286 }
287
288 return channelArray;
289 }
290
nativeReadEnergyMeters(JNIEnv * env,jclass clazz,jintArray channelIds)291 static jobjectArray nativeReadEnergyMeters(JNIEnv *env, jclass clazz, jintArray channelIds) {
292 std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
293
294 if (!connectToPowerStatsHal()) {
295 ALOGE("nativeGetEnergy failed to connect to power.stats HAL");
296 }
297
298 size_t channelIdCount = env->GetArrayLength(channelIds);
299 hidl_vec<uint32_t> channelIdVector(channelIdCount);
300
301 jint *channelIdElements = env->GetIntArrayElements(channelIds, 0);
302 for (int i = 0; i < channelIdCount; i++) {
303 channelIdVector[i] = channelIdElements[i];
304 }
305 env->ReleaseIntArrayElements(channelIds, channelIdElements, 0);
306
307 jobjectArray energyMeasurementArray = nullptr;
308 Return<void> ret =
309 gPowerStatsHalV1_0_ptr
310 ->getEnergyData(channelIdVector,
311 [&env, &energyMeasurementArray](auto energyData, auto status) {
312 energyMeasurementArray =
313 env->NewObjectArray(energyData.size(), class_EM,
314 nullptr);
315 for (int i = 0; i < energyData.size(); i++) {
316 jobject energyMeasurement =
317 env->NewObject(class_EM, method_EM_init);
318 env->SetIntField(energyMeasurement, field_EM_id,
319 energyData[i].index);
320 env->SetLongField(energyMeasurement,
321 field_EM_timestampMs,
322 energyData[i].timestamp);
323 env->SetLongField(energyMeasurement,
324 field_EM_durationMs,
325 energyData[i].timestamp);
326 env->SetLongField(energyMeasurement, field_EM_energyUWs,
327 energyData[i].energy);
328 env->SetObjectArrayElement(energyMeasurementArray, i,
329 energyMeasurement);
330 env->DeleteLocalRef(energyMeasurement);
331 }
332 });
333
334 if (!checkResult(ret, __func__)) {
335 ALOGE("getEnergyData failed");
336 return nullptr;
337 }
338
339 return energyMeasurementArray;
340 }
341
nativeInit(JNIEnv * env,jclass clazz)342 static jboolean nativeInit(JNIEnv *env, jclass clazz) {
343 std::lock_guard<std::mutex> lock(gPowerStatsHalMutex);
344
345 // Channel
346 jclass temp = env->FindClass("android/hardware/power/stats/Channel");
347 class_C = (jclass)env->NewGlobalRef(temp);
348 method_C_init = env->GetMethodID(class_C, "<init>", "()V");
349 field_C_id = env->GetFieldID(class_C, "id", "I");
350 field_C_name = env->GetFieldID(class_C, "name", "Ljava/lang/String;");
351 field_C_subsystem = env->GetFieldID(class_C, "subsystem", "Ljava/lang/String;");
352
353 // EnergyMeasurement
354 temp = env->FindClass("android/hardware/power/stats/EnergyMeasurement");
355 class_EM = (jclass)env->NewGlobalRef(temp);
356 method_EM_init = env->GetMethodID(class_EM, "<init>", "()V");
357 field_EM_id = env->GetFieldID(class_EM, "id", "I");
358 field_EM_timestampMs = env->GetFieldID(class_EM, "timestampMs", "J");
359 field_EM_durationMs = env->GetFieldID(class_EM, "durationMs", "J");
360 field_EM_energyUWs = env->GetFieldID(class_EM, "energyUWs", "J");
361
362 // State
363 temp = env->FindClass("android/hardware/power/stats/State");
364 class_S = (jclass)env->NewGlobalRef(temp);
365 method_S_init = env->GetMethodID(class_S, "<init>", "()V");
366 field_S_id = env->GetFieldID(class_S, "id", "I");
367 field_S_name = env->GetFieldID(class_S, "name", "Ljava/lang/String;");
368
369 // PowerEntity
370 temp = env->FindClass("android/hardware/power/stats/PowerEntity");
371 class_PE = (jclass)env->NewGlobalRef(temp);
372 method_PE_init = env->GetMethodID(class_PE, "<init>", "()V");
373 field_PE_id = env->GetFieldID(class_PE, "id", "I");
374 field_PE_name = env->GetFieldID(class_PE, "name", "Ljava/lang/String;");
375 field_PE_states = env->GetFieldID(class_PE, "states", "[Landroid/hardware/power/stats/State;");
376
377 // StateResidency
378 temp = env->FindClass("android/hardware/power/stats/StateResidency");
379 class_SR = (jclass)env->NewGlobalRef(temp);
380 method_SR_init = env->GetMethodID(class_SR, "<init>", "()V");
381 field_SR_id = env->GetFieldID(class_SR, "id", "I");
382 field_SR_totalTimeInStateMs = env->GetFieldID(class_SR, "totalTimeInStateMs", "J");
383 field_SR_totalStateEntryCount = env->GetFieldID(class_SR, "totalStateEntryCount", "J");
384 field_SR_lastEntryTimestampMs = env->GetFieldID(class_SR, "lastEntryTimestampMs", "J");
385
386 // StateResidencyResult
387 temp = env->FindClass("android/hardware/power/stats/StateResidencyResult");
388 class_SRR = (jclass)env->NewGlobalRef(temp);
389 method_SRR_init = env->GetMethodID(class_SRR, "<init>", "()V");
390 field_SRR_id = env->GetFieldID(class_SRR, "id", "I");
391 field_SRR_stateResidencyData =
392 env->GetFieldID(class_SRR, "stateResidencyData",
393 "[Landroid/hardware/power/stats/StateResidency;");
394
395 if (!connectToPowerStatsHal()) {
396 ALOGE("nativeInit failed to connect to power.stats HAL");
397 return false;
398 }
399
400 return true;
401 }
402
403 static const JNINativeMethod method_table[] = {
404 {"nativeInit", "()Z", (void *)nativeInit},
405 {"nativeGetPowerEntityInfo", "()[Landroid/hardware/power/stats/PowerEntity;",
406 (void *)nativeGetPowerEntityInfo},
407 {"nativeGetStateResidency", "([I)[Landroid/hardware/power/stats/StateResidencyResult;",
408 (void *)nativeGetStateResidency},
409 {"nativeGetEnergyMeterInfo", "()[Landroid/hardware/power/stats/Channel;",
410 (void *)nativeGetEnergyMeterInfo},
411 {"nativeReadEnergyMeters", "([I)[Landroid/hardware/power/stats/EnergyMeasurement;",
412 (void *)nativeReadEnergyMeters},
413 };
414
register_android_server_PowerStatsService(JNIEnv * env)415 int register_android_server_PowerStatsService(JNIEnv *env) {
416 return jniRegisterNativeMethods(env,
417 "com/android/server/powerstats/"
418 "PowerStatsHALWrapper$PowerStatsHAL10WrapperImpl",
419 method_table, NELEM(method_table));
420 }
421
422 }; // namespace android
423