[go: nahoru, domu]

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
44namespace 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;
52extern struct power_module* gPowerModule;
53
54static 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
65static 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
176static 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
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
290static const JNINativeMethod method_table[] = {
291    { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
292    { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats },
293};
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};
302