1/*
2 * Copyright (C) 2008 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 "BatteryService"
18
19#include "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 <sys/socket.h>
29#include <arpa/inet.h>
30#include <netinet/in.h>
31#include <stdlib.h>
32#include <errno.h>
33#include <unistd.h>
34#include <dirent.h>
35#include <linux/ioctl.h>
36
37namespace android {
38
39#define POWER_SUPPLY_PATH "/sys/class/power_supply"
40
41struct FieldIds {
42    // members
43    jfieldID mAcOnline;
44    jfieldID mUsbOnline;
45    jfieldID mBatteryStatus;
46    jfieldID mBatteryHealth;
47    jfieldID mBatteryPresent;
48    jfieldID mBatteryLevel;
49    jfieldID mBatteryVoltage;
50    jfieldID mBatteryTemperature;
51    jfieldID mBatteryTechnology;
52};
53static FieldIds gFieldIds;
54
55struct BatteryManagerConstants {
56    jint statusUnknown;
57    jint statusCharging;
58    jint statusDischarging;
59    jint statusNotCharging;
60    jint statusFull;
61    jint healthUnknown;
62    jint healthGood;
63    jint healthOverheat;
64    jint healthDead;
65    jint healthOverVoltage;
66    jint healthUnspecifiedFailure;
67    jint healthCold;
68};
69static BatteryManagerConstants gConstants;
70
71struct PowerSupplyPaths {
72    char* acOnlinePath;
73    char* usbOnlinePath;
74    char* batteryStatusPath;
75    char* batteryHealthPath;
76    char* batteryPresentPath;
77    char* batteryCapacityPath;
78    char* batteryVoltagePath;
79    char* batteryTemperaturePath;
80    char* batteryTechnologyPath;
81};
82static PowerSupplyPaths gPaths;
83
84static int gVoltageDivisor = 1;
85
86static jint getBatteryStatus(const char* status)
87{
88    switch (status[0]) {
89        case 'C': return gConstants.statusCharging;         // Charging
90        case 'D': return gConstants.statusDischarging;      // Discharging
91        case 'F': return gConstants.statusFull;             // Not charging
92        case 'N': return gConstants.statusNotCharging;      // Full
93        case 'U': return gConstants.statusUnknown;          // Unknown
94
95        default: {
96            LOGW("Unknown battery status '%s'", status);
97            return gConstants.statusUnknown;
98        }
99    }
100}
101
102static jint getBatteryHealth(const char* status)
103{
104    switch (status[0]) {
105        case 'C': return gConstants.healthCold;         // Cold
106        case 'D': return gConstants.healthDead;         // Dead
107        case 'G': return gConstants.healthGood;         // Good
108        case 'O': {
109            if (strcmp(status, "Overheat") == 0) {
110                return gConstants.healthOverheat;
111            } else if (strcmp(status, "Over voltage") == 0) {
112                return gConstants.healthOverVoltage;
113            }
114            LOGW("Unknown battery health[1] '%s'", status);
115            return gConstants.healthUnknown;
116        }
117
118        case 'U': {
119            if (strcmp(status, "Unspecified failure") == 0) {
120                return gConstants.healthUnspecifiedFailure;
121            } else if (strcmp(status, "Unknown") == 0) {
122                return gConstants.healthUnknown;
123            }
124            // fall through
125        }
126
127        default: {
128            LOGW("Unknown battery health[2] '%s'", status);
129            return gConstants.healthUnknown;
130        }
131    }
132}
133
134static int readFromFile(const char* path, char* buf, size_t size)
135{
136    if (!path)
137        return -1;
138    int fd = open(path, O_RDONLY, 0);
139    if (fd == -1) {
140        LOGE("Could not open '%s'", path);
141        return -1;
142    }
143
144    ssize_t count = read(fd, buf, size);
145    if (count > 0) {
146        while (count > 0 && buf[count-1] == '\n')
147            count--;
148        buf[count] = '\0';
149    } else {
150        buf[0] = '\0';
151    }
152
153    close(fd);
154    return count;
155}
156
157static void setBooleanField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID)
158{
159    const int SIZE = 16;
160    char buf[SIZE];
161
162    jboolean value = false;
163    if (readFromFile(path, buf, SIZE) > 0) {
164        if (buf[0] != '0') {
165            value = true;
166        }
167    }
168    env->SetBooleanField(obj, fieldID, value);
169}
170
171static void setIntField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID)
172{
173    const int SIZE = 128;
174    char buf[SIZE];
175
176    jint value = 0;
177    if (readFromFile(path, buf, SIZE) > 0) {
178        value = atoi(buf);
179    }
180    env->SetIntField(obj, fieldID, value);
181}
182
183static void setVoltageField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID)
184{
185    const int SIZE = 128;
186    char buf[SIZE];
187
188    jint value = 0;
189    if (readFromFile(path, buf, SIZE) > 0) {
190        value = atoi(buf);
191        value /= gVoltageDivisor;
192    }
193    env->SetIntField(obj, fieldID, value);
194}
195
196
197static void android_server_BatteryService_update(JNIEnv* env, jobject obj)
198{
199    setBooleanField(env, obj, gPaths.acOnlinePath, gFieldIds.mAcOnline);
200    setBooleanField(env, obj, gPaths.usbOnlinePath, gFieldIds.mUsbOnline);
201    setBooleanField(env, obj, gPaths.batteryPresentPath, gFieldIds.mBatteryPresent);
202
203    setIntField(env, obj, gPaths.batteryCapacityPath, gFieldIds.mBatteryLevel);
204    setVoltageField(env, obj, gPaths.batteryVoltagePath, gFieldIds.mBatteryVoltage);
205    setIntField(env, obj, gPaths.batteryTemperaturePath, gFieldIds.mBatteryTemperature);
206
207    const int SIZE = 128;
208    char buf[SIZE];
209
210    if (readFromFile(gPaths.batteryStatusPath, buf, SIZE) > 0)
211        env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf));
212    else
213        env->SetIntField(obj, gFieldIds.mBatteryStatus,
214                         gConstants.statusUnknown);
215
216    if (readFromFile(gPaths.batteryHealthPath, buf, SIZE) > 0)
217        env->SetIntField(obj, gFieldIds.mBatteryHealth, getBatteryHealth(buf));
218
219    if (readFromFile(gPaths.batteryTechnologyPath, buf, SIZE) > 0)
220        env->SetObjectField(obj, gFieldIds.mBatteryTechnology, env->NewStringUTF(buf));
221}
222
223static JNINativeMethod sMethods[] = {
224     /* name, signature, funcPtr */
225	{"native_update", "()V", (void*)android_server_BatteryService_update},
226};
227
228int register_android_server_BatteryService(JNIEnv* env)
229{
230    char    path[PATH_MAX];
231    struct dirent* entry;
232
233    DIR* dir = opendir(POWER_SUPPLY_PATH);
234    if (dir == NULL) {
235        LOGE("Could not open %s\n", POWER_SUPPLY_PATH);
236        return -1;
237    }
238    while ((entry = readdir(dir))) {
239        const char* name = entry->d_name;
240
241        // ignore "." and ".."
242        if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
243            continue;
244        }
245
246        char buf[20];
247        // Look for "type" file in each subdirectory
248        snprintf(path, sizeof(path), "%s/%s/type", POWER_SUPPLY_PATH, name);
249        int length = readFromFile(path, buf, sizeof(buf));
250        if (length > 0) {
251            if (buf[length - 1] == '\n')
252                buf[length - 1] = 0;
253
254            if (strcmp(buf, "Mains") == 0) {
255                snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
256                if (access(path, R_OK) == 0)
257                    gPaths.acOnlinePath = strdup(path);
258            }
259            else if (strcmp(buf, "USB") == 0) {
260                snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
261                if (access(path, R_OK) == 0)
262                    gPaths.usbOnlinePath = strdup(path);
263            }
264            else if (strcmp(buf, "Battery") == 0) {
265                snprintf(path, sizeof(path), "%s/%s/status", POWER_SUPPLY_PATH, name);
266                if (access(path, R_OK) == 0)
267                    gPaths.batteryStatusPath = strdup(path);
268                snprintf(path, sizeof(path), "%s/%s/health", POWER_SUPPLY_PATH, name);
269                if (access(path, R_OK) == 0)
270                    gPaths.batteryHealthPath = strdup(path);
271                snprintf(path, sizeof(path), "%s/%s/present", POWER_SUPPLY_PATH, name);
272                if (access(path, R_OK) == 0)
273                    gPaths.batteryPresentPath = strdup(path);
274                snprintf(path, sizeof(path), "%s/%s/capacity", POWER_SUPPLY_PATH, name);
275                if (access(path, R_OK) == 0)
276                    gPaths.batteryCapacityPath = strdup(path);
277
278                snprintf(path, sizeof(path), "%s/%s/voltage_now", POWER_SUPPLY_PATH, name);
279                if (access(path, R_OK) == 0) {
280                    gPaths.batteryVoltagePath = strdup(path);
281                    // voltage_now is in microvolts, not millivolts
282                    gVoltageDivisor = 1000;
283                } else {
284                    snprintf(path, sizeof(path), "%s/%s/batt_vol", POWER_SUPPLY_PATH, name);
285                    if (access(path, R_OK) == 0)
286                        gPaths.batteryVoltagePath = strdup(path);
287                }
288
289                snprintf(path, sizeof(path), "%s/%s/temp", POWER_SUPPLY_PATH, name);
290                if (access(path, R_OK) == 0) {
291                    gPaths.batteryTemperaturePath = strdup(path);
292                } else {
293                    snprintf(path, sizeof(path), "%s/%s/batt_temp", POWER_SUPPLY_PATH, name);
294                    if (access(path, R_OK) == 0)
295                        gPaths.batteryTemperaturePath = strdup(path);
296                }
297
298                snprintf(path, sizeof(path), "%s/%s/technology", POWER_SUPPLY_PATH, name);
299                if (access(path, R_OK) == 0)
300                    gPaths.batteryTechnologyPath = strdup(path);
301            }
302        }
303    }
304    closedir(dir);
305
306    if (!gPaths.acOnlinePath)
307        LOGE("acOnlinePath not found");
308    if (!gPaths.usbOnlinePath)
309        LOGE("usbOnlinePath not found");
310    if (!gPaths.batteryStatusPath)
311        LOGE("batteryStatusPath not found");
312    if (!gPaths.batteryHealthPath)
313        LOGE("batteryHealthPath not found");
314    if (!gPaths.batteryPresentPath)
315        LOGE("batteryPresentPath not found");
316    if (!gPaths.batteryCapacityPath)
317        LOGE("batteryCapacityPath not found");
318    if (!gPaths.batteryVoltagePath)
319        LOGE("batteryVoltagePath not found");
320    if (!gPaths.batteryTemperaturePath)
321        LOGE("batteryTemperaturePath not found");
322    if (!gPaths.batteryTechnologyPath)
323        LOGE("batteryTechnologyPath not found");
324
325    jclass clazz = env->FindClass("com/android/server/BatteryService");
326
327    if (clazz == NULL) {
328        LOGE("Can't find com/android/server/BatteryService");
329        return -1;
330    }
331
332    gFieldIds.mAcOnline = env->GetFieldID(clazz, "mAcOnline", "Z");
333    gFieldIds.mUsbOnline = env->GetFieldID(clazz, "mUsbOnline", "Z");
334    gFieldIds.mBatteryStatus = env->GetFieldID(clazz, "mBatteryStatus", "I");
335    gFieldIds.mBatteryHealth = env->GetFieldID(clazz, "mBatteryHealth", "I");
336    gFieldIds.mBatteryPresent = env->GetFieldID(clazz, "mBatteryPresent", "Z");
337    gFieldIds.mBatteryLevel = env->GetFieldID(clazz, "mBatteryLevel", "I");
338    gFieldIds.mBatteryTechnology = env->GetFieldID(clazz, "mBatteryTechnology", "Ljava/lang/String;");
339    gFieldIds.mBatteryVoltage = env->GetFieldID(clazz, "mBatteryVoltage", "I");
340    gFieldIds.mBatteryTemperature = env->GetFieldID(clazz, "mBatteryTemperature", "I");
341
342    LOG_FATAL_IF(gFieldIds.mAcOnline == NULL, "Unable to find BatteryService.AC_ONLINE_PATH");
343    LOG_FATAL_IF(gFieldIds.mUsbOnline == NULL, "Unable to find BatteryService.USB_ONLINE_PATH");
344    LOG_FATAL_IF(gFieldIds.mBatteryStatus == NULL, "Unable to find BatteryService.BATTERY_STATUS_PATH");
345    LOG_FATAL_IF(gFieldIds.mBatteryHealth == NULL, "Unable to find BatteryService.BATTERY_HEALTH_PATH");
346    LOG_FATAL_IF(gFieldIds.mBatteryPresent == NULL, "Unable to find BatteryService.BATTERY_PRESENT_PATH");
347    LOG_FATAL_IF(gFieldIds.mBatteryLevel == NULL, "Unable to find BatteryService.BATTERY_CAPACITY_PATH");
348    LOG_FATAL_IF(gFieldIds.mBatteryVoltage == NULL, "Unable to find BatteryService.BATTERY_VOLTAGE_PATH");
349    LOG_FATAL_IF(gFieldIds.mBatteryTemperature == NULL, "Unable to find BatteryService.BATTERY_TEMPERATURE_PATH");
350    LOG_FATAL_IF(gFieldIds.mBatteryTechnology == NULL, "Unable to find BatteryService.BATTERY_TECHNOLOGY_PATH");
351
352    clazz = env->FindClass("android/os/BatteryManager");
353
354    if (clazz == NULL) {
355        LOGE("Can't find android/os/BatteryManager");
356        return -1;
357    }
358
359    gConstants.statusUnknown = env->GetStaticIntField(clazz,
360            env->GetStaticFieldID(clazz, "BATTERY_STATUS_UNKNOWN", "I"));
361
362    gConstants.statusCharging = env->GetStaticIntField(clazz,
363            env->GetStaticFieldID(clazz, "BATTERY_STATUS_CHARGING", "I"));
364
365    gConstants.statusDischarging = env->GetStaticIntField(clazz,
366            env->GetStaticFieldID(clazz, "BATTERY_STATUS_DISCHARGING", "I"));
367
368    gConstants.statusNotCharging = env->GetStaticIntField(clazz,
369            env->GetStaticFieldID(clazz, "BATTERY_STATUS_NOT_CHARGING", "I"));
370
371    gConstants.statusFull = env->GetStaticIntField(clazz,
372            env->GetStaticFieldID(clazz, "BATTERY_STATUS_FULL", "I"));
373
374    gConstants.healthUnknown = env->GetStaticIntField(clazz,
375            env->GetStaticFieldID(clazz, "BATTERY_HEALTH_UNKNOWN", "I"));
376
377    gConstants.healthGood = env->GetStaticIntField(clazz,
378            env->GetStaticFieldID(clazz, "BATTERY_HEALTH_GOOD", "I"));
379
380    gConstants.healthOverheat = env->GetStaticIntField(clazz,
381            env->GetStaticFieldID(clazz, "BATTERY_HEALTH_OVERHEAT", "I"));
382
383    gConstants.healthDead = env->GetStaticIntField(clazz,
384            env->GetStaticFieldID(clazz, "BATTERY_HEALTH_DEAD", "I"));
385
386    gConstants.healthOverVoltage = env->GetStaticIntField(clazz,
387            env->GetStaticFieldID(clazz, "BATTERY_HEALTH_OVER_VOLTAGE", "I"));
388
389    gConstants.healthUnspecifiedFailure = env->GetStaticIntField(clazz,
390            env->GetStaticFieldID(clazz, "BATTERY_HEALTH_UNSPECIFIED_FAILURE", "I"));
391
392    gConstants.healthCold = env->GetStaticIntField(clazz,
393            env->GetStaticFieldID(clazz, "BATTERY_HEALTH_COLD", "I"));
394
395    return jniRegisterNativeMethods(env, "com/android/server/BatteryService", sMethods, NELEM(sMethods));
396}
397
398} /* namespace android */
399