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 "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
31namespace android {
32
33using hardware::hidl_vec;
34using hardware::thermal::V1_0::CoolingDevice;
35using hardware::thermal::V1_0::CpuUsage;
36using hardware::thermal::V1_0::IThermal;
37using hardware::thermal::V1_0::Temperature;
38using hardware::thermal::V1_0::ThermalStatus;
39using hardware::thermal::V1_0::ThermalStatusCode;
40template<typename T>
41using Return = hardware::Return<T>;
42
43// ---------------------------------------------------------------------------
44
45// These values must be kept in sync with the temperature source constants in
46// HardwarePropertiesManager.java
47enum {
48    TEMPERATURE_CURRENT = 0,
49    TEMPERATURE_THROTTLING = 1,
50    TEMPERATURE_SHUTDOWN = 2,
51    TEMPERATURE_THROTTLING_BELOW_VR_MIN = 3
52};
53
54static struct {
55    jclass clazz;
56    jmethodID initMethod;
57} gCpuUsageInfoClassInfo;
58
59jfloat gUndefinedTemperature;
60
61static sp<IThermal> gThermalModule;
62
63// ----------------------------------------------------------------------------
64
65float finalizeTemperature(float temperature) {
66    return isnan(temperature) ? gUndefinedTemperature : temperature;
67}
68
69static void nativeInit(JNIEnv* env, jobject obj) {
70    // TODO(b/31632518)
71    if (gThermalModule == nullptr) {
72        gThermalModule = IThermal::getService();
73    }
74
75    if (gThermalModule == nullptr) {
76        ALOGE("Unable to get Thermal service.");
77    }
78}
79
80static jfloatArray nativeGetFanSpeeds(JNIEnv *env, jclass /* clazz */) {
81    if (gThermalModule == nullptr) {
82        ALOGE("Couldn't get fan speeds because of HAL error.");
83        return env->NewFloatArray(0);
84    }
85
86    hidl_vec<CoolingDevice> list;
87    Return<void> ret = gThermalModule->getCoolingDevices(
88            [&list](ThermalStatus status, hidl_vec<CoolingDevice> devices) {
89                if (status.code == ThermalStatusCode::SUCCESS) {
90                    list = std::move(devices);
91                } else {
92                    ALOGE("Couldn't get fan speeds because of HAL error: %s",
93                          status.debugMessage.c_str());
94                }
95            });
96
97    if (!ret.isOk()) {
98        ALOGE("getCoolingDevices failed status: %s", ret.description().c_str());
99    }
100
101    float values[list.size()];
102    for (size_t i = 0; i < list.size(); ++i) {
103        values[i] = list[i].currentValue;
104    }
105    jfloatArray fanSpeeds = env->NewFloatArray(list.size());
106    env->SetFloatArrayRegion(fanSpeeds, 0, list.size(), values);
107    return fanSpeeds;
108}
109
110static jfloatArray nativeGetDeviceTemperatures(JNIEnv *env, jclass /* clazz */, int type,
111                                               int source) {
112    if (gThermalModule == nullptr) {
113        ALOGE("Couldn't get device temperatures because of HAL error.");
114        return env->NewFloatArray(0);
115    }
116    hidl_vec<Temperature> list;
117    Return<void> ret = gThermalModule->getTemperatures(
118            [&list](ThermalStatus status, hidl_vec<Temperature> temperatures) {
119                if (status.code == ThermalStatusCode::SUCCESS) {
120                    list = std::move(temperatures);
121                } else {
122                    ALOGE("Couldn't get temperatures because of HAL error: %s",
123                          status.debugMessage.c_str());
124                }
125            });
126
127    if (!ret.isOk()) {
128        ALOGE("getDeviceTemperatures failed status: %s", ret.description().c_str());
129    }
130
131    jfloat values[list.size()];
132    size_t length = 0;
133    for (size_t i = 0; i < list.size(); ++i) {
134        if (static_cast<int>(list[i].type) == type) {
135            switch (source) {
136                case TEMPERATURE_CURRENT:
137                    values[length++] = finalizeTemperature(list[i].currentValue);
138                    break;
139                case TEMPERATURE_THROTTLING:
140                    values[length++] = finalizeTemperature(list[i].throttlingThreshold);
141                    break;
142                case TEMPERATURE_SHUTDOWN:
143                    values[length++] = finalizeTemperature(list[i].shutdownThreshold);
144                    break;
145                case TEMPERATURE_THROTTLING_BELOW_VR_MIN:
146                    values[length++] = finalizeTemperature(list[i].vrThrottlingThreshold);
147                    break;
148            }
149        }
150    }
151    jfloatArray deviceTemps = env->NewFloatArray(length);
152    env->SetFloatArrayRegion(deviceTemps, 0, length, values);
153    return deviceTemps;
154}
155
156static jobjectArray nativeGetCpuUsages(JNIEnv *env, jclass /* clazz */) {
157    if (gThermalModule == nullptr || !gCpuUsageInfoClassInfo.initMethod) {
158        ALOGE("Couldn't get CPU usages because of HAL error.");
159        return env->NewObjectArray(0, gCpuUsageInfoClassInfo.clazz, nullptr);
160    }
161    hidl_vec<CpuUsage> list;
162    Return<void> ret = gThermalModule->getCpuUsages(
163            [&list](ThermalStatus status, hidl_vec<CpuUsage> cpuUsages) {
164                if (status.code == ThermalStatusCode::SUCCESS) {
165                    list = std::move(cpuUsages);
166                } else {
167                    ALOGE("Couldn't get CPU usages because of HAL error: %s",
168                          status.debugMessage.c_str());
169                }
170            });
171
172    if (!ret.isOk()) {
173        ALOGE("getCpuUsages failed status: %s", ret.description().c_str());
174    }
175
176    jobjectArray cpuUsages = env->NewObjectArray(list.size(), gCpuUsageInfoClassInfo.clazz,
177                                                 nullptr);
178    for (size_t i = 0; i < list.size(); ++i) {
179        if (list[i].isOnline) {
180            jobject cpuUsage = env->NewObject(gCpuUsageInfoClassInfo.clazz,
181                                              gCpuUsageInfoClassInfo.initMethod,
182                                              list[i].active,
183                                              list[i].total);
184            env->SetObjectArrayElement(cpuUsages, i, cpuUsage);
185        }
186    }
187    return cpuUsages;
188}
189
190// ----------------------------------------------------------------------------
191
192static const JNINativeMethod gHardwarePropertiesManagerServiceMethods[] = {
193    /* name, signature, funcPtr */
194    { "nativeInit", "()V",
195            (void*) nativeInit },
196    { "nativeGetFanSpeeds", "()[F",
197            (void*) nativeGetFanSpeeds },
198    { "nativeGetDeviceTemperatures", "(II)[F",
199            (void*) nativeGetDeviceTemperatures },
200    { "nativeGetCpuUsages", "()[Landroid/os/CpuUsageInfo;",
201            (void*) nativeGetCpuUsages }
202};
203
204int register_android_server_HardwarePropertiesManagerService(JNIEnv* env) {
205    gThermalModule = nullptr;
206    int res = jniRegisterNativeMethods(env, "com/android/server/HardwarePropertiesManagerService",
207                                       gHardwarePropertiesManagerServiceMethods,
208                                       NELEM(gHardwarePropertiesManagerServiceMethods));
209    jclass clazz = env->FindClass("android/os/CpuUsageInfo");
210    gCpuUsageInfoClassInfo.clazz = MakeGlobalRefOrDie(env, clazz);
211    gCpuUsageInfoClassInfo.initMethod = GetMethodIDOrDie(env, gCpuUsageInfoClassInfo.clazz,
212                                                         "<init>", "(JJ)V");
213
214    clazz = env->FindClass("android/os/HardwarePropertiesManager");
215    jfieldID undefined_temperature_field = GetStaticFieldIDOrDie(env, clazz,
216                                                                 "UNDEFINED_TEMPERATURE", "F");
217    gUndefinedTemperature = env->GetStaticFloatField(clazz, undefined_temperature_field);
218
219    return res;
220}
221
222} /* namespace android */
223