blob: d4513e934331d62a007e6c637edf44cf7aa7d2dc [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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"
Mathias Agopian25ba5b62009-05-18 15:08:03 -070021#include <utils/Log.h>
22#include <utils/misc.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023
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>
Mike Lockwood304928f2009-08-17 17:16:20 -040034#include <dirent.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035
36#if HAVE_ANDROID_OS
37#include <linux/ioctl.h>
38#endif
39
40namespace android {
41
Mike Lockwood304928f2009-08-17 17:16:20 -040042#define POWER_SUPPLY_PATH "/sys/class/power_supply"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043
44struct FieldIds {
45 // members
46 jfieldID mAcOnline;
47 jfieldID mUsbOnline;
48 jfieldID mBatteryStatus;
49 jfieldID mBatteryHealth;
50 jfieldID mBatteryPresent;
51 jfieldID mBatteryLevel;
52 jfieldID mBatteryVoltage;
53 jfieldID mBatteryTemperature;
54 jfieldID mBatteryTechnology;
55};
56static FieldIds gFieldIds;
57
58struct BatteryManagerConstants {
59 jint statusUnknown;
60 jint statusCharging;
61 jint statusDischarging;
62 jint statusNotCharging;
63 jint statusFull;
64 jint healthUnknown;
65 jint healthGood;
66 jint healthOverheat;
67 jint healthDead;
68 jint healthOverVoltage;
69 jint healthUnspecifiedFailure;
Imre Sunyi92396122010-09-20 18:02:50 +020070 jint healthCold;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071};
72static BatteryManagerConstants gConstants;
73
Mike Lockwood304928f2009-08-17 17:16:20 -040074struct PowerSupplyPaths {
75 char* acOnlinePath;
76 char* usbOnlinePath;
77 char* batteryStatusPath;
78 char* batteryHealthPath;
79 char* batteryPresentPath;
80 char* batteryCapacityPath;
81 char* batteryVoltagePath;
82 char* batteryTemperaturePath;
83 char* batteryTechnologyPath;
84};
85static PowerSupplyPaths gPaths;
86
Mike Lockwoodf24d13a2009-08-17 18:11:14 -040087static int gVoltageDivisor = 1;
88
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089static jint getBatteryStatus(const char* status)
90{
91 switch (status[0]) {
92 case 'C': return gConstants.statusCharging; // Charging
93 case 'D': return gConstants.statusDischarging; // Discharging
94 case 'F': return gConstants.statusFull; // Not charging
95 case 'N': return gConstants.statusNotCharging; // Full
96 case 'U': return gConstants.statusUnknown; // Unknown
97
98 default: {
99 LOGW("Unknown battery status '%s'", status);
100 return gConstants.statusUnknown;
101 }
102 }
103}
104
105static jint getBatteryHealth(const char* status)
106{
107 switch (status[0]) {
Imre Sunyi92396122010-09-20 18:02:50 +0200108 case 'C': return gConstants.healthCold; // Cold
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 case 'D': return gConstants.healthDead; // Dead
110 case 'G': return gConstants.healthGood; // Good
111 case 'O': {
112 if (strcmp(status, "Overheat") == 0) {
113 return gConstants.healthOverheat;
114 } else if (strcmp(status, "Over voltage") == 0) {
115 return gConstants.healthOverVoltage;
116 }
117 LOGW("Unknown battery health[1] '%s'", status);
118 return gConstants.healthUnknown;
119 }
120
121 case 'U': {
122 if (strcmp(status, "Unspecified failure") == 0) {
123 return gConstants.healthUnspecifiedFailure;
124 } else if (strcmp(status, "Unknown") == 0) {
125 return gConstants.healthUnknown;
126 }
127 // fall through
128 }
129
130 default: {
131 LOGW("Unknown battery health[2] '%s'", status);
132 return gConstants.healthUnknown;
133 }
134 }
135}
136
137static int readFromFile(const char* path, char* buf, size_t size)
138{
Mike Lockwoodf24d13a2009-08-17 18:11:14 -0400139 if (!path)
140 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 int fd = open(path, O_RDONLY, 0);
142 if (fd == -1) {
143 LOGE("Could not open '%s'", path);
144 return -1;
145 }
146
147 size_t count = read(fd, buf, size);
148 if (count > 0) {
149 count = (count < size) ? count : size - 1;
150 while (count > 0 && buf[count-1] == '\n') count--;
151 buf[count] = '\0';
152 } else {
153 buf[0] = '\0';
154 }
155
156 close(fd);
157 return count;
158}
159
160static void setBooleanField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID)
161{
162 const int SIZE = 16;
163 char buf[SIZE];
164
165 jboolean value = false;
166 if (readFromFile(path, buf, SIZE) > 0) {
Axel Haslam49016d62010-10-12 17:01:23 -0500167 if (buf[0] != '0') {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 value = true;
169 }
170 }
171 env->SetBooleanField(obj, fieldID, value);
172}
173
174static void setIntField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID)
175{
176 const int SIZE = 128;
177 char buf[SIZE];
178
179 jint value = 0;
180 if (readFromFile(path, buf, SIZE) > 0) {
181 value = atoi(buf);
182 }
183 env->SetIntField(obj, fieldID, value);
184}
185
Mike Lockwoodf24d13a2009-08-17 18:11:14 -0400186static void setVoltageField(JNIEnv* env, jobject obj, const char* path, jfieldID fieldID)
187{
188 const int SIZE = 128;
189 char buf[SIZE];
190
191 jint value = 0;
192 if (readFromFile(path, buf, SIZE) > 0) {
193 value = atoi(buf);
194 value /= gVoltageDivisor;
195 }
196 env->SetIntField(obj, fieldID, value);
197}
198
199
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200static void android_server_BatteryService_update(JNIEnv* env, jobject obj)
201{
Mike Lockwood304928f2009-08-17 17:16:20 -0400202 setBooleanField(env, obj, gPaths.acOnlinePath, gFieldIds.mAcOnline);
203 setBooleanField(env, obj, gPaths.usbOnlinePath, gFieldIds.mUsbOnline);
204 setBooleanField(env, obj, gPaths.batteryPresentPath, gFieldIds.mBatteryPresent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205
Mike Lockwood304928f2009-08-17 17:16:20 -0400206 setIntField(env, obj, gPaths.batteryCapacityPath, gFieldIds.mBatteryLevel);
Mike Lockwoodf24d13a2009-08-17 18:11:14 -0400207 setVoltageField(env, obj, gPaths.batteryVoltagePath, gFieldIds.mBatteryVoltage);
Mike Lockwood304928f2009-08-17 17:16:20 -0400208 setIntField(env, obj, gPaths.batteryTemperaturePath, gFieldIds.mBatteryTemperature);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209
210 const int SIZE = 128;
211 char buf[SIZE];
212
Mike Lockwood304928f2009-08-17 17:16:20 -0400213 if (readFromFile(gPaths.batteryStatusPath, buf, SIZE) > 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf));
Rebecca Schultz Zavine7e6fa32009-04-28 17:24:47 -0700215 else
216 env->SetIntField(obj, gFieldIds.mBatteryStatus,
217 gConstants.statusUnknown);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218
Mike Lockwood304928f2009-08-17 17:16:20 -0400219 if (readFromFile(gPaths.batteryHealthPath, buf, SIZE) > 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 env->SetIntField(obj, gFieldIds.mBatteryHealth, getBatteryHealth(buf));
221
Mike Lockwood304928f2009-08-17 17:16:20 -0400222 if (readFromFile(gPaths.batteryTechnologyPath, buf, SIZE) > 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800223 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{
Mike Lockwood304928f2009-08-17 17:16:20 -0400233 char path[PATH_MAX];
234 struct dirent* entry;
235
236 DIR* dir = opendir(POWER_SUPPLY_PATH);
237 if (dir == NULL) {
238 LOGE("Could not open %s\n", POWER_SUPPLY_PATH);
239 return -1;
240 }
241 while ((entry = readdir(dir))) {
Mike Lockwoodf24d13a2009-08-17 18:11:14 -0400242 const char* name = entry->d_name;
243
244 // ignore "." and ".."
245 if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
246 continue;
247 }
248
Mike Lockwood304928f2009-08-17 17:16:20 -0400249 char buf[20];
250 // Look for "type" file in each subdirectory
Mike Lockwoodf24d13a2009-08-17 18:11:14 -0400251 snprintf(path, sizeof(path), "%s/%s/type", POWER_SUPPLY_PATH, name);
Mike Lockwood304928f2009-08-17 17:16:20 -0400252 int length = readFromFile(path, buf, sizeof(buf));
253 if (length > 0) {
254 if (buf[length - 1] == '\n')
255 buf[length - 1] = 0;
256
257 if (strcmp(buf, "Mains") == 0) {
Mike Lockwoodf24d13a2009-08-17 18:11:14 -0400258 snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
259 if (access(path, R_OK) == 0)
260 gPaths.acOnlinePath = strdup(path);
Mike Lockwood304928f2009-08-17 17:16:20 -0400261 }
262 else if (strcmp(buf, "USB") == 0) {
Mike Lockwoodf24d13a2009-08-17 18:11:14 -0400263 snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
264 if (access(path, R_OK) == 0)
265 gPaths.usbOnlinePath = strdup(path);
Mike Lockwood304928f2009-08-17 17:16:20 -0400266 }
267 else if (strcmp(buf, "Battery") == 0) {
Mike Lockwoodf24d13a2009-08-17 18:11:14 -0400268 snprintf(path, sizeof(path), "%s/%s/status", POWER_SUPPLY_PATH, name);
269 if (access(path, R_OK) == 0)
270 gPaths.batteryStatusPath = strdup(path);
271 snprintf(path, sizeof(path), "%s/%s/health", POWER_SUPPLY_PATH, name);
272 if (access(path, R_OK) == 0)
273 gPaths.batteryHealthPath = strdup(path);
274 snprintf(path, sizeof(path), "%s/%s/present", POWER_SUPPLY_PATH, name);
275 if (access(path, R_OK) == 0)
276 gPaths.batteryPresentPath = strdup(path);
277 snprintf(path, sizeof(path), "%s/%s/capacity", POWER_SUPPLY_PATH, name);
278 if (access(path, R_OK) == 0)
279 gPaths.batteryCapacityPath = strdup(path);
280
281 snprintf(path, sizeof(path), "%s/%s/voltage_now", POWER_SUPPLY_PATH, name);
282 if (access(path, R_OK) == 0) {
283 gPaths.batteryVoltagePath = strdup(path);
284 // voltage_now is in microvolts, not millivolts
285 gVoltageDivisor = 1000;
286 } else {
287 snprintf(path, sizeof(path), "%s/%s/batt_vol", POWER_SUPPLY_PATH, name);
288 if (access(path, R_OK) == 0)
289 gPaths.batteryVoltagePath = strdup(path);
290 }
291
292 snprintf(path, sizeof(path), "%s/%s/temp", POWER_SUPPLY_PATH, name);
293 if (access(path, R_OK) == 0) {
294 gPaths.batteryTemperaturePath = strdup(path);
295 } else {
296 snprintf(path, sizeof(path), "%s/%s/batt_temp", POWER_SUPPLY_PATH, name);
297 if (access(path, R_OK) == 0)
298 gPaths.batteryTemperaturePath = strdup(path);
299 }
300
301 snprintf(path, sizeof(path), "%s/%s/technology", POWER_SUPPLY_PATH, name);
302 if (access(path, R_OK) == 0)
303 gPaths.batteryTechnologyPath = strdup(path);
Mike Lockwood304928f2009-08-17 17:16:20 -0400304 }
305 }
306 }
307 closedir(dir);
308
Mike Lockwoodf24d13a2009-08-17 18:11:14 -0400309 if (!gPaths.acOnlinePath)
310 LOGE("acOnlinePath not found");
311 if (!gPaths.usbOnlinePath)
312 LOGE("usbOnlinePath not found");
313 if (!gPaths.batteryStatusPath)
314 LOGE("batteryStatusPath not found");
315 if (!gPaths.batteryHealthPath)
316 LOGE("batteryHealthPath not found");
317 if (!gPaths.batteryPresentPath)
318 LOGE("batteryPresentPath not found");
319 if (!gPaths.batteryCapacityPath)
320 LOGE("batteryCapacityPath not found");
321 if (!gPaths.batteryVoltagePath)
322 LOGE("batteryVoltagePath not found");
323 if (!gPaths.batteryTemperaturePath)
324 LOGE("batteryTemperaturePath not found");
325 if (!gPaths.batteryTechnologyPath)
326 LOGE("batteryTechnologyPath not found");
327
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 jclass clazz = env->FindClass("com/android/server/BatteryService");
329
330 if (clazz == NULL) {
331 LOGE("Can't find com/android/server/BatteryService");
332 return -1;
333 }
334
335 gFieldIds.mAcOnline = env->GetFieldID(clazz, "mAcOnline", "Z");
336 gFieldIds.mUsbOnline = env->GetFieldID(clazz, "mUsbOnline", "Z");
337 gFieldIds.mBatteryStatus = env->GetFieldID(clazz, "mBatteryStatus", "I");
338 gFieldIds.mBatteryHealth = env->GetFieldID(clazz, "mBatteryHealth", "I");
339 gFieldIds.mBatteryPresent = env->GetFieldID(clazz, "mBatteryPresent", "Z");
340 gFieldIds.mBatteryLevel = env->GetFieldID(clazz, "mBatteryLevel", "I");
341 gFieldIds.mBatteryTechnology = env->GetFieldID(clazz, "mBatteryTechnology", "Ljava/lang/String;");
342 gFieldIds.mBatteryVoltage = env->GetFieldID(clazz, "mBatteryVoltage", "I");
343 gFieldIds.mBatteryTemperature = env->GetFieldID(clazz, "mBatteryTemperature", "I");
344
345 LOG_FATAL_IF(gFieldIds.mAcOnline == NULL, "Unable to find BatteryService.AC_ONLINE_PATH");
346 LOG_FATAL_IF(gFieldIds.mUsbOnline == NULL, "Unable to find BatteryService.USB_ONLINE_PATH");
347 LOG_FATAL_IF(gFieldIds.mBatteryStatus == NULL, "Unable to find BatteryService.BATTERY_STATUS_PATH");
348 LOG_FATAL_IF(gFieldIds.mBatteryHealth == NULL, "Unable to find BatteryService.BATTERY_HEALTH_PATH");
349 LOG_FATAL_IF(gFieldIds.mBatteryPresent == NULL, "Unable to find BatteryService.BATTERY_PRESENT_PATH");
350 LOG_FATAL_IF(gFieldIds.mBatteryLevel == NULL, "Unable to find BatteryService.BATTERY_CAPACITY_PATH");
351 LOG_FATAL_IF(gFieldIds.mBatteryVoltage == NULL, "Unable to find BatteryService.BATTERY_VOLTAGE_PATH");
352 LOG_FATAL_IF(gFieldIds.mBatteryTemperature == NULL, "Unable to find BatteryService.BATTERY_TEMPERATURE_PATH");
353 LOG_FATAL_IF(gFieldIds.mBatteryTechnology == NULL, "Unable to find BatteryService.BATTERY_TECHNOLOGY_PATH");
354
355 clazz = env->FindClass("android/os/BatteryManager");
356
357 if (clazz == NULL) {
358 LOGE("Can't find android/os/BatteryManager");
359 return -1;
360 }
361
362 gConstants.statusUnknown = env->GetStaticIntField(clazz,
363 env->GetStaticFieldID(clazz, "BATTERY_STATUS_UNKNOWN", "I"));
364
365 gConstants.statusCharging = env->GetStaticIntField(clazz,
366 env->GetStaticFieldID(clazz, "BATTERY_STATUS_CHARGING", "I"));
367
368 gConstants.statusDischarging = env->GetStaticIntField(clazz,
369 env->GetStaticFieldID(clazz, "BATTERY_STATUS_DISCHARGING", "I"));
370
371 gConstants.statusNotCharging = env->GetStaticIntField(clazz,
372 env->GetStaticFieldID(clazz, "BATTERY_STATUS_NOT_CHARGING", "I"));
373
374 gConstants.statusFull = env->GetStaticIntField(clazz,
375 env->GetStaticFieldID(clazz, "BATTERY_STATUS_FULL", "I"));
376
377 gConstants.healthUnknown = env->GetStaticIntField(clazz,
378 env->GetStaticFieldID(clazz, "BATTERY_HEALTH_UNKNOWN", "I"));
379
380 gConstants.healthGood = env->GetStaticIntField(clazz,
381 env->GetStaticFieldID(clazz, "BATTERY_HEALTH_GOOD", "I"));
382
383 gConstants.healthOverheat = env->GetStaticIntField(clazz,
384 env->GetStaticFieldID(clazz, "BATTERY_HEALTH_OVERHEAT", "I"));
385
386 gConstants.healthDead = env->GetStaticIntField(clazz,
387 env->GetStaticFieldID(clazz, "BATTERY_HEALTH_DEAD", "I"));
388
389 gConstants.healthOverVoltage = env->GetStaticIntField(clazz,
390 env->GetStaticFieldID(clazz, "BATTERY_HEALTH_OVER_VOLTAGE", "I"));
391
392 gConstants.healthUnspecifiedFailure = env->GetStaticIntField(clazz,
393 env->GetStaticFieldID(clazz, "BATTERY_HEALTH_UNSPECIFIED_FAILURE", "I"));
394
Imre Sunyi92396122010-09-20 18:02:50 +0200395 gConstants.healthCold = env->GetStaticIntField(clazz,
396 env->GetStaticFieldID(clazz, "BATTERY_HEALTH_COLD", "I"));
397
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 return jniRegisterNativeMethods(env, "com/android/server/BatteryService", sMethods, NELEM(sMethods));
399}
400
401} /* namespace android */