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