1 /*
2  * Copyright (C) 2015 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 "HardwarePropertiesManagerService-JNI"
18 
19 #include <nativehelper/JNIHelp.h>
20 #include "jni.h"
21 
22 #include <math.h>
23 #include <stdlib.h>
24 
25 #include <android/hardware/thermal/1.0/IThermal.h>
26 #include <utils/Log.h>
27 #include <utils/String8.h>
28 
29 #include "core_jni_helpers.h"
30 
31 namespace android {
32 
33 using android::hidl::base::V1_0::IBase;
34 using hardware::hidl_death_recipient;
35 using hardware::hidl_vec;
36 using hardware::thermal::V1_0::CoolingDevice;
37 using hardware::thermal::V1_0::CpuUsage;
38 using hardware::thermal::V1_0::IThermal;
39 using hardware::thermal::V1_0::Temperature;
40 using hardware::thermal::V1_0::ThermalStatus;
41 using hardware::thermal::V1_0::ThermalStatusCode;
42 template<typename T>
43 using Return = hardware::Return<T>;
44 
45 // ---------------------------------------------------------------------------
46 
47 // These values must be kept in sync with the temperature source constants in
48 // HardwarePropertiesManager.java
49 enum {
50     TEMPERATURE_CURRENT = 0,
51     TEMPERATURE_THROTTLING = 1,
52     TEMPERATURE_SHUTDOWN = 2,
53     TEMPERATURE_THROTTLING_BELOW_VR_MIN = 3
54 };
55 
56 static struct {
57     jclass clazz;
58     jmethodID initMethod;
59 } gCpuUsageInfoClassInfo;
60 
61 jfloat gUndefinedTemperature;
62 
63 static void getThermalHalLocked();
64 static std::mutex gThermalHalMutex;
65 static sp<IThermal> gThermalHal = nullptr;
66 
67 // struct ThermalHalDeathRecipient;
68 struct ThermalHalDeathRecipient : virtual public hidl_death_recipient {
69       // hidl_death_recipient interface
serviceDiedandroid::ThermalHalDeathRecipient70       virtual void serviceDied(uint64_t cookie, const wp<IBase>& who) override {
71           std::lock_guard<std::mutex> lock(gThermalHalMutex);
72           ALOGE("ThermalHAL just died");
73           gThermalHal = nullptr;
74           getThermalHalLocked();
75       }
76 };
77 
78 sp<ThermalHalDeathRecipient> gThermalHalDeathRecipient = nullptr;
79 
80 // ----------------------------------------------------------------------------
81 
finalizeTemperature(float temperature)82 float finalizeTemperature(float temperature) {
83     return isnan(temperature) ? gUndefinedTemperature : temperature;
84 }
85 
86 // The caller must be holding gThermalHalMutex.
getThermalHalLocked()87 static void getThermalHalLocked() {
88     if (gThermalHal != nullptr) {
89         return;
90     }
91 
92     gThermalHal = IThermal::getService();
93 
94     if (gThermalHal == nullptr) {
95         ALOGE("Unable to get Thermal service.");
96     } else {
97         if (gThermalHalDeathRecipient == nullptr) {
98             gThermalHalDeathRecipient = new ThermalHalDeathRecipient();
99         }
100         hardware::Return<bool> linked = gThermalHal->linkToDeath(
101             gThermalHalDeathRecipient, 0x451F /* cookie */);
102         if (!linked.isOk()) {
103             ALOGE("Transaction error in linking to ThermalHAL death: %s",
104             linked.description().c_str());
105             gThermalHal = nullptr;
106         } else if (!linked) {
107             ALOGW("Unable to link to ThermalHal death notifications");
108             gThermalHal = nullptr;
109         } else {
110             ALOGD("Link to death notification successful");
111         }
112     }
113 }
114 
nativeInit(JNIEnv * env,jobject obj)115 static void nativeInit(JNIEnv* env, jobject obj) {
116     std::lock_guard<std::mutex> lock(gThermalHalMutex);
117     getThermalHalLocked();
118 }
119 
nativeGetFanSpeeds(JNIEnv * env,jclass)120 static jfloatArray nativeGetFanSpeeds(JNIEnv *env, jclass /* clazz */) {
121     std::lock_guard<std::mutex> lock(gThermalHalMutex);
122     getThermalHalLocked();
123     if (gThermalHal == nullptr) {
124         ALOGE("Couldn't get fan speeds because of HAL error.");
125         return env->NewFloatArray(0);
126     }
127 
128     hidl_vec<CoolingDevice> list;
129     Return<void> ret = gThermalHal->getCoolingDevices(
130             [&list](ThermalStatus status, hidl_vec<CoolingDevice> devices) {
131                 if (status.code == ThermalStatusCode::SUCCESS) {
132                     list = std::move(devices);
133                 } else {
134                     ALOGE("Couldn't get fan speeds because of HAL error: %s",
135                           status.debugMessage.c_str());
136                 }
137             });
138 
139     if (!ret.isOk()) {
140         ALOGE("getCoolingDevices failed status: %s", ret.description().c_str());
141     }
142 
143     float values[list.size()];
144     for (size_t i = 0; i < list.size(); ++i) {
145         values[i] = list[i].currentValue;
146     }
147     jfloatArray fanSpeeds = env->NewFloatArray(list.size());
148     env->SetFloatArrayRegion(fanSpeeds, 0, list.size(), values);
149     return fanSpeeds;
150 }
151 
nativeGetDeviceTemperatures(JNIEnv * env,jclass,int type,int source)152 static jfloatArray nativeGetDeviceTemperatures(JNIEnv *env, jclass /* clazz */, int type,
153                                                int source) {
154     std::lock_guard<std::mutex> lock(gThermalHalMutex);
155     getThermalHalLocked();
156     if (gThermalHal == nullptr) {
157         ALOGE("Couldn't get device temperatures because of HAL error.");
158         return env->NewFloatArray(0);
159     }
160     hidl_vec<Temperature> list;
161     Return<void> ret = gThermalHal->getTemperatures(
162             [&list](ThermalStatus status, hidl_vec<Temperature> temperatures) {
163                 if (status.code == ThermalStatusCode::SUCCESS) {
164                     list = std::move(temperatures);
165                 } else {
166                     ALOGE("Couldn't get temperatures because of HAL error: %s",
167                           status.debugMessage.c_str());
168                 }
169             });
170 
171     if (!ret.isOk()) {
172         ALOGE("getDeviceTemperatures failed status: %s", ret.description().c_str());
173     }
174 
175     jfloat values[list.size()];
176     size_t length = 0;
177     for (size_t i = 0; i < list.size(); ++i) {
178         if (static_cast<int>(list[i].type) == type) {
179             switch (source) {
180                 case TEMPERATURE_CURRENT:
181                     values[length++] = finalizeTemperature(list[i].currentValue);
182                     break;
183                 case TEMPERATURE_THROTTLING:
184                     values[length++] = finalizeTemperature(list[i].throttlingThreshold);
185                     break;
186                 case TEMPERATURE_SHUTDOWN:
187                     values[length++] = finalizeTemperature(list[i].shutdownThreshold);
188                     break;
189                 case TEMPERATURE_THROTTLING_BELOW_VR_MIN:
190                     values[length++] = finalizeTemperature(list[i].vrThrottlingThreshold);
191                     break;
192             }
193         }
194     }
195     jfloatArray deviceTemps = env->NewFloatArray(length);
196     env->SetFloatArrayRegion(deviceTemps, 0, length, values);
197     return deviceTemps;
198 }
199 
nativeGetCpuUsages(JNIEnv * env,jclass)200 static jobjectArray nativeGetCpuUsages(JNIEnv *env, jclass /* clazz */) {
201     std::lock_guard<std::mutex> lock(gThermalHalMutex);
202     getThermalHalLocked();
203     if (gThermalHal == nullptr || !gCpuUsageInfoClassInfo.initMethod) {
204         ALOGE("Couldn't get CPU usages because of HAL error.");
205         return env->NewObjectArray(0, gCpuUsageInfoClassInfo.clazz, nullptr);
206     }
207     hidl_vec<CpuUsage> list;
208     Return<void> ret = gThermalHal->getCpuUsages(
209             [&list](ThermalStatus status, hidl_vec<CpuUsage> cpuUsages) {
210                 if (status.code == ThermalStatusCode::SUCCESS) {
211                     list = std::move(cpuUsages);
212                 } else {
213                     ALOGE("Couldn't get CPU usages because of HAL error: %s",
214                           status.debugMessage.c_str());
215                 }
216             });
217 
218     if (!ret.isOk()) {
219         ALOGE("getCpuUsages failed status: %s", ret.description().c_str());
220     }
221 
222     jobjectArray cpuUsages = env->NewObjectArray(list.size(), gCpuUsageInfoClassInfo.clazz,
223                                                  nullptr);
224     for (size_t i = 0; i < list.size(); ++i) {
225         if (list[i].isOnline) {
226             jobject cpuUsage = env->NewObject(gCpuUsageInfoClassInfo.clazz,
227                                               gCpuUsageInfoClassInfo.initMethod,
228                                               list[i].active,
229                                               list[i].total);
230             env->SetObjectArrayElement(cpuUsages, i, cpuUsage);
231         }
232     }
233     return cpuUsages;
234 }
235 
236 // ----------------------------------------------------------------------------
237 
238 static const JNINativeMethod gHardwarePropertiesManagerServiceMethods[] = {
239     /* name, signature, funcPtr */
240     { "nativeInit", "()V",
241             (void*) nativeInit },
242     { "nativeGetFanSpeeds", "()[F",
243             (void*) nativeGetFanSpeeds },
244     { "nativeGetDeviceTemperatures", "(II)[F",
245             (void*) nativeGetDeviceTemperatures },
246     { "nativeGetCpuUsages", "()[Landroid/os/CpuUsageInfo;",
247             (void*) nativeGetCpuUsages }
248 };
249 
register_android_server_HardwarePropertiesManagerService(JNIEnv * env)250 int register_android_server_HardwarePropertiesManagerService(JNIEnv* env) {
251     int res = jniRegisterNativeMethods(env, "com/android/server/HardwarePropertiesManagerService",
252                                        gHardwarePropertiesManagerServiceMethods,
253                                        NELEM(gHardwarePropertiesManagerServiceMethods));
254     jclass clazz = env->FindClass("android/os/CpuUsageInfo");
255     gCpuUsageInfoClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
256     gCpuUsageInfoClassInfo.initMethod = GetMethodIDOrDie(env, gCpuUsageInfoClassInfo.clazz,
257                                                          "<init>", "(JJ)V");
258 
259     clazz = env->FindClass("android/os/HardwarePropertiesManager");
260     jfieldID undefined_temperature_field = GetStaticFieldIDOrDie(env, clazz,
261                                                                  "UNDEFINED_TEMPERATURE", "F");
262     gUndefinedTemperature = env->GetStaticFloatField(clazz, undefined_temperature_field);
263 
264     return res;
265 }
266 
267 } /* namespace android */
268