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