blob: 2ceb5356e624b654b6942d518128e732869716fe [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#include <linux/ioctl.h>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036
37namespace android {
38
Mike Lockwood304928f2009-08-17 17:16:20 -040039#define POWER_SUPPLY_PATH "/sys/class/power_supply"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040
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;
Imre Sunyi92396122010-09-20 18:02:50 +020067 jint healthCold;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068};
69static BatteryManagerConstants gConstants;
70
Mike Lockwood304928f2009-08-17 17:16:20 -040071struct 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
Mike Lockwoodf24d13a2009-08-17 18:11:14 -040084static int gVoltageDivisor = 1;
85
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086static 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]) {
Imre Sunyi92396122010-09-20 18:02:50 +0200105 case 'C': return gConstants.healthCold; // Cold
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 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{
Mike Lockwoodf24d13a2009-08-17 18:11:14 -0400136 if (!path)
137 return -1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 int fd = open(path, O_RDONLY, 0);
139 if (fd == -1) {
140 LOGE("Could not open '%s'", path);
141 return -1;
142 }
143
Dima Zavin98e044a2011-10-28 18:05:47 -0700144 ssize_t count = read(fd, buf, size);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145 if (count > 0) {
Dima Zavin98e044a2011-10-28 18:05:47 -0700146 while (count > 0 && buf[count-1] == '\n')
147 count--;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 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) {
Axel Haslam49016d62010-10-12 17:01:23 -0500164 if (buf[0] != '0') {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 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
Mike Lockwoodf24d13a2009-08-17 18:11:14 -0400183static 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197static void android_server_BatteryService_update(JNIEnv* env, jobject obj)
198{
Mike Lockwood304928f2009-08-17 17:16:20 -0400199 setBooleanField(env, obj, gPaths.acOnlinePath, gFieldIds.mAcOnline);
200 setBooleanField(env, obj, gPaths.usbOnlinePath, gFieldIds.mUsbOnline);
201 setBooleanField(env, obj, gPaths.batteryPresentPath, gFieldIds.mBatteryPresent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202
Mike Lockwood304928f2009-08-17 17:16:20 -0400203 setIntField(env, obj, gPaths.batteryCapacityPath, gFieldIds.mBatteryLevel);
Mike Lockwoodf24d13a2009-08-17 18:11:14 -0400204 setVoltageField(env, obj, gPaths.batteryVoltagePath, gFieldIds.mBatteryVoltage);
Mike Lockwood304928f2009-08-17 17:16:20 -0400205 setIntField(env, obj, gPaths.batteryTemperaturePath, gFieldIds.mBatteryTemperature);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206
207 const int SIZE = 128;
208 char buf[SIZE];
209
Mike Lockwood304928f2009-08-17 17:16:20 -0400210 if (readFromFile(gPaths.batteryStatusPath, buf, SIZE) > 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 env->SetIntField(obj, gFieldIds.mBatteryStatus, getBatteryStatus(buf));
Rebecca Schultz Zavine7e6fa32009-04-28 17:24:47 -0700212 else
213 env->SetIntField(obj, gFieldIds.mBatteryStatus,
214 gConstants.statusUnknown);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215
Mike Lockwood304928f2009-08-17 17:16:20 -0400216 if (readFromFile(gPaths.batteryHealthPath, buf, SIZE) > 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217 env->SetIntField(obj, gFieldIds.mBatteryHealth, getBatteryHealth(buf));
218
Mike Lockwood304928f2009-08-17 17:16:20 -0400219 if (readFromFile(gPaths.batteryTechnologyPath, buf, SIZE) > 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 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{
Mike Lockwood304928f2009-08-17 17:16:20 -0400230 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))) {
Mike Lockwoodf24d13a2009-08-17 18:11:14 -0400239 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
Mike Lockwood304928f2009-08-17 17:16:20 -0400246 char buf[20];
247 // Look for "type" file in each subdirectory
Mike Lockwoodf24d13a2009-08-17 18:11:14 -0400248 snprintf(path, sizeof(path), "%s/%s/type", POWER_SUPPLY_PATH, name);
Mike Lockwood304928f2009-08-17 17:16:20 -0400249 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) {
Mike Lockwoodf24d13a2009-08-17 18:11:14 -0400255 snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
256 if (access(path, R_OK) == 0)
257 gPaths.acOnlinePath = strdup(path);
Mike Lockwood304928f2009-08-17 17:16:20 -0400258 }
259 else if (strcmp(buf, "USB") == 0) {
Mike Lockwoodf24d13a2009-08-17 18:11:14 -0400260 snprintf(path, sizeof(path), "%s/%s/online", POWER_SUPPLY_PATH, name);
261 if (access(path, R_OK) == 0)
262 gPaths.usbOnlinePath = strdup(path);
Mike Lockwood304928f2009-08-17 17:16:20 -0400263 }
264 else if (strcmp(buf, "Battery") == 0) {
Mike Lockwoodf24d13a2009-08-17 18:11:14 -0400265 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);
Mike Lockwood304928f2009-08-17 17:16:20 -0400301 }
302 }
303 }
304 closedir(dir);
305
Mike Lockwoodf24d13a2009-08-17 18:11:14 -0400306 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325 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
Imre Sunyi92396122010-09-20 18:02:50 +0200392 gConstants.healthCold = env->GetStaticIntField(clazz,
393 env->GetStaticFieldID(clazz, "BATTERY_HEALTH_COLD", "I"));
394
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800395 return jniRegisterNativeMethods(env, "com/android/server/BatteryService", sMethods, NELEM(sMethods));
396}
397
398} /* namespace android */