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
43using android::hardware::Return;
44using android::hardware::Void;
45using android::hardware::power::V1_0::IPower;
46using android::hardware::power::V1_0::PowerStatePlatformSleepState;
47using android::hardware::power::V1_0::PowerStateVoter;
48using android::hardware::power::V1_0::Status;
49using android::hardware::hidl_vec;
50
51namespace android
52{
53
54#define LAST_RESUME_REASON "/sys/kernel/wakeup_reasons/last_resume_reason"
55#define MAX_REASON_SIZE 512
56
57static bool wakeup_init = false;
58static sem_t wakeup_sem;
59extern sp<IPower> gPowerHal;
60extern std::mutex gPowerHalMutex;
61extern bool getPowerHal();
62
63static 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
74static 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
185static 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
266static const JNINativeMethod method_table[] = {
267    { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
268    { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats },
269};
270
271int 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