• 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/hardware/power/1.1/IPower.h>
33 #include <android_runtime/AndroidRuntime.h>
34 #include <jni.h>
35 
36 #include <nativehelper/ScopedLocalRef.h>
37 #include <nativehelper/ScopedPrimitiveArray.h>
38 
39 #include <log/log.h>
40 #include <utils/misc.h>
41 #include <utils/Log.h>
42 #include <suspend/autosuspend.h>
43 
44 using android::hardware::Return;
45 using android::hardware::Void;
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::power::V1_1::PowerStateSubsystem;
50 using android::hardware::power::V1_1::PowerStateSubsystemSleepState;
51 using android::hardware::hidl_vec;
52 using IPowerV1_1 = android::hardware::power::V1_1::IPower;
53 using IPowerV1_0 = android::hardware::power::V1_0::IPower;
54 
55 namespace android
56 {
57 
58 #define LAST_RESUME_REASON "/sys/kernel/wakeup_reasons/last_resume_reason"
59 #define MAX_REASON_SIZE 512
60 
61 static bool wakeup_init = false;
62 static sem_t wakeup_sem;
63 extern sp<IPowerV1_0> getPowerHalV1_0();
64 extern sp<IPowerV1_1> getPowerHalV1_1();
65 extern bool processPowerHalReturn(const Return<void> &ret, const char* functionName);
66 
67 // Java methods used in getLowPowerStats
68 static jmethodID jgetAndUpdatePlatformState = NULL;
69 static jmethodID jgetSubsystem = NULL;
70 static jmethodID jputVoter = NULL;
71 static jmethodID jputState = NULL;
72 
wakeup_callback(bool success)73 static void wakeup_callback(bool success)
74 {
75     ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted");
76     int ret = sem_post(&wakeup_sem);
77     if (ret < 0) {
78         char buf[80];
79         strerror_r(errno, buf, sizeof(buf));
80         ALOGE("Error posting wakeup sem: %s\n", buf);
81     }
82 }
83 
nativeWaitWakeup(JNIEnv * env,jobject clazz,jobject outBuf)84 static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf)
85 {
86     if (outBuf == NULL) {
87         jniThrowException(env, "java/lang/NullPointerException", "null argument");
88         return -1;
89     }
90 
91     // Register our wakeup callback if not yet done.
92     if (!wakeup_init) {
93         wakeup_init = true;
94         ALOGV("Creating semaphore...");
95         int ret = sem_init(&wakeup_sem, 0, 0);
96         if (ret < 0) {
97             char buf[80];
98             strerror_r(errno, buf, sizeof(buf));
99             ALOGE("Error creating semaphore: %s\n", buf);
100             jniThrowException(env, "java/lang/IllegalStateException", buf);
101             return -1;
102         }
103         ALOGV("Registering callback...");
104         autosuspend_set_wakeup_callback(&wakeup_callback);
105     }
106 
107     // Wait for wakeup.
108     ALOGV("Waiting for wakeup...");
109     int ret = sem_wait(&wakeup_sem);
110     if (ret < 0) {
111         char buf[80];
112         strerror_r(errno, buf, sizeof(buf));
113         ALOGE("Error waiting on semaphore: %s\n", buf);
114         // Return 0 here to let it continue looping but not return results.
115         return 0;
116     }
117 
118     FILE *fp = fopen(LAST_RESUME_REASON, "r");
119     if (fp == NULL) {
120         ALOGE("Failed to open %s", LAST_RESUME_REASON);
121         return -1;
122     }
123 
124     char* mergedreason = (char*)env->GetDirectBufferAddress(outBuf);
125     int remainreasonlen = (int)env->GetDirectBufferCapacity(outBuf);
126 
127     ALOGV("Reading wakeup reasons");
128     char* mergedreasonpos = mergedreason;
129     char reasonline[128];
130     int i = 0;
131     while (fgets(reasonline, sizeof(reasonline), fp) != NULL) {
132         char* pos = reasonline;
133         char* endPos;
134         int len;
135         // First field is the index or 'Abort'.
136         int irq = (int)strtol(pos, &endPos, 10);
137         if (pos != endPos) {
138             // Write the irq number to the merged reason string.
139             len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "%d" : ":%d", irq);
140         } else {
141             // The first field is not an irq, it may be the word Abort.
142             const size_t abortPrefixLen = strlen("Abort:");
143             if (strncmp(pos, "Abort:", abortPrefixLen) != 0) {
144                 // Ooops.
145                 ALOGE("Bad reason line: %s", reasonline);
146                 continue;
147             }
148 
149             // Write 'Abort' to the merged reason string.
150             len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "Abort" : ":Abort");
151             endPos = pos + abortPrefixLen;
152         }
153         pos = endPos;
154 
155         if (len >= 0 && len < remainreasonlen) {
156             mergedreasonpos += len;
157             remainreasonlen -= len;
158         }
159 
160         // Skip whitespace; rest of the buffer is the reason string.
161         while (*pos == ' ') {
162             pos++;
163         }
164 
165         // Chop newline at end.
166         char* endpos = pos;
167         while (*endpos != 0) {
168             if (*endpos == '\n') {
169                 *endpos = 0;
170                 break;
171             }
172             endpos++;
173         }
174 
175         len = snprintf(mergedreasonpos, remainreasonlen, ":%s", pos);
176         if (len >= 0 && len < remainreasonlen) {
177             mergedreasonpos += len;
178             remainreasonlen -= len;
179         }
180         i++;
181     }
182 
183     ALOGV("Got %d reasons", i);
184     if (i > 0) {
185         *mergedreasonpos = 0;
186     }
187 
188     if (fclose(fp) != 0) {
189         ALOGE("Failed to close %s", LAST_RESUME_REASON);
190         return -1;
191     }
192     return mergedreasonpos - mergedreason;
193 }
194 
getLowPowerStats(JNIEnv * env,jobject,jobject jrpmStats)195 static void getLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject jrpmStats) {
196     if (jrpmStats == NULL) {
197         jniThrowException(env, "java/lang/NullPointerException",
198                 "The rpmstats jni input jobject jrpmStats is null.");
199         return;
200     }
201     if (jgetAndUpdatePlatformState == NULL || jgetSubsystem == NULL
202             || jputVoter == NULL || jputState == NULL) {
203         ALOGE("A rpmstats jni jmethodID is null.");
204         return;
205     }
206 
207     sp<IPowerV1_0> powerHalV1_0 = getPowerHalV1_0();
208     if (powerHalV1_0 == nullptr) {
209         ALOGE("Power Hal not loaded");
210         return;
211     }
212 
213     Return<void> ret = powerHalV1_0->getPlatformLowPowerStats(
214             [&env, &jrpmStats](hidl_vec<PowerStatePlatformSleepState> states, Status status) {
215 
216             if (status != Status::SUCCESS) return;
217 
218             for (size_t i = 0; i < states.size(); i++) {
219                 const PowerStatePlatformSleepState& state = states[i];
220 
221                 jobject jplatformState = env->CallObjectMethod(jrpmStats,
222                         jgetAndUpdatePlatformState,
223                         env->NewStringUTF(state.name.c_str()),
224                         state.residencyInMsecSinceBoot,
225                         state.totalTransitions);
226                 if (jplatformState == NULL) {
227                     ALOGE("The rpmstats jni jobject jplatformState is null.");
228                     return;
229                 }
230 
231                 for (size_t j = 0; j < state.voters.size(); j++) {
232                     const PowerStateVoter& voter = state.voters[j];
233                     env->CallVoidMethod(jplatformState, jputVoter,
234                             env->NewStringUTF(voter.name.c_str()),
235                             voter.totalTimeInMsecVotedForSinceBoot,
236                             voter.totalNumberOfTimesVotedSinceBoot);
237                 }
238             }
239     });
240     if (!processPowerHalReturn(ret, "getPlatformLowPowerStats")) {
241         return;
242     }
243 
244     // Trying to get IPower 1.1, this will succeed only for devices supporting 1.1
245     sp<IPowerV1_1> powerHal_1_1 = getPowerHalV1_1();
246     if (powerHal_1_1 == nullptr) {
247         // This device does not support IPower@1.1, exiting gracefully
248         return;
249     }
250     ret = powerHal_1_1->getSubsystemLowPowerStats(
251             [&env, &jrpmStats](hidl_vec<PowerStateSubsystem> subsystems, Status status) {
252 
253         if (status != Status::SUCCESS) return;
254 
255         if (subsystems.size() > 0) {
256             for (size_t i = 0; i < subsystems.size(); i++) {
257                 const PowerStateSubsystem &subsystem = subsystems[i];
258 
259                 jobject jsubsystem = env->CallObjectMethod(jrpmStats, jgetSubsystem,
260                         env->NewStringUTF(subsystem.name.c_str()));
261                 if (jsubsystem == NULL) {
262                     ALOGE("The rpmstats jni jobject jsubsystem is null.");
263                     return;
264                 }
265 
266                 for (size_t j = 0; j < subsystem.states.size(); j++) {
267                     const PowerStateSubsystemSleepState& state = subsystem.states[j];
268                     env->CallVoidMethod(jsubsystem, jputState,
269                             env->NewStringUTF(state.name.c_str()),
270                             state.residencyInMsecSinceBoot,
271                             state.totalTransitions);
272                 }
273             }
274         }
275     });
276     processPowerHalReturn(ret, "getSubsystemLowPowerStats");
277 }
278 
getPlatformLowPowerStats(JNIEnv * env,jobject,jobject outBuf)279 static jint getPlatformLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
280     char *output = (char*)env->GetDirectBufferAddress(outBuf);
281     char *offset = output;
282     int remaining = (int)env->GetDirectBufferCapacity(outBuf);
283     int total_added = -1;
284 
285     if (outBuf == NULL) {
286         jniThrowException(env, "java/lang/NullPointerException", "null argument");
287         return -1;
288     }
289 
290     {
291         sp<IPowerV1_0> powerHalV1_0 = getPowerHalV1_0();
292         if (powerHalV1_0 == nullptr) {
293             ALOGE("Power Hal not loaded");
294             return -1;
295         }
296 
297         Return<void> ret = powerHalV1_0->getPlatformLowPowerStats(
298             [&offset, &remaining, &total_added](hidl_vec<PowerStatePlatformSleepState> states,
299                     Status status) {
300                 if (status != Status::SUCCESS)
301                     return;
302                 for (size_t i = 0; i < states.size(); i++) {
303                     int added;
304                     const PowerStatePlatformSleepState& state = states[i];
305 
306                     added = snprintf(offset, remaining,
307                         "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ",
308                         i + 1, state.name.c_str(), state.residencyInMsecSinceBoot,
309                         state.totalTransitions);
310                     if (added < 0) {
311                         break;
312                     }
313                     if (added > remaining) {
314                         added = remaining;
315                     }
316                     offset += added;
317                     remaining -= added;
318                     total_added += added;
319 
320                     for (size_t j = 0; j < state.voters.size(); j++) {
321                         const PowerStateVoter& voter = state.voters[j];
322                         added = snprintf(offset, remaining,
323                                 "voter_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ",
324                                 j + 1, voter.name.c_str(),
325                                 voter.totalTimeInMsecVotedForSinceBoot,
326                                 voter.totalNumberOfTimesVotedSinceBoot);
327                         if (added < 0) {
328                             break;
329                         }
330                         if (added > remaining) {
331                             added = remaining;
332                         }
333                         offset += added;
334                         remaining -= added;
335                         total_added += added;
336                     }
337 
338                     if (remaining <= 0) {
339                         /* rewrite NULL character*/
340                         offset--;
341                         total_added--;
342                         ALOGE("PowerHal: buffer not enough");
343                         break;
344                     }
345                 }
346             }
347         );
348 
349         if (!processPowerHalReturn(ret, "getPlatformLowPowerStats")) {
350             return -1;
351         }
352     }
353     *offset = 0;
354     total_added += 1;
355     return total_added;
356 }
357 
getSubsystemLowPowerStats(JNIEnv * env,jobject,jobject outBuf)358 static jint getSubsystemLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) {
359     char *output = (char*)env->GetDirectBufferAddress(outBuf);
360     char *offset = output;
361     int remaining = (int)env->GetDirectBufferCapacity(outBuf);
362     int total_added = -1;
363 
364     // This is a IPower 1.1 API
365     sp<IPowerV1_1> powerHal_1_1 = nullptr;
366 
367     if (outBuf == NULL) {
368         jniThrowException(env, "java/lang/NullPointerException", "null argument");
369         return -1;
370     }
371 
372     {
373         // Trying to get 1.1, this will succeed only for devices supporting 1.1
374         powerHal_1_1 = getPowerHalV1_1();
375         if (powerHal_1_1 == nullptr) {
376             //This device does not support IPower@1.1, exiting gracefully
377             return 0;
378         }
379 
380         Return<void> ret = powerHal_1_1->getSubsystemLowPowerStats(
381            [&offset, &remaining, &total_added](hidl_vec<PowerStateSubsystem> subsystems,
382                 Status status) {
383 
384             if (status != Status::SUCCESS)
385                 return;
386 
387             if (subsystems.size() > 0) {
388                 int added = snprintf(offset, remaining, "SubsystemPowerState ");
389                 offset += added;
390                 remaining -= added;
391                 total_added += added;
392 
393                 for (size_t i = 0; i < subsystems.size(); i++) {
394                     const PowerStateSubsystem &subsystem = subsystems[i];
395 
396                     added = snprintf(offset, remaining,
397                                      "subsystem_%zu name=%s ", i + 1, subsystem.name.c_str());
398                     if (added < 0) {
399                         break;
400                     }
401 
402                     if (added > remaining) {
403                         added = remaining;
404                     }
405 
406                     offset += added;
407                     remaining -= added;
408                     total_added += added;
409 
410                     for (size_t j = 0; j < subsystem.states.size(); j++) {
411                         const PowerStateSubsystemSleepState& state = subsystem.states[j];
412                         added = snprintf(offset, remaining,
413                                          "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " last entry=%" PRIu64 " ",
414                                          j + 1, state.name.c_str(), state.residencyInMsecSinceBoot,
415                                          state.totalTransitions, state.lastEntryTimestampMs);
416                         if (added < 0) {
417                             break;
418                         }
419 
420                         if (added > remaining) {
421                             added = remaining;
422                         }
423 
424                         offset += added;
425                         remaining -= added;
426                         total_added += added;
427                     }
428 
429                     if (remaining <= 0) {
430                         /* rewrite NULL character*/
431                         offset--;
432                         total_added--;
433                         ALOGE("PowerHal: buffer not enough");
434                         break;
435                     }
436                 }
437             }
438         }
439         );
440 
441         if (!processPowerHalReturn(ret, "getSubsystemLowPowerStats")) {
442             return -1;
443         }
444     }
445 
446     *offset = 0;
447     total_added += 1;
448     return total_added;
449 }
450 
451 static const JNINativeMethod method_table[] = {
452     { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup },
453     { "getLowPowerStats", "(Lcom/android/internal/os/RpmStats;)V", (void*)getLowPowerStats },
454     { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats },
455     { "getSubsystemLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getSubsystemLowPowerStats },
456 };
457 
register_android_server_BatteryStatsService(JNIEnv * env)458 int register_android_server_BatteryStatsService(JNIEnv *env)
459 {
460     // get java classes and methods
461     jclass clsRpmStats = env->FindClass("com/android/internal/os/RpmStats");
462     jclass clsPowerStatePlatformSleepState =
463             env->FindClass("com/android/internal/os/RpmStats$PowerStatePlatformSleepState");
464     jclass clsPowerStateSubsystem =
465             env->FindClass("com/android/internal/os/RpmStats$PowerStateSubsystem");
466     if (clsRpmStats == NULL || clsPowerStatePlatformSleepState == NULL
467             || clsPowerStateSubsystem == NULL) {
468         ALOGE("A rpmstats jni jclass is null.");
469     } else {
470         jgetAndUpdatePlatformState = env->GetMethodID(clsRpmStats, "getAndUpdatePlatformState",
471                 "(Ljava/lang/String;JI)Lcom/android/internal/os/RpmStats$PowerStatePlatformSleepState;");
472         jgetSubsystem = env->GetMethodID(clsRpmStats, "getSubsystem",
473                 "(Ljava/lang/String;)Lcom/android/internal/os/RpmStats$PowerStateSubsystem;");
474         jputVoter = env->GetMethodID(clsPowerStatePlatformSleepState, "putVoter",
475                 "(Ljava/lang/String;JI)V");
476         jputState = env->GetMethodID(clsPowerStateSubsystem, "putState",
477                 "(Ljava/lang/String;JI)V");
478     }
479 
480     return jniRegisterNativeMethods(env, "com/android/server/am/BatteryStatsService",
481             method_table, NELEM(method_table));
482 }
483 
484 };
485