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 <suspend/autosuspend.h> 31 32#include <stdio.h> 33#include <errno.h> 34#include <fcntl.h> 35#include <semaphore.h> 36#include <stddef.h> 37#include <string.h> 38#include <sys/stat.h> 39#include <sys/types.h> 40#include <unistd.h> 41 42namespace android 43{ 44 45#define LAST_RESUME_REASON "/sys/kernel/wakeup_reasons/last_resume_reason" 46#define MAX_REASON_SIZE 512 47 48static bool wakeup_init = false; 49static sem_t wakeup_sem; 50 51static void wakeup_callback(void) 52{ 53 ALOGV("In wakeup_callback"); 54 int ret = sem_post(&wakeup_sem); 55 if (ret < 0) { 56 char buf[80]; 57 strerror_r(errno, buf, sizeof(buf)); 58 ALOGE("Error posting wakeup sem: %s\n", buf); 59 } 60} 61 62static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jintArray outIrqs, 63 jobjectArray outReasons) 64{ 65 bool first_time = false; 66 67 if (outIrqs == NULL || outReasons == 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 // First time through, we will just drain the current wakeup reasons. 87 first_time = true; 88 } else { 89 // On following calls, we need to wait for wakeup. 90 ALOGV("Waiting for wakeup..."); 91 int ret = sem_wait(&wakeup_sem); 92 if (ret < 0) { 93 char buf[80]; 94 strerror_r(errno, buf, sizeof(buf)); 95 ALOGE("Error waiting on semaphore: %s\n", buf); 96 // Return 0 here to let it continue looping but not return results. 97 return 0; 98 } 99 } 100 101 FILE *fp = fopen(LAST_RESUME_REASON, "r"); 102 if (fp == NULL) { 103 ALOGE("Failed to open %s", LAST_RESUME_REASON); 104 return -1; 105 } 106 107 int numOut = env->GetArrayLength(outIrqs); 108 ScopedIntArrayRW irqs(env, outIrqs); 109 110 ALOGV("Reading up to %d wakeup reasons", numOut); 111 112 char mergedreason[MAX_REASON_SIZE]; 113 char* mergedreasonpos = mergedreason; 114 int remainreasonlen = MAX_REASON_SIZE; 115 int firstirq = 0; 116 char reasonline[128]; 117 int i = 0; 118 while (fgets(reasonline, sizeof(reasonline), fp) != NULL && i < numOut) { 119 char* pos = reasonline; 120 char* endPos; 121 // First field is the index. 122 int irq = (int)strtol(pos, &endPos, 10); 123 if (pos == endPos) { 124 // Ooops. 125 ALOGE("Bad reason line: %s", reasonline); 126 continue; 127 } 128 pos = endPos; 129 // Skip whitespace; rest of the buffer is the reason string. 130 while (*pos == ' ') { 131 pos++; 132 } 133 // Chop newline at end. 134 char* endpos = pos; 135 while (*endpos != 0) { 136 if (*endpos == '\n') { 137 *endpos = 0; 138 break; 139 } 140 endpos++; 141 } 142 // For now we are not separating out the first irq. 143 // This is because in practice there are always multiple 144 // lines of wakeup reasons, so it is better to just treat 145 // them all together as a single string. 146 if (false && i == 0) { 147 firstirq = irq; 148 } else { 149 int len = snprintf(mergedreasonpos, remainreasonlen, 150 i == 0 ? "%d" : ":%d", irq); 151 if (len >= 0 && len < remainreasonlen) { 152 mergedreasonpos += len; 153 remainreasonlen -= len; 154 } 155 } 156 int len = snprintf(mergedreasonpos, remainreasonlen, ":%s", pos); 157 if (len >= 0 && len < remainreasonlen) { 158 mergedreasonpos += len; 159 remainreasonlen -= len; 160 } 161 // For now it is better to combine all of these in to one entry in the 162 // battery history. In the future, it might be nice to figure out a way 163 // to efficiently store multiple lines as a single entry in the history. 164 //irqs[i] = irq; 165 //ScopedLocalRef<jstring> reasonString(env, env->NewStringUTF(pos)); 166 //env->SetObjectArrayElement(outReasons, i, reasonString.get()); 167 //ALOGV("Wakeup reason #%d: irw %d reason %s", i, irq, pos); 168 i++; 169 } 170 171 ALOGV("Got %d reasons", i); 172 if (first_time) { 173 i = 0; 174 } 175 if (i > 0) { 176 irqs[0] = firstirq; 177 *mergedreasonpos = 0; 178 ScopedLocalRef<jstring> reasonString(env, env->NewStringUTF(mergedreason)); 179 env->SetObjectArrayElement(outReasons, 0, reasonString.get()); 180 i = 1; 181 } 182 183 if (fclose(fp) != 0) { 184 ALOGE("Failed to close %s", LAST_RESUME_REASON); 185 return -1; 186 } 187 188 return first_time ? 0 : i; 189} 190 191static JNINativeMethod method_table[] = { 192 { "nativeWaitWakeup", "([I[Ljava/lang/String;)I", (void*)nativeWaitWakeup }, 193}; 194 195int register_android_server_BatteryStatsService(JNIEnv *env) 196{ 197 return jniRegisterNativeMethods(env, "com/android/server/am/BatteryStatsService", 198 method_table, NELEM(method_table)); 199} 200 201}; 202