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 <android_runtime/AndroidRuntime.h>
21 #include <jni.h>
22 
23 #include <ScopedLocalRef.h>
24 #include <ScopedPrimitiveArray.h>
25 
26 #include <cutils/log.h>
27 #include <utils/misc.h>
28 #include <utils/Log.h>
29 #include <hardware/hardware.h>
30 #include <hardware/power.h>
31 #include <suspend/autosuspend.h>
32 
33 #include <inttypes.h>
34 #include <stdio.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <semaphore.h>
38 #include <stddef.h>
39 #include <string.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42 #include <unistd.h>
43 
44 namespace android
45 {
46 
47 #define LAST_RESUME_REASON "/sys/kernel/wakeup_reasons/last_resume_reason"
48 #define MAX_REASON_SIZE 512
49 
50 static bool wakeup_init = false;
51 static sem_t wakeup_sem;
52 extern struct power_module* gPowerModule;
53 
wakeup_callback(bool success)54 static void wakeup_callback(bool success)
55 {
56     ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
57     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 
nativeWaitWakeup(JNIEnv * env,jobject clazz,jobject outBuf)65 static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf)
66 {
67     if (outBuf == NULL) {
68         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);
86     }
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;
97     }
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 
105     char* mergedreason = (char*)env->GetDirectBufferAddress(outBuf);
106     int remainreasonlen = (int)env->GetDirectBufferCapacity(outBuf);
107 
108     ALOGV("Reading wakeup reasons");
109     char* mergedreasonpos = mergedreason;
110     char reasonline[128];
111     int i = 0;
112     while (fgets(reasonline, sizeof(reasonline), fp) != NULL) {
113         char* pos = reasonline;
114         char* endPos;
115         int len;
116         // First field is the index or 'Abort'.
117         int irq = (int)strtol(pos, &endPos, 10);
118         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;
133         }
134         pos = endPos;
135 
136         if (len >= 0 && len < remainreasonlen) {
137             mergedreasonpos += len;
138             remainreasonlen -= len;
139         }
140 
141         // Skip whitespace; rest of the buffer is the reason string.
142         while (*pos == ' ') {
143             pos++;
144         }
145 
146         // 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         }
155 
156         len = snprintf(mergedreasonpos, remainreasonlen, ":%s", pos);
157         if (len >= 0 && len < remainreasonlen) {
158             mergedreasonpos += len;
159             remainreasonlen -= len;
160         }
161         i++;
162     }
163 
164     ALOGV("Got %d reasons", i);
165     if (i > 0) {
166         *mergedreasonpos = 0;
167     }
168 
169     if (fclose(fp) != 0) {
170         ALOGE("Failed to close %s", LAST_RESUME_REASON);
171         return -1;
172     }
173     return mergedreasonpos - mergedreason;
174 }
175 
getPlatformLowPowerStats(JNIEnv * env,jobject,jobject outBuf)176 static 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 
237             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,
240                     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++) {
252                 added = snprintf(offset, remaining,
253                         "voter_%d name=%s time=%" PRIu64 " count=%" PRIu64 " ",
254                         j + 1, list[i].voters[j].name,
255                         list[i].voters[j].total_time_in_msec_voted_for_since_boot,
256                         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 
280 err_free:
281     for (int i = 0; i < num_modes; i++) {
282         free(list[i].voters);
283     }
284     free(list);
285     free(voter_list);
286 error:
287     return total_added;
288 }
289 
290 static const JNINativeMethod method_table[] = {
291     { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
292     { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats },
293 };
294 
register_android_server_BatteryStatsService(JNIEnv * env)295 int 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 };
302