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_runtime/AndroidRuntime.h>
33 #include <jni.h>
34
35 #include <ScopedLocalRef.h>
36 #include <ScopedPrimitiveArray.h>
37
38 #include <log/log.h>
39 #include <utils/misc.h>
40 #include <utils/Log.h>
41 #include <suspend/autosuspend.h>
42
43 using android::hardware::Return;
44 using android::hardware::Void;
45 using android::hardware::power::V1_0::IPower;
46 using android::hardware::power::V1_0::PowerStatePlatformSleepState;
47 using android::hardware::power::V1_0::PowerStateVoter;
48 using android::hardware::power::V1_0::Status;
49 using android::hardware::hidl_vec;
50
51 namespace android
52 {
53
54 #define LAST_RESUME_REASON "/sys/kernel/wakeup_reasons/last_resume_reason"
55 #define MAX_REASON_SIZE 512
56
57 static bool wakeup_init = false;
58 static sem_t wakeup_sem;
59 extern sp<IPower> gPowerHal;
60 extern std::mutex gPowerHalMutex;
61 extern bool getPowerHal();
62
wakeup_callback(bool success)63 static void wakeup_callback(bool success)
64 {
65 ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
66 int ret = sem_post(&wakeup_sem);
67 if (ret < 0) {
68 char buf[80];
69 strerror_r(errno, buf, sizeof(buf));
70 ALOGE("Error posting wakeup sem: %s\n", buf);
71 }
72 }
73
nativeWaitWakeup(JNIEnv * env,jobject clazz,jobject outBuf)74 static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf)
75 {
76 if (outBuf == NULL) {
77 jniThrowException(env, "java/lang/NullPointerException", "null argument");
78 return -1;
79 }
80
81 // Register our wakeup callback if not yet done.
82 if (!wakeup_init) {
83 wakeup_init = true;
84 ALOGV("Creating semaphore...");
85 int ret = sem_init(&wakeup_sem, 0, 0);
86 if (ret < 0) {
87 char buf[80];
88 strerror_r(errno, buf, sizeof(buf));
89 ALOGE("Error creating semaphore: %s\n", buf);
90 jniThrowException(env, "java/lang/IllegalStateException", buf);
91 return -1;
92 }
93 ALOGV("Registering callback...");
94 set_wakeup_callback(&wakeup_callback);
95 }
96
97 // Wait for wakeup.
98 ALOGV("Waiting for wakeup...");
99 int ret = sem_wait(&wakeup_sem);
100 if (ret < 0) {
101 char buf[80];
102 strerror_r(errno, buf, sizeof(buf));
103 ALOGE("Error waiting on semaphore: %s\n", buf);
104 // Return 0 here to let it continue looping but not return results.
105 return 0;
106 }
107
108 FILE *fp = fopen(LAST_RESUME_REASON, "r");
109 if (fp == NULL) {
110 ALOGE("Failed to open %s", LAST_RESUME_REASON);
111 return -1;
112 }
113
114 char* mergedreason = (char*)env->GetDirectBufferAddress(outBuf);
115 int remainreasonlen = (int)env->GetDirectBufferCapacity(outBuf);
116
117 ALOGV("Reading wakeup reasons");
118 char* mergedreasonpos = mergedreason;
119 char reasonline[128];
120 int i = 0;
121 while (fgets(reasonline, sizeof(reasonline), fp) != NULL) {
122 char* pos = reasonline;
123 char* endPos;
124 int len;
125 // First field is the index or 'Abort'.
126 int irq = (int)strtol(pos, &endPos, 10);
127 if (pos != endPos) {
128 // Write the irq number to the merged reason string.
129 len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "%d" : ":%d", irq);
130 } else {
131 // The first field is not an irq, it may be the word Abort.
132 const size_t abortPrefixLen = strlen("Abort:");
133 if (strncmp(pos, "Abort:", abortPrefixLen) != 0) {
134 // Ooops.
135 ALOGE("Bad reason line: %s", reasonline);
136 continue;
137 }
138
139 // Write 'Abort' to the merged reason string.
140 len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "Abort" : ":Abort");
141 endPos = pos + abortPrefixLen;
142 }
143 pos = endPos;
144
145 if (len >= 0 && len < remainreasonlen) {
146 mergedreasonpos += len;
147 remainreasonlen -= len;
148 }
149
150 // Skip whitespace; rest of the buffer is the reason string.
151 while (*pos == ' ') {
152 pos++;
153 }
154
155 // Chop newline at end.
156 char* endpos = pos;
157 while (*endpos != 0) {
158 if (*endpos == '\n') {
159 *endpos = 0;
160 break;
161 }
162 endpos++;
163 }
164
165 len = snprintf(mergedreasonpos, remainreasonlen, ":%s", pos);
166 if (len >= 0 && len < remainreasonlen) {
167 mergedreasonpos += len;
168 remainreasonlen -= len;
169 }
170 i++;
171 }
172
173 ALOGV("Got %d reasons", i);
174 if (i > 0) {
175 *mergedreasonpos = 0;
176 }
177
178 if (fclose(fp) != 0) {
179 ALOGE("Failed to close %s", LAST_RESUME_REASON);
180 return -1;
181 }
182 return mergedreasonpos - mergedreason;
183 }
184
getPlatformLowPowerStats(JNIEnv * env,jobject,jobject outBuf)185 static jint getPlatformLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
186 char *output = (char*)env->GetDirectBufferAddress(outBuf);
187 char *offset = output;
188 int remaining = (int)env->GetDirectBufferCapacity(outBuf);
189 int total_added = -1;
190
191 if (outBuf == NULL) {
192 jniThrowException(env, "java/lang/NullPointerException", "null argument");
193 return -1;
194 }
195
196 {
197 std::lock_guard<std::mutex> lock(gPowerHalMutex);
198 if (!getPowerHal()) {
199 ALOGE("Power Hal not loaded");
200 return -1;
201 }
202
203 Return<void> ret = gPowerHal->getPlatformLowPowerStats(
204 [&offset, &remaining, &total_added](hidl_vec<PowerStatePlatformSleepState> states,
205 Status status) {
206 if (status != Status::SUCCESS)
207 return;
208 for (size_t i = 0; i < states.size(); i++) {
209 int added;
210 const PowerStatePlatformSleepState& state = states[i];
211
212 added = snprintf(offset, remaining,
213 "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ",
214 i + 1, state.name.c_str(), state.residencyInMsecSinceBoot,
215 state.totalTransitions);
216 if (added < 0) {
217 break;
218 }
219 if (added > remaining) {
220 added = remaining;
221 }
222 offset += added;
223 remaining -= added;
224 total_added += added;
225
226 for (size_t j = 0; j < state.voters.size(); j++) {
227 const PowerStateVoter& voter = state.voters[j];
228 added = snprintf(offset, remaining,
229 "voter_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ",
230 j + 1, voter.name.c_str(),
231 voter.totalTimeInMsecVotedForSinceBoot,
232 voter.totalNumberOfTimesVotedSinceBoot);
233 if (added < 0) {
234 break;
235 }
236 if (added > remaining) {
237 added = remaining;
238 }
239 offset += added;
240 remaining -= added;
241 total_added += added;
242 }
243
244 if (remaining <= 0) {
245 /* rewrite NULL character*/
246 offset--;
247 total_added--;
248 ALOGE("PowerHal: buffer not enough");
249 break;
250 }
251 }
252 }
253 );
254
255 if (!ret.isOk()) {
256 ALOGE("getPlatformLowPowerStats() failed: power HAL service not available");
257 gPowerHal = nullptr;
258 return -1;
259 }
260 }
261 *offset = 0;
262 total_added += 1;
263 return total_added;
264 }
265
266 static const JNINativeMethod method_table[] = {
267 { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
268 { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats },
269 };
270
register_android_server_BatteryStatsService(JNIEnv * env)271 int register_android_server_BatteryStatsService(JNIEnv *env)
272 {
273 return jniRegisterNativeMethods(env, "com/android/server/am/BatteryStatsService",
274 method_table, NELEM(method_table));
275 }
276
277 };
278