blob: 531f9462a98f08b5b5c429e656039c8877b53fd9 [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
Dianne Hackbornc51cf032014-03-02 19:08:15 -080031#include <android_runtime/AndroidRuntime.h>
32#include <jni.h>
33
34#include <ScopedLocalRef.h>
35#include <ScopedPrimitiveArray.h>
36
Mark Salyzyn96bf5982016-09-28 16:15:30 -070037#include <log/log.h>
Dianne Hackbornc51cf032014-03-02 19:08:15 -080038#include <utils/misc.h>
39#include <utils/Log.h>
40#include <hardware/hardware.h>
Badhri Jagan Sridharan68cdf192016-04-03 21:57:15 -070041#include <hardware/power.h>
Dianne Hackbornc51cf032014-03-02 19:08:15 -080042#include <suspend/autosuspend.h>
43
Dianne Hackbornc51cf032014-03-02 19:08:15 -080044namespace android
45{
46
47#define LAST_RESUME_REASON "/sys/kernel/wakeup_reasons/last_resume_reason"
48#define MAX_REASON_SIZE 512
49
50static bool wakeup_init = false;
51static sem_t wakeup_sem;
Badhri Jagan Sridharan68cdf192016-04-03 21:57:15 -070052extern struct power_module* gPowerModule;
Dianne Hackbornc51cf032014-03-02 19:08:15 -080053
Adam Lesinski87fd3222015-06-25 13:10:36 -070054static void wakeup_callback(bool success)
Dianne Hackbornc51cf032014-03-02 19:08:15 -080055{
Adam Lesinski87fd3222015-06-25 13:10:36 -070056 ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
Dianne Hackbornc51cf032014-03-02 19:08:15 -080057 int ret = sem_post(&wakeup_sem);
58 if (ret < 0) {
59 char buf[80];
60 strerror_r(errno, buf, sizeof(buf));
61 ALOGE("Error posting wakeup sem: %s\n", buf);
62 }
63}
64
Adam Lesinski515702c2015-07-23 18:13:38 -070065static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf)
Dianne Hackbornc51cf032014-03-02 19:08:15 -080066{
Adam Lesinski515702c2015-07-23 18:13:38 -070067 if (outBuf == NULL) {
Dianne Hackbornc51cf032014-03-02 19:08:15 -080068 jniThrowException(env, "java/lang/NullPointerException", "null argument");
69 return -1;
70 }
71
72 // Register our wakeup callback if not yet done.
73 if (!wakeup_init) {
74 wakeup_init = true;
75 ALOGV("Creating semaphore...");
76 int ret = sem_init(&wakeup_sem, 0, 0);
77 if (ret < 0) {
78 char buf[80];
79 strerror_r(errno, buf, sizeof(buf));
80 ALOGE("Error creating semaphore: %s\n", buf);
81 jniThrowException(env, "java/lang/IllegalStateException", buf);
82 return -1;
83 }
84 ALOGV("Registering callback...");
85 set_wakeup_callback(&wakeup_callback);
Adam Lesinski6b0331a2015-06-05 17:08:22 -070086 }
87
88 // Wait for wakeup.
89 ALOGV("Waiting for wakeup...");
90 int ret = sem_wait(&wakeup_sem);
91 if (ret < 0) {
92 char buf[80];
93 strerror_r(errno, buf, sizeof(buf));
94 ALOGE("Error waiting on semaphore: %s\n", buf);
95 // Return 0 here to let it continue looping but not return results.
96 return 0;
Dianne Hackbornc51cf032014-03-02 19:08:15 -080097 }
98
99 FILE *fp = fopen(LAST_RESUME_REASON, "r");
100 if (fp == NULL) {
101 ALOGE("Failed to open %s", LAST_RESUME_REASON);
102 return -1;
103 }
104
Adam Lesinski515702c2015-07-23 18:13:38 -0700105 char* mergedreason = (char*)env->GetDirectBufferAddress(outBuf);
106 int remainreasonlen = (int)env->GetDirectBufferCapacity(outBuf);
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800107
Adam Lesinski515702c2015-07-23 18:13:38 -0700108 ALOGV("Reading wakeup reasons");
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800109 char* mergedreasonpos = mergedreason;
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800110 char reasonline[128];
111 int i = 0;
Adam Lesinski87fd3222015-06-25 13:10:36 -0700112 while (fgets(reasonline, sizeof(reasonline), fp) != NULL) {
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800113 char* pos = reasonline;
114 char* endPos;
Adam Lesinski87fd3222015-06-25 13:10:36 -0700115 int len;
116 // First field is the index or 'Abort'.
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800117 int irq = (int)strtol(pos, &endPos, 10);
Adam Lesinski87fd3222015-06-25 13:10:36 -0700118 if (pos != endPos) {
119 // Write the irq number to the merged reason string.
120 len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "%d" : ":%d", irq);
121 } else {
122 // The first field is not an irq, it may be the word Abort.
123 const size_t abortPrefixLen = strlen("Abort:");
124 if (strncmp(pos, "Abort:", abortPrefixLen) != 0) {
125 // Ooops.
126 ALOGE("Bad reason line: %s", reasonline);
127 continue;
128 }
129
130 // Write 'Abort' to the merged reason string.
131 len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "Abort" : ":Abort");
132 endPos = pos + abortPrefixLen;
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800133 }
134 pos = endPos;
Adam Lesinski87fd3222015-06-25 13:10:36 -0700135
136 if (len >= 0 && len < remainreasonlen) {
137 mergedreasonpos += len;
138 remainreasonlen -= len;
139 }
140
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800141 // Skip whitespace; rest of the buffer is the reason string.
142 while (*pos == ' ') {
143 pos++;
144 }
Adam Lesinski87fd3222015-06-25 13:10:36 -0700145
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800146 // Chop newline at end.
147 char* endpos = pos;
148 while (*endpos != 0) {
149 if (*endpos == '\n') {
150 *endpos = 0;
151 break;
152 }
153 endpos++;
154 }
Adam Lesinski87fd3222015-06-25 13:10:36 -0700155
156 len = snprintf(mergedreasonpos, remainreasonlen, ":%s", pos);
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800157 if (len >= 0 && len < remainreasonlen) {
158 mergedreasonpos += len;
159 remainreasonlen -= len;
160 }
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800161 i++;
162 }
163
164 ALOGV("Got %d reasons", i);
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800165 if (i > 0) {
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800166 *mergedreasonpos = 0;
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800167 }
168
169 if (fclose(fp) != 0) {
170 ALOGE("Failed to close %s", LAST_RESUME_REASON);
171 return -1;
172 }
Adam Lesinski515702c2015-07-23 18:13:38 -0700173 return mergedreasonpos - mergedreason;
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800174}
175
Badhri Jagan Sridharan68cdf192016-04-03 21:57:15 -0700176static jint getPlatformLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
177 int num_modes = -1;
178 char *output = (char*)env->GetDirectBufferAddress(outBuf), *offset = output;
179 int remaining = (int)env->GetDirectBufferCapacity(outBuf);
180 power_state_platform_sleep_state_t *list;
181 size_t *voter_list;
182 int total_added = -1;
183
184 if (outBuf == NULL) {
185 jniThrowException(env, "java/lang/NullPointerException", "null argument");
186 goto error;
187 }
188
189 if (!gPowerModule) {
190 ALOGE("%s: gPowerModule not loaded", POWER_HARDWARE_MODULE_ID);
191 goto error;
192 }
193
194 if (! (gPowerModule->get_platform_low_power_stats && gPowerModule->get_number_of_platform_modes
195 && gPowerModule->get_voter_list)) {
196 ALOGE("%s: Missing API", POWER_HARDWARE_MODULE_ID);
197 goto error;
198 }
199
200 if (gPowerModule->get_number_of_platform_modes) {
201 num_modes = gPowerModule->get_number_of_platform_modes(gPowerModule);
202 }
203
204 if (num_modes < 1) {
205 ALOGE("%s: Platform does not even have one low power mode", POWER_HARDWARE_MODULE_ID);
206 goto error;
207 }
208
209 list = (power_state_platform_sleep_state_t *)calloc(num_modes,
210 sizeof(power_state_platform_sleep_state_t));
211 if (!list) {
212 ALOGE("%s: power_state_platform_sleep_state_t allocation failed", POWER_HARDWARE_MODULE_ID);
213 goto error;
214 }
215
216 voter_list = (size_t *)calloc(num_modes, sizeof(*voter_list));
217 if (!voter_list) {
218 ALOGE("%s: voter_list allocation failed", POWER_HARDWARE_MODULE_ID);
219 goto err_free;
220 }
221
222 gPowerModule->get_voter_list(gPowerModule, voter_list);
223
224 for (int i = 0; i < num_modes; i++) {
225 list[i].voters = (power_state_voter_t *)calloc(voter_list[i],
226 sizeof(power_state_voter_t));
227 if (!list[i].voters) {
228 ALOGE("%s: voter_t allocation failed", POWER_HARDWARE_MODULE_ID);
229 goto err_free;
230 }
231 }
232
233 if (!gPowerModule->get_platform_low_power_stats(gPowerModule, list)) {
234 for (int i = 0; i < num_modes; i++) {
235 int added;
236
Badhri Jagan Sridharan15c4e312016-05-02 21:42:49 -0700237 added = snprintf(offset, remaining,
238 "state_%d name=%s time=%" PRIu64 " count=%" PRIu64 " ",
239 i + 1, list[i].name, list[i].residency_in_msec_since_boot,
Badhri Jagan Sridharan68cdf192016-04-03 21:57:15 -0700240 list[i].total_transitions);
241 if (added < 0) {
242 break;
243 }
244 if (added > remaining) {
245 added = remaining;
246 }
247 offset += added;
248 remaining -= added;
249 total_added += added;
250
251 for (unsigned int j = 0; j < list[i].number_of_voters; j++) {
Badhri Jagan Sridharan15c4e312016-05-02 21:42:49 -0700252 added = snprintf(offset, remaining,
253 "voter_%d name=%s time=%" PRIu64 " count=%" PRIu64 " ",
254 j + 1, list[i].voters[j].name,
Badhri Jagan Sridharan68cdf192016-04-03 21:57:15 -0700255 list[i].voters[j].total_time_in_msec_voted_for_since_boot,
Badhri Jagan Sridharan68cdf192016-04-03 21:57:15 -0700256 list[i].voters[j].total_number_of_times_voted_since_boot);
257 if (added < 0) {
258 break;
259 }
260 if (added > remaining) {
261 added = remaining;
262 }
263 offset += added;
264 remaining -= added;
265 total_added += added;
266 }
267
268 if (remaining <= 0) {
269 /* rewrite NULL character*/
270 offset--;
271 total_added--;
272 ALOGE("%s module: buffer not enough", POWER_HARDWARE_MODULE_ID);
273 break;
274 }
275 }
276 }
277 *offset = 0;
278 total_added += 1;
279
280err_free:
281 for (int i = 0; i < num_modes; i++) {
282 free(list[i].voters);
283 }
284 free(list);
285 free(voter_list);
286error:
287 return total_added;
288}
289
Daniel Micay76f6a862015-09-19 17:31:01 -0400290static const JNINativeMethod method_table[] = {
Adam Lesinski515702c2015-07-23 18:13:38 -0700291 { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
Badhri Jagan Sridharan68cdf192016-04-03 21:57:15 -0700292 { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats },
Dianne Hackbornc51cf032014-03-02 19:08:15 -0800293};
294
295int register_android_server_BatteryStatsService(JNIEnv *env)
296{
297 return jniRegisterNativeMethods(env, "com/android/server/am/BatteryStatsService",
298 method_table, NELEM(method_table));
299}
300
301};