• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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