1/*
2 * Copyright (C) 2014 Intel Corporation
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 "ThermalManagerJNI"
18
19#include <nativehelper/JNIHelp.h>
20#include "jni.h"
21#include <utils/Log.h>
22#include <utils/misc.h>
23
24#include <fcntl.h>
25#include <stdio.h>
26#include <string.h>
27#include <sys/types.h>
28#include <stdlib.h>
29#include <unistd.h>
30
31namespace android {
32
33#define THERMAL_ZONE_PATH "/sys/class/thermal/thermal_zone"
34#define COOLING_DEV_PATH  "/sys/class/thermal/cooling_device"
35
36#define UNUSED(expr) (void)(expr)
37
38static int readFromFile(const char *path, char* buf, size_t size, bool throwError)
39{
40    if (!path)
41        return -1;
42
43    int fd = open(path, O_RDONLY, 0);
44    if (fd < 0) {
45        if (throwError) {
46            ALOGE("Could not open '%s'", path);
47        }
48        return -1;
49    }
50
51    ssize_t count = read(fd, buf, size);
52    if (count > 0) {
53        while (count > 0 && buf[count-1] == '\n')
54            count--;
55        buf[count] = '\0';
56    } else {
57        buf[0] = '\0';
58    }
59
60    close(fd);
61    return count;
62}
63
64static int writeToFile(const char *path, int val)
65{
66    const int SIZE = 20;
67    int ret, fd, len;
68    char value[SIZE];
69
70    if (!path)
71        return -1;
72
73    fd = open(path, O_WRONLY, 0);
74    if (fd < 0) {
75        ALOGE("writeToFile: Could not open '%s' err: %d", path, errno);
76        return -1;
77    }
78
79    len = snprintf(value, SIZE, "%d\n", val);
80    ret = write(fd, value, len);
81
82    close(fd);
83    return (ret == len) ? 0 : -1;
84}
85
86static int lookup(const char *base_path, const char *name)
87{
88    const int SIZE = 128;
89    char buf[SIZE];
90    char full_path[SIZE];
91    int count = 0;
92
93    do {
94        snprintf(full_path, SIZE, "%s%d/type", base_path, count);
95        // Loop through all thermal_zones or cooling_devices until we
96        // find a first match. We call it a match when the given
97        // 'name' of the thermal_zone (or a cooling_device) matches
98        // with the value of 'type' sysfs interface of a thermal_zone
99        // (or cooling_device).
100        if (readFromFile(full_path, buf, SIZE, false) < 0) break;
101
102        if (!strcmp(name, buf)) return count;
103
104        count++;
105    } while(1);
106
107    // lookup failed.
108    return -1;
109}
110
111static int lookup_contains(const char *base_path, const char *name)
112{
113    const int SIZE = 128;
114    char buf[SIZE];
115    char full_path[SIZE];
116    int count = 0;
117
118    do {
119        snprintf(full_path, SIZE, "%s%d/type", base_path, count);
120        if (readFromFile(full_path, buf, SIZE, false) < 0) break;
121        // Check if 'buf' contains 'name'
122        if (strstr(buf, name) != NULL) return count;
123
124        count++;
125    } while(1);
126
127    // lookup failed.
128    return -1;
129}
130
131static jboolean isFileExists(JNIEnv* env, jobject obj, jstring jPath)
132{
133    const char *path = NULL;
134    jboolean ret = true;
135    UNUSED(obj);
136
137    path = jPath ? env->GetStringUTFChars(jPath, NULL) : NULL;
138    if (!path) {
139        return false;
140    }
141
142    int fd = open(path, O_RDONLY, 0);
143
144    if (fd < 0) {
145        ret = false;
146    } else {
147        close(fd);
148    }
149    env->ReleaseStringUTFChars(jPath, path);
150    return ret;
151}
152
153static jint getThermalZoneIndex(JNIEnv* env, jobject obj, jstring jType)
154{
155    int ret;
156    const char *type = NULL;
157    UNUSED(obj);
158
159    type = jType ? env->GetStringUTFChars(jType, NULL) : NULL;
160    if (!type) {
161        jniThrowNullPointerException(env, "Type");
162        return -1;
163    }
164
165    ret = lookup(THERMAL_ZONE_PATH, type);
166    env->ReleaseStringUTFChars(jType, type);
167    return ret;
168}
169
170static jint getThermalZoneIndexContains(JNIEnv* env, jobject obj, jstring jType)
171{
172    int ret;
173    const char *type = NULL;
174    UNUSED(obj);
175
176    type = jType ? env->GetStringUTFChars(jType, NULL) : NULL;
177    if (!type) {
178        jniThrowNullPointerException(env, "Type");
179        return -1;
180    }
181
182    ret = lookup_contains(THERMAL_ZONE_PATH, type);
183    env->ReleaseStringUTFChars(jType, type);
184    return ret;
185}
186
187static jint getCoolingDeviceIndex(JNIEnv* env, jobject obj, jstring jType)
188{
189    int ret;
190    const char *type = NULL;
191    UNUSED(obj);
192
193    type = jType ? env->GetStringUTFChars(jType, NULL) : NULL;
194    if (!type) {
195        jniThrowNullPointerException(env, "Type");
196        return -1;
197    }
198
199    ret = lookup(COOLING_DEV_PATH, type);
200    env->ReleaseStringUTFChars(jType, type);
201    return ret;
202}
203
204static jint getCoolingDeviceIndexContains(JNIEnv* env, jobject obj, jstring jType)
205{
206    int ret;
207    const char *type = NULL;
208    UNUSED(obj);
209
210    type = jType ? env->GetStringUTFChars(jType, NULL) : NULL;
211    if (!type) {
212        jniThrowNullPointerException(env, "Type");
213        return -1;
214    }
215
216    ret = lookup_contains(COOLING_DEV_PATH, type);
217    env->ReleaseStringUTFChars(jType, type);
218    return ret;
219}
220
221static jint writeSysfs(JNIEnv* env, jobject obj, jstring jPath, jint jVal)
222{
223    int ret;
224    const char *path = NULL;
225    UNUSED(obj);
226
227    path = jPath ? env->GetStringUTFChars(jPath, NULL) : NULL;
228    if (!path) {
229        jniThrowNullPointerException(env, "path");
230        return -EINVAL;
231    }
232
233    ret = writeToFile(path, jVal);
234    env->ReleaseStringUTFChars(jPath, path);
235    return ret;
236}
237
238static jstring readSysfs(JNIEnv* env, jobject obj, jstring jPath)
239{
240    const char *path = NULL;
241    const int SIZE = 512;
242    char buf[SIZE];
243    UNUSED(obj);
244
245    path = jPath ? env->GetStringUTFChars(jPath, NULL) : NULL;
246    if (!path) {
247        jniThrowNullPointerException(env, "path");
248        return NULL;
249    }
250
251    if (readFromFile(path, buf, SIZE, true) > 0) {
252        env->ReleaseStringUTFChars(jPath, path);
253        return env->NewStringUTF(buf);
254    } else {
255        env->ReleaseStringUTFChars(jPath, path);
256        return NULL;
257    }
258}
259
260static jint readSysfsTemp(JNIEnv* env, jobject obj, jstring jPath)
261{
262    const char *path = NULL;
263    const int SIZE = 64;
264    char buf[SIZE];
265    // Convention: To allow returning of normal negative temperatures
266    // (say -10C), let us return errno as a negative offset from
267    // absolute zero millidegree C.
268    const int ABS_ZERO = -273000;
269    int ret;
270    UNUSED(obj);
271
272    path = jPath ? env->GetStringUTFChars(jPath, NULL) : NULL;
273    if (!path) {
274        jniThrowNullPointerException(env, "path");
275        return (ABS_ZERO - ENOENT);
276    }
277
278    ret = readFromFile(path, buf, SIZE, true);
279    env->ReleaseStringUTFChars(jPath, path);
280    if (ret > 0) {
281        return atoi(buf);
282    }
283    return (ret + ABS_ZERO);
284}
285
286static JNINativeMethod sMethods[] = {
287     /* name, signature, funcPtr */
288        {"native_readSysfs", "(Ljava/lang/String;)Ljava/lang/String;", (void*)readSysfs},
289        {"native_readSysfsTemp", "(Ljava/lang/String;)I", (void*)readSysfsTemp},
290        {"native_writeSysfs", "(Ljava/lang/String;I)I", (void*)writeSysfs},
291        {"native_getThermalZoneIndex", "(Ljava/lang/String;)I", (void*)getThermalZoneIndex},
292        {"native_getThermalZoneIndexContains", "(Ljava/lang/String;)I",
293                 (void*)getThermalZoneIndexContains},
294        {"native_getCoolingDeviceIndex", "(Ljava/lang/String;)I", (void*)getCoolingDeviceIndex},
295        {"native_getCoolingDeviceIndexContains", "(Ljava/lang/String;)I",
296                 (void*)getCoolingDeviceIndexContains},
297        {"native_isFileExists", "(Ljava/lang/String;)Z", (void*)isFileExists},
298};
299
300int register_intel_thermal_ituxd(JNIEnv* env)
301{
302    jclass clazz = env->FindClass("com/intel/thermal/ThermalUtils");
303    if (clazz == NULL) {
304        ALOGE("Can't find com/intel/thermal/ThermalUtils");
305        return -1;
306    }
307
308    return jniRegisterNativeMethods(env, "com/intel/thermal/ThermalUtils",
309            sMethods, NELEM(sMethods));
310}
311
312} /* namespace android */
313