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