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