Home | History | Annotate | Download | only in jni
      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 
     42 namespace android
     43 {
     44 
     45 #define LAST_RESUME_REASON "/sys/kernel/wakeup_reasons/last_resume_reason"
     46 #define MAX_REASON_SIZE 512
     47 
     48 static bool wakeup_init = false;
     49 static sem_t wakeup_sem;
     50 
     51 static 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 
     62 static 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 
    191 static JNINativeMethod method_table[] = {
    192     { "nativeWaitWakeup", "([I[Ljava/lang/String;)I", (void*)nativeWaitWakeup },
    193 };
    194 
    195 int 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