1 /*
2 * Copyright (C) 2014 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 "BatteryStatsService"
18 //#define LOG_NDEBUG 0
19
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <inttypes.h>
23 #include <semaphore.h>
24 #include <stddef.h>
25 #include <stdio.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30
31 #include <android/hardware/power/1.0/IPower.h>
32 #include <android/hardware/power/1.1/IPower.h>
33 #include <android_runtime/AndroidRuntime.h>
34 #include <jni.h>
35
36 #include <nativehelper/ScopedLocalRef.h>
37 #include <nativehelper/ScopedPrimitiveArray.h>
38
39 #include <log/log.h>
40 #include <utils/misc.h>
41 #include <utils/Log.h>
42 #include <suspend/autosuspend.h>
43
44 using android::hardware::Return;
45 using android::hardware::Void;
46 using android::hardware::power::V1_0::IPower;
47 using android::hardware::power::V1_0::PowerStatePlatformSleepState;
48 using android::hardware::power::V1_0::PowerStateVoter;
49 using android::hardware::power::V1_0::Status;
50 using android::hardware::power::V1_1::PowerStateSubsystem;
51 using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
52 using android::hardware::hidl_vec;
53
54 namespace android
55 {
56
57 #define LAST_RESUME_REASON "/sys/kernel/wakeup_reasons/last_resume_reason"
58 #define MAX_REASON_SIZE 512
59
60 static bool wakeup_init = false;
61 static sem_t wakeup_sem;
62 extern sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0;
63 extern std::mutex gPowerHalMutex;
64 extern bool getPowerHal();
65
66 // Java methods used in getLowPowerStats
67 static jmethodID jgetAndUpdatePlatformState = NULL;
68 static jmethodID jgetSubsystem = NULL;
69 static jmethodID jputVoter = NULL;
70 static jmethodID jputState = NULL;
71
wakeup_callback(bool success)72 static void wakeup_callback(bool success)
73 {
74 ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
75 int ret = sem_post(&wakeup_sem);
76 if (ret < 0) {
77 char buf[80];
78 strerror_r(errno, buf, sizeof(buf));
79 ALOGE("Error posting wakeup sem: %s\n", buf);
80 }
81 }
82
nativeWaitWakeup(JNIEnv * env,jobject clazz,jobject outBuf)83 static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf)
84 {
85 if (outBuf == NULL) {
86 jniThrowException(env, "java/lang/NullPointerException", "null argument");
87 return -1;
88 }
89
90 // Register our wakeup callback if not yet done.
91 if (!wakeup_init) {
92 wakeup_init = true;
93 ALOGV("Creating semaphore...");
94 int ret = sem_init(&wakeup_sem, 0, 0);
95 if (ret < 0) {
96 char buf[80];
97 strerror_r(errno, buf, sizeof(buf));
98 ALOGE("Error creating semaphore: %s\n", buf);
99 jniThrowException(env, "java/lang/IllegalStateException", buf);
100 return -1;
101 }
102 ALOGV("Registering callback...");
103 set_wakeup_callback(&wakeup_callback);
104 }
105
106 // Wait for wakeup.
107 ALOGV("Waiting for wakeup...");
108 int ret = sem_wait(&wakeup_sem);
109 if (ret < 0) {
110 char buf[80];
111 strerror_r(errno, buf, sizeof(buf));
112 ALOGE("Error waiting on semaphore: %s\n", buf);
113 // Return 0 here to let it continue looping but not return results.
114 return 0;
115 }
116
117 FILE *fp = fopen(LAST_RESUME_REASON, "r");
118 if (fp == NULL) {
119 ALOGE("Failed to open %s", LAST_RESUME_REASON);
120 return -1;
121 }
122
123 char* mergedreason = (char*)env->GetDirectBufferAddress(outBuf);
124 int remainreasonlen = (int)env->GetDirectBufferCapacity(outBuf);
125
126 ALOGV("Reading wakeup reasons");
127 char* mergedreasonpos = mergedreason;
128 char reasonline[128];
129 int i = 0;
130 while (fgets(reasonline, sizeof(reasonline), fp) != NULL) {
131 char* pos = reasonline;
132 char* endPos;
133 int len;
134 // First field is the index or 'Abort'.
135 int irq = (int)strtol(pos, &endPos, 10);
136 if (pos != endPos) {
137 // Write the irq number to the merged reason string.
138 len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "%d" : ":%d", irq);
139 } else {
140 // The first field is not an irq, it may be the word Abort.
141 const size_t abortPrefixLen = strlen("Abort:");
142 if (strncmp(pos, "Abort:", abortPrefixLen) != 0) {
143 // Ooops.
144 ALOGE("Bad reason line: %s", reasonline);
145 continue;
146 }
147
148 // Write 'Abort' to the merged reason string.
149 len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "Abort" : ":Abort");
150 endPos = pos + abortPrefixLen;
151 }
152 pos = endPos;
153
154 if (len >= 0 && len < remainreasonlen) {
155 mergedreasonpos += len;
156 remainreasonlen -= len;
157 }
158
159 // Skip whitespace; rest of the buffer is the reason string.
160 while (*pos == ' ') {
161 pos++;
162 }
163
164 // Chop newline at end.
165 char* endpos = pos;
166 while (*endpos != 0) {
167 if (*endpos == '\n') {
168 *endpos = 0;
169 break;
170 }
171 endpos++;
172 }
173
174 len = snprintf(mergedreasonpos, remainreasonlen, ":%s", pos);
175 if (len >= 0 && len < remainreasonlen) {
176 mergedreasonpos += len;
177 remainreasonlen -= len;
178 }
179 i++;
180 }
181
182 ALOGV("Got %d reasons", i);
183 if (i > 0) {
184 *mergedreasonpos = 0;
185 }
186
187 if (fclose(fp) != 0) {
188 ALOGE("Failed to close %s", LAST_RESUME_REASON);
189 return -1;
190 }
191 return mergedreasonpos - mergedreason;
192 }
193
getLowPowerStats(JNIEnv * env,jobject,jobject jrpmStats)194 static void getLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrpmStats) {
195 if (jrpmStats == NULL) {
196 jniThrowException(env, "java/lang/NullPointerException",
197 "The rpmstats jni input jobject jrpmStats is null.");
198 return;
199 }
200 if (jgetAndUpdatePlatformState == NULL || jgetSubsystem == NULL
201 || jputVoter == NULL || jputState == NULL) {
202 ALOGE("A rpmstats jni jmethodID is null.");
203 return;
204 }
205
206 std::lock_guard<std::mutex> lock(gPowerHalMutex);
207 if (!getPowerHal()) {
208 ALOGE("Power Hal not loaded");
209 return;
210 }
211
212 Return<void> ret = gPowerHalV1_0->getPlatformLowPowerStats(
213 [&env, &jrpmStats](hidl_vec<PowerStatePlatformSleepState> states, Status status) {
214
215 if (status != Status::SUCCESS) return;
216
217 for (size_t i = 0; i < states.size(); i++) {
218 const PowerStatePlatformSleepState& state = states[i];
219
220 jobject jplatformState = env->CallObjectMethod(jrpmStats,
221 jgetAndUpdatePlatformState,
222 env->NewStringUTF(state.name.c_str()),
223 state.residencyInMsecSinceBoot,
224 state.totalTransitions);
225 if (jplatformState == NULL) {
226 ALOGE("The rpmstats jni jobject jplatformState is null.");
227 return;
228 }
229
230 for (size_t j = 0; j < state.voters.size(); j++) {
231 const PowerStateVoter& voter = state.voters[j];
232 env->CallVoidMethod(jplatformState, jputVoter,
233 env->NewStringUTF(voter.name.c_str()),
234 voter.totalTimeInMsecVotedForSinceBoot,
235 voter.totalNumberOfTimesVotedSinceBoot);
236 }
237 }
238 });
239 if (!ret.isOk()) {
240 ALOGE("getLowPowerStats() failed: power HAL service not available");
241 gPowerHalV1_0 = nullptr;
242 return;
243 }
244
245 //Trying to cast to IPower 1.1, this will succeed only for devices supporting 1.1
246 sp<android::hardware::power::V1_1::IPower> gPowerHal_1_1
247 = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
248 if (gPowerHal_1_1 == nullptr) {
249 //This device does not support IPower@1.1, exiting gracefully
250 return;
251 }
252 ret = gPowerHal_1_1->getSubsystemLowPowerStats(
253 [&env, &jrpmStats](hidl_vec<PowerStateSubsystem> subsystems, Status status) {
254
255 if (status != Status::SUCCESS) return;
256
257 if (subsystems.size() > 0) {
258 for (size_t i = 0; i < subsystems.size(); i++) {
259 const PowerStateSubsystem &subsystem = subsystems[i];
260
261 jobject jsubsystem = env->CallObjectMethod(jrpmStats, jgetSubsystem,
262 env->NewStringUTF(subsystem.name.c_str()));
263 if (jsubsystem == NULL) {
264 ALOGE("The rpmstats jni jobject jsubsystem is null.");
265 return;
266 }
267
268 for (size_t j = 0; j < subsystem.states.size(); j++) {
269 const PowerStateSubsystemSleepState& state = subsystem.states[j];
270 env->CallVoidMethod(jsubsystem, jputState,
271 env->NewStringUTF(state.name.c_str()),
272 state.residencyInMsecSinceBoot,
273 state.totalTransitions);
274 }
275 }
276 }
277 });
278 if (!ret.isOk()) {
279 ALOGE("getSubsystemLowPowerStats() failed: power HAL service not available");
280 gPowerHalV1_0 = nullptr;
281 }
282 // gPowerHalMutex released here
283 }
284
getPlatformLowPowerStats(JNIEnv * env,jobject,jobject outBuf)285 static jint getPlatformLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
286 char *output = (char*)env->GetDirectBufferAddress(outBuf);
287 char *offset = output;
288 int remaining = (int)env->GetDirectBufferCapacity(outBuf);
289 int total_added = -1;
290
291 if (outBuf == NULL) {
292 jniThrowException(env, "java/lang/NullPointerException", "null argument");
293 return -1;
294 }
295
296 {
297 std::lock_guard<std::mutex> lock(gPowerHalMutex);
298 if (!getPowerHal()) {
299 ALOGE("Power Hal not loaded");
300 return -1;
301 }
302
303 Return<void> ret = gPowerHalV1_0->getPlatformLowPowerStats(
304 [&offset, &remaining, &total_added](hidl_vec<PowerStatePlatformSleepState> states,
305 Status status) {
306 if (status != Status::SUCCESS)
307 return;
308 for (size_t i = 0; i < states.size(); i++) {
309 int added;
310 const PowerStatePlatformSleepState& state = states[i];
311
312 added = snprintf(offset, remaining,
313 "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ",
314 i + 1, state.name.c_str(), state.residencyInMsecSinceBoot,
315 state.totalTransitions);
316 if (added < 0) {
317 break;
318 }
319 if (added > remaining) {
320 added = remaining;
321 }
322 offset += added;
323 remaining -= added;
324 total_added += added;
325
326 for (size_t j = 0; j < state.voters.size(); j++) {
327 const PowerStateVoter& voter = state.voters[j];
328 added = snprintf(offset, remaining,
329 "voter_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ",
330 j + 1, voter.name.c_str(),
331 voter.totalTimeInMsecVotedForSinceBoot,
332 voter.totalNumberOfTimesVotedSinceBoot);
333 if (added < 0) {
334 break;
335 }
336 if (added > remaining) {
337 added = remaining;
338 }
339 offset += added;
340 remaining -= added;
341 total_added += added;
342 }
343
344 if (remaining <= 0) {
345 /* rewrite NULL character*/
346 offset--;
347 total_added--;
348 ALOGE("PowerHal: buffer not enough");
349 break;
350 }
351 }
352 }
353 );
354
355 if (!ret.isOk()) {
356 ALOGE("getPlatformLowPowerStats() failed: power HAL service not available");
357 gPowerHalV1_0 = nullptr;
358 return -1;
359 }
360 }
361 *offset = 0;
362 total_added += 1;
363 return total_added;
364 }
365
getSubsystemLowPowerStats(JNIEnv * env,jobject,jobject outBuf)366 static jint getSubsystemLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
367 char *output = (char*)env->GetDirectBufferAddress(outBuf);
368 char *offset = output;
369 int remaining = (int)env->GetDirectBufferCapacity(outBuf);
370 int total_added = -1;
371
372 //This is a IPower 1.1 API
373 sp<android::hardware::power::V1_1::IPower> gPowerHal_1_1 = nullptr;
374
375 if (outBuf == NULL) {
376 jniThrowException(env, "java/lang/NullPointerException", "null argument");
377 return -1;
378 }
379
380 {
381 std::lock_guard<std::mutex> lock(gPowerHalMutex);
382 if (!getPowerHal()) {
383 ALOGE("Power Hal not loaded");
384 return -1;
385 }
386
387 //Trying to cast to 1.1, this will succeed only for devices supporting 1.1
388 gPowerHal_1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
389 if (gPowerHal_1_1 == nullptr) {
390 //This device does not support IPower@1.1, exiting gracefully
391 return 0;
392 }
393
394 Return<void> ret = gPowerHal_1_1->getSubsystemLowPowerStats(
395 [&offset, &remaining, &total_added](hidl_vec<PowerStateSubsystem> subsystems,
396 Status status) {
397
398 if (status != Status::SUCCESS)
399 return;
400
401 if (subsystems.size() > 0) {
402 int added = snprintf(offset, remaining, "SubsystemPowerState ");
403 offset += added;
404 remaining -= added;
405 total_added += added;
406
407 for (size_t i = 0; i < subsystems.size(); i++) {
408 const PowerStateSubsystem &subsystem = subsystems[i];
409
410 added = snprintf(offset, remaining,
411 "subsystem_%zu name=%s ", i + 1, subsystem.name.c_str());
412 if (added < 0) {
413 break;
414 }
415
416 if (added > remaining) {
417 added = remaining;
418 }
419
420 offset += added;
421 remaining -= added;
422 total_added += added;
423
424 for (size_t j = 0; j < subsystem.states.size(); j++) {
425 const PowerStateSubsystemSleepState& state = subsystem.states[j];
426 added = snprintf(offset, remaining,
427 "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " last entry=%" PRIu64 " ",
428 j + 1, state.name.c_str(), state.residencyInMsecSinceBoot,
429 state.totalTransitions, state.lastEntryTimestampMs);
430 if (added < 0) {
431 break;
432 }
433
434 if (added > remaining) {
435 added = remaining;
436 }
437
438 offset += added;
439 remaining -= added;
440 total_added += added;
441 }
442
443 if (remaining <= 0) {
444 /* rewrite NULL character*/
445 offset--;
446 total_added--;
447 ALOGE("PowerHal: buffer not enough");
448 break;
449 }
450 }
451 }
452 }
453 );
454
455 if (!ret.isOk()) {
456 ALOGE("getSubsystemLowPowerStats() failed: power HAL service not available");
457 gPowerHalV1_0 = nullptr;
458 return -1;
459 }
460 }
461
462 *offset = 0;
463 total_added += 1;
464 return total_added;
465 }
466
467 static const JNINativeMethod method_table[] = {
468 { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
469 { "getLowPowerStats", "(Lcom/android/internal/os/RpmStats;)V", (void*)getLowPowerStats },
470 { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats },
471 { "getSubsystemLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getSubsystemLowPowerStats },
472 };
473
register_android_server_BatteryStatsService(JNIEnv * env)474 int register_android_server_BatteryStatsService(JNIEnv *env)
475 {
476 // get java classes and methods
477 jclass clsRpmStats = env->FindClass("com/android/internal/os/RpmStats");
478 jclass clsPowerStatePlatformSleepState =
479 env->FindClass("com/android/internal/os/RpmStats$PowerStatePlatformSleepState");
480 jclass clsPowerStateSubsystem =
481 env->FindClass("com/android/internal/os/RpmStats$PowerStateSubsystem");
482 if (clsRpmStats == NULL || clsPowerStatePlatformSleepState == NULL
483 || clsPowerStateSubsystem == NULL) {
484 ALOGE("A rpmstats jni jclass is null.");
485 } else {
486 jgetAndUpdatePlatformState = env->GetMethodID(clsRpmStats, "getAndUpdatePlatformState",
487 "(Ljava/lang/String;JI)Lcom/android/internal/os/RpmStats$PowerStatePlatformSleepState;");
488 jgetSubsystem = env->GetMethodID(clsRpmStats, "getSubsystem",
489 "(Ljava/lang/String;)Lcom/android/internal/os/RpmStats$PowerStateSubsystem;");
490 jputVoter = env->GetMethodID(clsPowerStatePlatformSleepState, "putVoter",
491 "(Ljava/lang/String;JI)V");
492 jputState = env->GetMethodID(clsPowerStateSubsystem, "putState",
493 "(Ljava/lang/String;JI)V");
494 }
495
496 return jniRegisterNativeMethods(env, "com/android/server/am/BatteryStatsService",
497 method_table, NELEM(method_table));
498 }
499
500 };
501