blob: 413f430a7e42b4d0868bd6bdf58d72fe99094657 [file] [log] [blame]
Dianne Hackbornc51cf032014-03-02 19:08:15 -08001/*
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
Mark Salyzyn96bf5982016-09-28 16:15:30 -070020#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
Ruchi Kandoi0d434042016-10-03 09:12:02 -070031#include <android/hardware/power/1.0/IPower.h>
Ahmed ElArabawyd8b44112017-05-23 21:25:02 +000032#include <android/hardware/power/1.1/IPower.h>
Dianne Hackbornc51cf032014-03-02 19:08:15 -080033#include <android_runtime/AndroidRuntime.h>
34#include <jni.h>
35
36#include <ScopedLocalRef.h>
37#include <ScopedPrimitiveArray.h>
38
Mark Salyzyn96bf5982016-09-28 16:15:30 -070039#include <log/log.h>
Dianne Hackbornc51cf032014-03-02 19:08:15 -080040#include <utils/misc.h>
41#include <utils/Log.h>
Dianne Hackbornc51cf032014-03-02 19:08:15 -080042#include <suspend/autosuspend.h>
43
Ruchi Kandoi0d434042016-10-03 09:12:02 -070044using android::hardware::Return;
45using android::hardware::Void;
46using android::hardware::power::V1_0::IPower;
47using android::hardware::power::V1_0::PowerStatePlatformSleepState;
48using android::hardware::power::V1_0::PowerStateVoter;
49using android::hardware::power::V1_0::Status;
Ahmed ElArabawyd8b44112017-05-23 21:25:02 +000050using android::hardware::power::V1_1::PowerStateSubsystem;
51using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
Ruchi Kandoi0d434042016-10-03 09:12:02 -070052using android::hardware::hidl_vec;
53
Dianne Hackbornc51cf032014-03-02 19:08:15 -080054namespace android
55{
56
57#define LAST_RESUME_REASON "/sys/kernel/wakeup_reasons/last_resume_reason"
58#define MAX_REASON_SIZE 512
59
60static bool wakeup_init = false;
61static sem_t wakeup_sem;
Philip Cuadraa99154d2017-07-11 17:01:06 -070062extern sp<android::hardware::power::V1_0::IPower> gPowerHalV1_0;
Connor O'Brien578eb7f2017-02-27 16:41:35 -080063extern std::mutex gPowerHalMutex;
64extern bool getPowerHal();
Dianne Hackbornc51cf032014-03-02 19:08:15 -080065
Adam Lesinski87fd3222015-06-25 13:10:36 -070066static void wakeup_callback(bool success)
Dianne Hackbornc51cf032014-03-02 19:08:15 -080067{
Adam Lesinski87fd3222015-06-25 13:10:36 -070068 ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
Dianne Hackbornc51cf032014-03-02 19:08:15 -080069 int ret = sem_post(&wakeup_sem);
70 if (ret < 0) {
71 char buf[80];
72 strerror_r(errno, buf, sizeof(buf));
73 ALOGE("Error posting wakeup sem: %s\n", buf);
74 }
75}
76
Adam Lesinski515702c2015-07-23 18:13:38 -070077static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf)
Dianne Hackbornc51cf032014-03-02 19:08:15 -080078{
Adam Lesinski515702c2015-07-23 18:13:38 -070079 if (outBuf == NULL) {
Dianne Hackbornc51cf032014-03-02 19:08:15 -080080 jniThrowException(env, "java/lang/NullPointerException", "null argument");
81 return -1;
82 }
83
84 // Register our wakeup callback if not yet done.
85 if (!wakeup_init) {
86 wakeup_init = true;
87 ALOGV("Creating semaphore...");
88 int ret = sem_init(&wakeup_sem, 0, 0);
89 if (ret < 0) {
90 char buf[80];
91 strerror_r(errno, buf, sizeof(buf));
92 ALOGE("Error creating semaphore: %s\n", buf);
93 jniThrowException(env, "java/lang/IllegalStateException", buf);
94 return -1;
95 }
96 ALOGV("Registering callback...");
97 set_wakeup_callback(&wakeup_callback);
Adam Lesinski6b0331a2015-06-05 17:08:22 -070098 }
99
100 // Wait for wakeup.
101 ALOGV("Waiting for wakeup...");
102 int ret = sem_wait(&wakeup_sem);
103 if (ret < 0) {
104 char buf[80];
105 strerror_r(errno, buf, sizeof(buf));
106 ALOGE("Error waiting on semaphore: %s\n", buf);
107 // Return 0 here to let it continue looping but not return results.
108 return 0;
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800109 }
110
111 FILE *fp = fopen(LAST_RESUME_REASON, "r");
112 if (fp == NULL) {
113 ALOGE("Failed to open %s", LAST_RESUME_REASON);
114 return -1;
115 }
116
Adam Lesinski515702c2015-07-23 18:13:38 -0700117 char* mergedreason = (char*)env->GetDirectBufferAddress(outBuf);
118 int remainreasonlen = (int)env->GetDirectBufferCapacity(outBuf);
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800119
Adam Lesinski515702c2015-07-23 18:13:38 -0700120 ALOGV("Reading wakeup reasons");
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800121 char* mergedreasonpos = mergedreason;
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800122 char reasonline[128];
123 int i = 0;
Adam Lesinski87fd3222015-06-25 13:10:36 -0700124 while (fgets(reasonline, sizeof(reasonline), fp) != NULL) {
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800125 char* pos = reasonline;
126 char* endPos;
Adam Lesinski87fd3222015-06-25 13:10:36 -0700127 int len;
128 // First field is the index or 'Abort'.
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800129 int irq = (int)strtol(pos, &endPos, 10);
Adam Lesinski87fd3222015-06-25 13:10:36 -0700130 if (pos != endPos) {
131 // Write the irq number to the merged reason string.
132 len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "%d" : ":%d", irq);
133 } else {
134 // The first field is not an irq, it may be the word Abort.
135 const size_t abortPrefixLen = strlen("Abort:");
136 if (strncmp(pos, "Abort:", abortPrefixLen) != 0) {
137 // Ooops.
138 ALOGE("Bad reason line: %s", reasonline);
139 continue;
140 }
141
142 // Write 'Abort' to the merged reason string.
143 len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "Abort" : ":Abort");
144 endPos = pos + abortPrefixLen;
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800145 }
146 pos = endPos;
Adam Lesinski87fd3222015-06-25 13:10:36 -0700147
148 if (len >= 0 && len < remainreasonlen) {
149 mergedreasonpos += len;
150 remainreasonlen -= len;
151 }
152
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800153 // Skip whitespace; rest of the buffer is the reason string.
154 while (*pos == ' ') {
155 pos++;
156 }
Adam Lesinski87fd3222015-06-25 13:10:36 -0700157
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800158 // Chop newline at end.
159 char* endpos = pos;
160 while (*endpos != 0) {
161 if (*endpos == '\n') {
162 *endpos = 0;
163 break;
164 }
165 endpos++;
166 }
Adam Lesinski87fd3222015-06-25 13:10:36 -0700167
168 len = snprintf(mergedreasonpos, remainreasonlen, ":%s", pos);
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800169 if (len >= 0 && len < remainreasonlen) {
170 mergedreasonpos += len;
171 remainreasonlen -= len;
172 }
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800173 i++;
174 }
175
176 ALOGV("Got %d reasons", i);
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800177 if (i > 0) {
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800178 *mergedreasonpos = 0;
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800179 }
180
181 if (fclose(fp) != 0) {
182 ALOGE("Failed to close %s", LAST_RESUME_REASON);
183 return -1;
184 }
Adam Lesinski515702c2015-07-23 18:13:38 -0700185 return mergedreasonpos - mergedreason;
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800186}
187
Badhri Jagan Sridharan68cdf192016-04-03 21:57:15 -0700188static jint getPlatformLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
Ruchi Kandoi0d434042016-10-03 09:12:02 -0700189 char *output = (char*)env->GetDirectBufferAddress(outBuf);
190 char *offset = output;
Badhri Jagan Sridharan68cdf192016-04-03 21:57:15 -0700191 int remaining = (int)env->GetDirectBufferCapacity(outBuf);
Badhri Jagan Sridharan68cdf192016-04-03 21:57:15 -0700192 int total_added = -1;
193
194 if (outBuf == NULL) {
195 jniThrowException(env, "java/lang/NullPointerException", "null argument");
Ruchi Kandoi0d434042016-10-03 09:12:02 -0700196 return -1;
Badhri Jagan Sridharan68cdf192016-04-03 21:57:15 -0700197 }
198
Connor O'Brien578eb7f2017-02-27 16:41:35 -0800199 {
200 std::lock_guard<std::mutex> lock(gPowerHalMutex);
201 if (!getPowerHal()) {
202 ALOGE("Power Hal not loaded");
203 return -1;
204 }
Badhri Jagan Sridharan68cdf192016-04-03 21:57:15 -0700205
Philip Cuadraa99154d2017-07-11 17:01:06 -0700206 Return<void> ret = gPowerHalV1_0->getPlatformLowPowerStats(
Connor O'Brien578eb7f2017-02-27 16:41:35 -0800207 [&offset, &remaining, &total_added](hidl_vec<PowerStatePlatformSleepState> states,
208 Status status) {
209 if (status != Status::SUCCESS)
210 return;
211 for (size_t i = 0; i < states.size(); i++) {
212 int added;
213 const PowerStatePlatformSleepState& state = states[i];
Badhri Jagan Sridharan68cdf192016-04-03 21:57:15 -0700214
Ruchi Kandoi0d434042016-10-03 09:12:02 -0700215 added = snprintf(offset, remaining,
Connor O'Brien578eb7f2017-02-27 16:41:35 -0800216 "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ",
217 i + 1, state.name.c_str(), state.residencyInMsecSinceBoot,
218 state.totalTransitions);
Ruchi Kandoi0d434042016-10-03 09:12:02 -0700219 if (added < 0) {
220 break;
221 }
222 if (added > remaining) {
223 added = remaining;
224 }
225 offset += added;
226 remaining -= added;
227 total_added += added;
Ruchi Kandoi0d434042016-10-03 09:12:02 -0700228
Connor O'Brien578eb7f2017-02-27 16:41:35 -0800229 for (size_t j = 0; j < state.voters.size(); j++) {
230 const PowerStateVoter& voter = state.voters[j];
231 added = snprintf(offset, remaining,
232 "voter_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ",
233 j + 1, voter.name.c_str(),
234 voter.totalTimeInMsecVotedForSinceBoot,
235 voter.totalNumberOfTimesVotedSinceBoot);
236 if (added < 0) {
237 break;
238 }
239 if (added > remaining) {
240 added = remaining;
241 }
242 offset += added;
243 remaining -= added;
244 total_added += added;
245 }
246
247 if (remaining <= 0) {
248 /* rewrite NULL character*/
249 offset--;
250 total_added--;
251 ALOGE("PowerHal: buffer not enough");
252 break;
253 }
Ruchi Kandoi0d434042016-10-03 09:12:02 -0700254 }
Badhri Jagan Sridharan68cdf192016-04-03 21:57:15 -0700255 }
Connor O'Brien578eb7f2017-02-27 16:41:35 -0800256 );
257
258 if (!ret.isOk()) {
259 ALOGE("getPlatformLowPowerStats() failed: power HAL service not available");
Philip Cuadraa99154d2017-07-11 17:01:06 -0700260 gPowerHalV1_0 = nullptr;
Connor O'Brien578eb7f2017-02-27 16:41:35 -0800261 return -1;
Badhri Jagan Sridharan68cdf192016-04-03 21:57:15 -0700262 }
Connor O'Brien578eb7f2017-02-27 16:41:35 -0800263 }
Badhri Jagan Sridharan68cdf192016-04-03 21:57:15 -0700264 *offset = 0;
265 total_added += 1;
Badhri Jagan Sridharan68cdf192016-04-03 21:57:15 -0700266 return total_added;
267}
268
Ahmed ElArabawyd8b44112017-05-23 21:25:02 +0000269static jint getSubsystemLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
270 char *output = (char*)env->GetDirectBufferAddress(outBuf);
271 char *offset = output;
272 int remaining = (int)env->GetDirectBufferCapacity(outBuf);
273 int total_added = -1;
274
275 //This is a IPower 1.1 API
276 sp<android::hardware::power::V1_1::IPower> gPowerHal_1_1 = nullptr;
277
278 if (outBuf == NULL) {
279 jniThrowException(env, "java/lang/NullPointerException", "null argument");
280 return -1;
281 }
282
283 {
284 std::lock_guard<std::mutex> lock(gPowerHalMutex);
285 if (!getPowerHal()) {
286 ALOGE("Power Hal not loaded");
287 return -1;
288 }
289
290 //Trying to cast to 1.1, this will succeed only for devices supporting 1.1
Philip Cuadraa99154d2017-07-11 17:01:06 -0700291 gPowerHal_1_1 = android::hardware::power::V1_1::IPower::castFrom(gPowerHalV1_0);
Ahmed ElArabawyd8b44112017-05-23 21:25:02 +0000292 if (gPowerHal_1_1 == nullptr) {
293 //This device does not support IPower@1.1, exiting gracefully
294 return 0;
295 }
296
297 Return<void> ret = gPowerHal_1_1->getSubsystemLowPowerStats(
298 [&offset, &remaining, &total_added](hidl_vec<PowerStateSubsystem> subsystems,
299 Status status) {
300
301 if (status != Status::SUCCESS)
302 return;
303
Ahmed ElArabawy307edcd2017-07-07 17:48:13 -0700304 if (subsystems.size() > 0) {
305 int added = snprintf(offset, remaining, "SubsystemPowerState ");
Ahmed ElArabawyd8b44112017-05-23 21:25:02 +0000306 offset += added;
307 remaining -= added;
308 total_added += added;
309
Ahmed ElArabawy307edcd2017-07-07 17:48:13 -0700310 for (size_t i = 0; i < subsystems.size(); i++) {
311 const PowerStateSubsystem &subsystem = subsystems[i];
312
Ahmed ElArabawyd8b44112017-05-23 21:25:02 +0000313 added = snprintf(offset, remaining,
Ahmed ElArabawy307edcd2017-07-07 17:48:13 -0700314 "subsystem_%zu name=%s ", i + 1, subsystem.name.c_str());
Ahmed ElArabawyd8b44112017-05-23 21:25:02 +0000315 if (added < 0) {
316 break;
317 }
318
319 if (added > remaining) {
320 added = remaining;
321 }
322
323 offset += added;
324 remaining -= added;
325 total_added += added;
Ahmed ElArabawyd8b44112017-05-23 21:25:02 +0000326
Ahmed ElArabawy307edcd2017-07-07 17:48:13 -0700327 for (size_t j = 0; j < subsystem.states.size(); j++) {
328 const PowerStateSubsystemSleepState& state = subsystem.states[j];
329 added = snprintf(offset, remaining,
330 "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " last entry=%" PRIu64 " ",
331 j + 1, state.name.c_str(), state.residencyInMsecSinceBoot,
332 state.totalTransitions, state.lastEntryTimestampMs);
333 if (added < 0) {
334 break;
335 }
336
337 if (added > remaining) {
338 added = remaining;
339 }
340
341 offset += added;
342 remaining -= added;
343 total_added += added;
344 }
345
346 if (remaining <= 0) {
347 /* rewrite NULL character*/
348 offset--;
349 total_added--;
350 ALOGE("PowerHal: buffer not enough");
351 break;
352 }
Ahmed ElArabawyd8b44112017-05-23 21:25:02 +0000353 }
354 }
355 }
356 );
357
358 if (!ret.isOk()) {
359 ALOGE("getSubsystemLowPowerStats() failed: power HAL service not available");
Philip Cuadraa99154d2017-07-11 17:01:06 -0700360 gPowerHalV1_0 = nullptr;
Ahmed ElArabawyd8b44112017-05-23 21:25:02 +0000361 return -1;
362 }
363 }
364
365 *offset = 0;
366 total_added += 1;
367 return total_added;
368}
369
Daniel Micay76f6a862015-09-19 17:31:01 -0400370static const JNINativeMethod method_table[] = {
Adam Lesinski515702c2015-07-23 18:13:38 -0700371 { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
Badhri Jagan Sridharan68cdf192016-04-03 21:57:15 -0700372 { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats },
Ahmed ElArabawyd8b44112017-05-23 21:25:02 +0000373 { "getSubsystemLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getSubsystemLowPowerStats },
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800374};
375
376int register_android_server_BatteryStatsService(JNIEnv *env)
377{
378 return jniRegisterNativeMethods(env, "com/android/server/am/BatteryStatsService",
379 method_table, NELEM(method_table));
380}
381
382};