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 <android_runtime/AndroidRuntime.h> 21#include <jni.h> 22 23#include <ScopedLocalRef.h> 24#include <ScopedPrimitiveArray.h> 25 26#include <cutils/log.h> 27#include <utils/misc.h> 28#include <utils/Log.h> 29#include <hardware/hardware.h> 30#include <hardware/power.h> 31#include <suspend/autosuspend.h> 32 33#include <inttypes.h> 34#include <stdio.h> 35#include <errno.h> 36#include <fcntl.h> 37#include <semaphore.h> 38#include <stddef.h> 39#include <string.h> 40#include <sys/stat.h> 41#include <sys/types.h> 42#include <unistd.h> 43 44namespace android 45{ 46 47#define LAST_RESUME_REASON "/sys/kernel/wakeup_reasons/last_resume_reason" 48#define MAX_REASON_SIZE 512 49 50static bool wakeup_init = false; 51static sem_t wakeup_sem; 52extern struct power_module* gPowerModule; 53 54static void wakeup_callback(bool success) 55{ 56 ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted"); 57 int ret = sem_post(&wakeup_sem); 58 if (ret < 0) { 59 char buf[80]; 60 strerror_r(errno, buf, sizeof(buf)); 61 ALOGE("Error posting wakeup sem: %s\n", buf); 62 } 63} 64 65static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf) 66{ 67 if (outBuf == NULL) { 68 jniThrowException(env, "java/lang/NullPointerException", "null argument"); 69 return -1; 70 } 71 72 // Register our wakeup callback if not yet done. 73 if (!wakeup_init) { 74 wakeup_init = true; 75 ALOGV("Creating semaphore..."); 76 int ret = sem_init(&wakeup_sem, 0, 0); 77 if (ret < 0) { 78 char buf[80]; 79 strerror_r(errno, buf, sizeof(buf)); 80 ALOGE("Error creating semaphore: %s\n", buf); 81 jniThrowException(env, "java/lang/IllegalStateException", buf); 82 return -1; 83 } 84 ALOGV("Registering callback..."); 85 set_wakeup_callback(&wakeup_callback); 86 } 87 88 // Wait for wakeup. 89 ALOGV("Waiting for wakeup..."); 90 int ret = sem_wait(&wakeup_sem); 91 if (ret < 0) { 92 char buf[80]; 93 strerror_r(errno, buf, sizeof(buf)); 94 ALOGE("Error waiting on semaphore: %s\n", buf); 95 // Return 0 here to let it continue looping but not return results. 96 return 0; 97 } 98 99 FILE *fp = fopen(LAST_RESUME_REASON, "r"); 100 if (fp == NULL) { 101 ALOGE("Failed to open %s", LAST_RESUME_REASON); 102 return -1; 103 } 104 105 char* mergedreason = (char*)env->GetDirectBufferAddress(outBuf); 106 int remainreasonlen = (int)env->GetDirectBufferCapacity(outBuf); 107 108 ALOGV("Reading wakeup reasons"); 109 char* mergedreasonpos = mergedreason; 110 char reasonline[128]; 111 int i = 0; 112 while (fgets(reasonline, sizeof(reasonline), fp) != NULL) { 113 char* pos = reasonline; 114 char* endPos; 115 int len; 116 // First field is the index or 'Abort'. 117 int irq = (int)strtol(pos, &endPos, 10); 118 if (pos != endPos) { 119 // Write the irq number to the merged reason string. 120 len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "%d" : ":%d", irq); 121 } else { 122 // The first field is not an irq, it may be the word Abort. 123 const size_t abortPrefixLen = strlen("Abort:"); 124 if (strncmp(pos, "Abort:", abortPrefixLen) != 0) { 125 // Ooops. 126 ALOGE("Bad reason line: %s", reasonline); 127 continue; 128 } 129 130 // Write 'Abort' to the merged reason string. 131 len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "Abort" : ":Abort"); 132 endPos = pos + abortPrefixLen; 133 } 134 pos = endPos; 135 136 if (len >= 0 && len < remainreasonlen) { 137 mergedreasonpos += len; 138 remainreasonlen -= len; 139 } 140 141 // Skip whitespace; rest of the buffer is the reason string. 142 while (*pos == ' ') { 143 pos++; 144 } 145 146 // Chop newline at end. 147 char* endpos = pos; 148 while (*endpos != 0) { 149 if (*endpos == '\n') { 150 *endpos = 0; 151 break; 152 } 153 endpos++; 154 } 155 156 len = snprintf(mergedreasonpos, remainreasonlen, ":%s", pos); 157 if (len >= 0 && len < remainreasonlen) { 158 mergedreasonpos += len; 159 remainreasonlen -= len; 160 } 161 i++; 162 } 163 164 ALOGV("Got %d reasons", i); 165 if (i > 0) { 166 *mergedreasonpos = 0; 167 } 168 169 if (fclose(fp) != 0) { 170 ALOGE("Failed to close %s", LAST_RESUME_REASON); 171 return -1; 172 } 173 return mergedreasonpos - mergedreason; 174} 175 176static jint getPlatformLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) { 177 int num_modes = -1; 178 char *output = (char*)env->GetDirectBufferAddress(outBuf), *offset = output; 179 int remaining = (int)env->GetDirectBufferCapacity(outBuf); 180 power_state_platform_sleep_state_t *list; 181 size_t *voter_list; 182 int total_added = -1; 183 184 if (outBuf == NULL) { 185 jniThrowException(env, "java/lang/NullPointerException", "null argument"); 186 goto error; 187 } 188 189 if (!gPowerModule) { 190 ALOGE("%s: gPowerModule not loaded", POWER_HARDWARE_MODULE_ID); 191 goto error; 192 } 193 194 if (! (gPowerModule->get_platform_low_power_stats && gPowerModule->get_number_of_platform_modes 195 && gPowerModule->get_voter_list)) { 196 ALOGE("%s: Missing API", POWER_HARDWARE_MODULE_ID); 197 goto error; 198 } 199 200 if (gPowerModule->get_number_of_platform_modes) { 201 num_modes = gPowerModule->get_number_of_platform_modes(gPowerModule); 202 } 203 204 if (num_modes < 1) { 205 ALOGE("%s: Platform does not even have one low power mode", POWER_HARDWARE_MODULE_ID); 206 goto error; 207 } 208 209 list = (power_state_platform_sleep_state_t *)calloc(num_modes, 210 sizeof(power_state_platform_sleep_state_t)); 211 if (!list) { 212 ALOGE("%s: power_state_platform_sleep_state_t allocation failed", POWER_HARDWARE_MODULE_ID); 213 goto error; 214 } 215 216 voter_list = (size_t *)calloc(num_modes, sizeof(*voter_list)); 217 if (!voter_list) { 218 ALOGE("%s: voter_list allocation failed", POWER_HARDWARE_MODULE_ID); 219 goto err_free; 220 } 221 222 gPowerModule->get_voter_list(gPowerModule, voter_list); 223 224 for (int i = 0; i < num_modes; i++) { 225 list[i].voters = (power_state_voter_t *)calloc(voter_list[i], 226 sizeof(power_state_voter_t)); 227 if (!list[i].voters) { 228 ALOGE("%s: voter_t allocation failed", POWER_HARDWARE_MODULE_ID); 229 goto err_free; 230 } 231 } 232 233 if (!gPowerModule->get_platform_low_power_stats(gPowerModule, list)) { 234 for (int i = 0; i < num_modes; i++) { 235 int added; 236 237 added = snprintf(offset, remaining, 238 "state_%d name=%s time=%" PRIu64 " count=%" PRIu64 " ", 239 i + 1, list[i].name, list[i].residency_in_msec_since_boot, 240 list[i].total_transitions); 241 if (added < 0) { 242 break; 243 } 244 if (added > remaining) { 245 added = remaining; 246 } 247 offset += added; 248 remaining -= added; 249 total_added += added; 250 251 for (unsigned int j = 0; j < list[i].number_of_voters; j++) { 252 added = snprintf(offset, remaining, 253 "voter_%d name=%s time=%" PRIu64 " count=%" PRIu64 " ", 254 j + 1, list[i].voters[j].name, 255 list[i].voters[j].total_time_in_msec_voted_for_since_boot, 256 list[i].voters[j].total_number_of_times_voted_since_boot); 257 if (added < 0) { 258 break; 259 } 260 if (added > remaining) { 261 added = remaining; 262 } 263 offset += added; 264 remaining -= added; 265 total_added += added; 266 } 267 268 if (remaining <= 0) { 269 /* rewrite NULL character*/ 270 offset--; 271 total_added--; 272 ALOGE("%s module: buffer not enough", POWER_HARDWARE_MODULE_ID); 273 break; 274 } 275 } 276 } 277 *offset = 0; 278 total_added += 1; 279 280err_free: 281 for (int i = 0; i < num_modes; i++) { 282 free(list[i].voters); 283 } 284 free(list); 285 free(voter_list); 286error: 287 return total_added; 288} 289 290static const JNINativeMethod method_table[] = { 291 { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup }, 292 { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats }, 293}; 294 295int register_android_server_BatteryStatsService(JNIEnv *env) 296{ 297 return jniRegisterNativeMethods(env, "com/android/server/am/BatteryStatsService", 298 method_table, NELEM(method_table)); 299} 300 301}; 302