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