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