Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 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 | |
Mark Salyzyn | 96bf598 | 2016-09-28 16:15:30 -0700 | [diff] [blame] | 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 | |
Ruchi Kandoi | 0d43404 | 2016-10-03 09:12:02 -0700 | [diff] [blame] | 31 | #include <android/hardware/power/1.0/IPower.h> |
Ahmed ElArabawy | d8b4411 | 2017-05-23 21:25:02 +0000 | [diff] [blame] | 32 | #include <android/hardware/power/1.1/IPower.h> |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 33 | #include <android_runtime/AndroidRuntime.h> |
| 34 | #include <jni.h> |
| 35 | |
Steven Moreland | 2279b25 | 2017-07-19 09:50:45 -0700 | [diff] [blame] | 36 | #include <nativehelper/ScopedLocalRef.h> |
| 37 | #include <nativehelper/ScopedPrimitiveArray.h> |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 38 | |
Mark Salyzyn | 96bf598 | 2016-09-28 16:15:30 -0700 | [diff] [blame] | 39 | #include <log/log.h> |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 40 | #include <utils/misc.h> |
| 41 | #include <utils/Log.h> |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 42 | #include <suspend/autosuspend.h> |
| 43 | |
Ruchi Kandoi | 0d43404 | 2016-10-03 09:12:02 -0700 | [diff] [blame] | 44 | using android::hardware::Return; |
| 45 | using android::hardware::Void; |
Ruchi Kandoi | 0d43404 | 2016-10-03 09:12:02 -0700 | [diff] [blame] | 46 | using android::hardware::power::V1_0::PowerStatePlatformSleepState; |
| 47 | using android::hardware::power::V1_0::PowerStateVoter; |
| 48 | using android::hardware::power::V1_0::Status; |
Ahmed ElArabawy | d8b4411 | 2017-05-23 21:25:02 +0000 | [diff] [blame] | 49 | using android::hardware::power::V1_1::PowerStateSubsystem; |
| 50 | using android::hardware::power::V1_1::PowerStateSubsystemSleepState; |
Ruchi Kandoi | 0d43404 | 2016-10-03 09:12:02 -0700 | [diff] [blame] | 51 | using android::hardware::hidl_vec; |
Wei Wang | 5d77c72 | 2017-11-29 12:28:00 -0800 | [diff] [blame] | 52 | using IPowerV1_1 = android::hardware::power::V1_1::IPower; |
| 53 | using IPowerV1_0 = android::hardware::power::V1_0::IPower; |
Ruchi Kandoi | 0d43404 | 2016-10-03 09:12:02 -0700 | [diff] [blame] | 54 | |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 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; |
Wei Wang | 5d77c72 | 2017-11-29 12:28:00 -0800 | [diff] [blame] | 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); |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 66 | |
Bookatz | 50df711 | 2017-08-04 14:53:26 -0700 | [diff] [blame] | 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 | |
Adam Lesinski | 87fd322 | 2015-06-25 13:10:36 -0700 | [diff] [blame] | 73 | static void wakeup_callback(bool success) |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 74 | { |
Adam Lesinski | 87fd322 | 2015-06-25 13:10:36 -0700 | [diff] [blame] | 75 | ALOGV("In wakeup_callback: %s", success ? "resumed from suspend" : "suspend aborted"); |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 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 | |
Adam Lesinski | 515702c | 2015-07-23 18:13:38 -0700 | [diff] [blame] | 84 | static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jobject outBuf) |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 85 | { |
Adam Lesinski | 515702c | 2015-07-23 18:13:38 -0700 | [diff] [blame] | 86 | if (outBuf == NULL) { |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 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..."); |
Steve Paik | 4955d6c | 2017-12-18 10:23:31 -0800 | [diff] [blame] | 104 | autosuspend_set_wakeup_callback(&wakeup_callback); |
Adam Lesinski | 6b0331a | 2015-06-05 17:08:22 -0700 | [diff] [blame] | 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; |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 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 | |
Adam Lesinski | 515702c | 2015-07-23 18:13:38 -0700 | [diff] [blame] | 124 | char* mergedreason = (char*)env->GetDirectBufferAddress(outBuf); |
| 125 | int remainreasonlen = (int)env->GetDirectBufferCapacity(outBuf); |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 126 | |
Adam Lesinski | 515702c | 2015-07-23 18:13:38 -0700 | [diff] [blame] | 127 | ALOGV("Reading wakeup reasons"); |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 128 | char* mergedreasonpos = mergedreason; |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 129 | char reasonline[128]; |
| 130 | int i = 0; |
Adam Lesinski | 87fd322 | 2015-06-25 13:10:36 -0700 | [diff] [blame] | 131 | while (fgets(reasonline, sizeof(reasonline), fp) != NULL) { |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 132 | char* pos = reasonline; |
| 133 | char* endPos; |
Adam Lesinski | 87fd322 | 2015-06-25 13:10:36 -0700 | [diff] [blame] | 134 | int len; |
| 135 | // First field is the index or 'Abort'. |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 136 | int irq = (int)strtol(pos, &endPos, 10); |
Adam Lesinski | 87fd322 | 2015-06-25 13:10:36 -0700 | [diff] [blame] | 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; |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 152 | } |
| 153 | pos = endPos; |
Adam Lesinski | 87fd322 | 2015-06-25 13:10:36 -0700 | [diff] [blame] | 154 | |
| 155 | if (len >= 0 && len < remainreasonlen) { |
| 156 | mergedreasonpos += len; |
| 157 | remainreasonlen -= len; |
| 158 | } |
| 159 | |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 160 | // Skip whitespace; rest of the buffer is the reason string. |
| 161 | while (*pos == ' ') { |
| 162 | pos++; |
| 163 | } |
Adam Lesinski | 87fd322 | 2015-06-25 13:10:36 -0700 | [diff] [blame] | 164 | |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 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 | } |
Adam Lesinski | 87fd322 | 2015-06-25 13:10:36 -0700 | [diff] [blame] | 174 | |
| 175 | len = snprintf(mergedreasonpos, remainreasonlen, ":%s", pos); |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 176 | if (len >= 0 && len < remainreasonlen) { |
| 177 | mergedreasonpos += len; |
| 178 | remainreasonlen -= len; |
| 179 | } |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 180 | i++; |
| 181 | } |
| 182 | |
| 183 | ALOGV("Got %d reasons", i); |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 184 | if (i > 0) { |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 185 | *mergedreasonpos = 0; |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 186 | } |
| 187 | |
| 188 | if (fclose(fp) != 0) { |
| 189 | ALOGE("Failed to close %s", LAST_RESUME_REASON); |
| 190 | return -1; |
| 191 | } |
Adam Lesinski | 515702c | 2015-07-23 18:13:38 -0700 | [diff] [blame] | 192 | return mergedreasonpos - mergedreason; |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 193 | } |
| 194 | |
Bookatz | 50df711 | 2017-08-04 14:53:26 -0700 | [diff] [blame] | 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 | |
Wei Wang | 5d77c72 | 2017-11-29 12:28:00 -0800 | [diff] [blame] | 207 | sp<IPowerV1_0> powerHalV1_0 = getPowerHalV1_0(); |
| 208 | if (powerHalV1_0 == nullptr) { |
Bookatz | 50df711 | 2017-08-04 14:53:26 -0700 | [diff] [blame] | 209 | ALOGE("Power Hal not loaded"); |
| 210 | return; |
| 211 | } |
| 212 | |
Wei Wang | 5d77c72 | 2017-11-29 12:28:00 -0800 | [diff] [blame] | 213 | Return<void> ret = powerHalV1_0->getPlatformLowPowerStats( |
Bookatz | 50df711 | 2017-08-04 14:53:26 -0700 | [diff] [blame] | 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 | }); |
Wei Wang | 5d77c72 | 2017-11-29 12:28:00 -0800 | [diff] [blame] | 240 | if (!processPowerHalReturn(ret, "getPlatformLowPowerStats")) { |
Bookatz | 50df711 | 2017-08-04 14:53:26 -0700 | [diff] [blame] | 241 | return; |
| 242 | } |
| 243 | |
Wei Wang | 5d77c72 | 2017-11-29 12:28:00 -0800 | [diff] [blame] | 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@1.1, exiting gracefully |
Bookatz | 50df711 | 2017-08-04 14:53:26 -0700 | [diff] [blame] | 248 | return; |
| 249 | } |
Wei Wang | 5d77c72 | 2017-11-29 12:28:00 -0800 | [diff] [blame] | 250 | ret = powerHal_1_1->getSubsystemLowPowerStats( |
Bookatz | 50df711 | 2017-08-04 14:53:26 -0700 | [diff] [blame] | 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 | }); |
Wei Wang | 5d77c72 | 2017-11-29 12:28:00 -0800 | [diff] [blame] | 276 | processPowerHalReturn(ret, "getSubsystemLowPowerStats"); |
Bookatz | 50df711 | 2017-08-04 14:53:26 -0700 | [diff] [blame] | 277 | } |
| 278 | |
Badhri Jagan Sridharan | 68cdf19 | 2016-04-03 21:57:15 -0700 | [diff] [blame] | 279 | static jint getPlatformLowPowerStats(JNIEnv* env, jobject /* clazz */, jobject outBuf) { |
Ruchi Kandoi | 0d43404 | 2016-10-03 09:12:02 -0700 | [diff] [blame] | 280 | char *output = (char*)env->GetDirectBufferAddress(outBuf); |
| 281 | char *offset = output; |
Badhri Jagan Sridharan | 68cdf19 | 2016-04-03 21:57:15 -0700 | [diff] [blame] | 282 | int remaining = (int)env->GetDirectBufferCapacity(outBuf); |
Badhri Jagan Sridharan | 68cdf19 | 2016-04-03 21:57:15 -0700 | [diff] [blame] | 283 | int total_added = -1; |
| 284 | |
| 285 | if (outBuf == NULL) { |
| 286 | jniThrowException(env, "java/lang/NullPointerException", "null argument"); |
Ruchi Kandoi | 0d43404 | 2016-10-03 09:12:02 -0700 | [diff] [blame] | 287 | return -1; |
Badhri Jagan Sridharan | 68cdf19 | 2016-04-03 21:57:15 -0700 | [diff] [blame] | 288 | } |
| 289 | |
Connor O'Brien | 578eb7f | 2017-02-27 16:41:35 -0800 | [diff] [blame] | 290 | { |
Wei Wang | 5d77c72 | 2017-11-29 12:28:00 -0800 | [diff] [blame] | 291 | sp<IPowerV1_0> powerHalV1_0 = getPowerHalV1_0(); |
| 292 | if (powerHalV1_0 == nullptr) { |
Connor O'Brien | 578eb7f | 2017-02-27 16:41:35 -0800 | [diff] [blame] | 293 | ALOGE("Power Hal not loaded"); |
| 294 | return -1; |
| 295 | } |
Badhri Jagan Sridharan | 68cdf19 | 2016-04-03 21:57:15 -0700 | [diff] [blame] | 296 | |
Wei Wang | 5d77c72 | 2017-11-29 12:28:00 -0800 | [diff] [blame] | 297 | Return<void> ret = powerHalV1_0->getPlatformLowPowerStats( |
Connor O'Brien | 578eb7f | 2017-02-27 16:41:35 -0800 | [diff] [blame] | 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]; |
Badhri Jagan Sridharan | 68cdf19 | 2016-04-03 21:57:15 -0700 | [diff] [blame] | 305 | |
Ruchi Kandoi | 0d43404 | 2016-10-03 09:12:02 -0700 | [diff] [blame] | 306 | added = snprintf(offset, remaining, |
Connor O'Brien | 578eb7f | 2017-02-27 16:41:35 -0800 | [diff] [blame] | 307 | "state_%zu name=%s time=%" PRIu64 " count=%" PRIu64 " ", |
| 308 | i + 1, state.name.c_str(), state.residencyInMsecSinceBoot, |
| 309 | state.totalTransitions); |
Ruchi Kandoi | 0d43404 | 2016-10-03 09:12:02 -0700 | [diff] [blame] | 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; |
Ruchi Kandoi | 0d43404 | 2016-10-03 09:12:02 -0700 | [diff] [blame] | 319 | |
Connor O'Brien | 578eb7f | 2017-02-27 16:41:35 -0800 | [diff] [blame] | 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 | } |
Ruchi Kandoi | 0d43404 | 2016-10-03 09:12:02 -0700 | [diff] [blame] | 345 | } |
Badhri Jagan Sridharan | 68cdf19 | 2016-04-03 21:57:15 -0700 | [diff] [blame] | 346 | } |
Connor O'Brien | 578eb7f | 2017-02-27 16:41:35 -0800 | [diff] [blame] | 347 | ); |
| 348 | |
Wei Wang | 5d77c72 | 2017-11-29 12:28:00 -0800 | [diff] [blame] | 349 | if (!processPowerHalReturn(ret, "getPlatformLowPowerStats")) { |
Connor O'Brien | 578eb7f | 2017-02-27 16:41:35 -0800 | [diff] [blame] | 350 | return -1; |
Badhri Jagan Sridharan | 68cdf19 | 2016-04-03 21:57:15 -0700 | [diff] [blame] | 351 | } |
Connor O'Brien | 578eb7f | 2017-02-27 16:41:35 -0800 | [diff] [blame] | 352 | } |
Badhri Jagan Sridharan | 68cdf19 | 2016-04-03 21:57:15 -0700 | [diff] [blame] | 353 | *offset = 0; |
| 354 | total_added += 1; |
Badhri Jagan Sridharan | 68cdf19 | 2016-04-03 21:57:15 -0700 | [diff] [blame] | 355 | return total_added; |
| 356 | } |
| 357 | |
Ahmed ElArabawy | d8b4411 | 2017-05-23 21:25:02 +0000 | [diff] [blame] | 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 | |
Wei Wang | 5d77c72 | 2017-11-29 12:28:00 -0800 | [diff] [blame] | 364 | // This is a IPower 1.1 API |
| 365 | sp<IPowerV1_1> powerHal_1_1 = nullptr; |
Ahmed ElArabawy | d8b4411 | 2017-05-23 21:25:02 +0000 | [diff] [blame] | 366 | |
| 367 | if (outBuf == NULL) { |
| 368 | jniThrowException(env, "java/lang/NullPointerException", "null argument"); |
| 369 | return -1; |
| 370 | } |
| 371 | |
| 372 | { |
Wei Wang | 5d77c72 | 2017-11-29 12:28:00 -0800 | [diff] [blame] | 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) { |
Ahmed ElArabawy | d8b4411 | 2017-05-23 21:25:02 +0000 | [diff] [blame] | 376 | //This device does not support IPower@1.1, exiting gracefully |
| 377 | return 0; |
Wei Wang | 5d77c72 | 2017-11-29 12:28:00 -0800 | [diff] [blame] | 378 | } |
Ahmed ElArabawy | d8b4411 | 2017-05-23 21:25:02 +0000 | [diff] [blame] | 379 | |
Wei Wang | 5d77c72 | 2017-11-29 12:28:00 -0800 | [diff] [blame] | 380 | Return<void> ret = powerHal_1_1->getSubsystemLowPowerStats( |
Ahmed ElArabawy | d8b4411 | 2017-05-23 21:25:02 +0000 | [diff] [blame] | 381 | [&offset, &remaining, &total_added](hidl_vec<PowerStateSubsystem> subsystems, |
| 382 | Status status) { |
| 383 | |
| 384 | if (status != Status::SUCCESS) |
| 385 | return; |
| 386 | |
Ahmed ElArabawy | 307edcd | 2017-07-07 17:48:13 -0700 | [diff] [blame] | 387 | if (subsystems.size() > 0) { |
| 388 | int added = snprintf(offset, remaining, "SubsystemPowerState "); |
Ahmed ElArabawy | d8b4411 | 2017-05-23 21:25:02 +0000 | [diff] [blame] | 389 | offset += added; |
| 390 | remaining -= added; |
| 391 | total_added += added; |
| 392 | |
Ahmed ElArabawy | 307edcd | 2017-07-07 17:48:13 -0700 | [diff] [blame] | 393 | for (size_t i = 0; i < subsystems.size(); i++) { |
| 394 | const PowerStateSubsystem &subsystem = subsystems[i]; |
| 395 | |
Ahmed ElArabawy | d8b4411 | 2017-05-23 21:25:02 +0000 | [diff] [blame] | 396 | added = snprintf(offset, remaining, |
Ahmed ElArabawy | 307edcd | 2017-07-07 17:48:13 -0700 | [diff] [blame] | 397 | "subsystem_%zu name=%s ", i + 1, subsystem.name.c_str()); |
Ahmed ElArabawy | d8b4411 | 2017-05-23 21:25:02 +0000 | [diff] [blame] | 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; |
Ahmed ElArabawy | d8b4411 | 2017-05-23 21:25:02 +0000 | [diff] [blame] | 409 | |
Ahmed ElArabawy | 307edcd | 2017-07-07 17:48:13 -0700 | [diff] [blame] | 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 | } |
Ahmed ElArabawy | d8b4411 | 2017-05-23 21:25:02 +0000 | [diff] [blame] | 436 | } |
| 437 | } |
| 438 | } |
| 439 | ); |
| 440 | |
Wei Wang | 5d77c72 | 2017-11-29 12:28:00 -0800 | [diff] [blame] | 441 | if (!processPowerHalReturn(ret, "getSubsystemLowPowerStats")) { |
Ahmed ElArabawy | d8b4411 | 2017-05-23 21:25:02 +0000 | [diff] [blame] | 442 | return -1; |
| 443 | } |
| 444 | } |
| 445 | |
| 446 | *offset = 0; |
| 447 | total_added += 1; |
| 448 | return total_added; |
| 449 | } |
| 450 | |
Daniel Micay | 76f6a86 | 2015-09-19 17:31:01 -0400 | [diff] [blame] | 451 | static const JNINativeMethod method_table[] = { |
Adam Lesinski | 515702c | 2015-07-23 18:13:38 -0700 | [diff] [blame] | 452 | { "nativeWaitWakeup", "(Ljava/nio/ByteBuffer;)I", (void*)nativeWaitWakeup }, |
Bookatz | 50df711 | 2017-08-04 14:53:26 -0700 | [diff] [blame] | 453 | { "getLowPowerStats", "(Lcom/android/internal/os/RpmStats;)V", (void*)getLowPowerStats }, |
Badhri Jagan Sridharan | 68cdf19 | 2016-04-03 21:57:15 -0700 | [diff] [blame] | 454 | { "getPlatformLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getPlatformLowPowerStats }, |
Ahmed ElArabawy | d8b4411 | 2017-05-23 21:25:02 +0000 | [diff] [blame] | 455 | { "getSubsystemLowPowerStats", "(Ljava/nio/ByteBuffer;)I", (void*)getSubsystemLowPowerStats }, |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 456 | }; |
| 457 | |
| 458 | int register_android_server_BatteryStatsService(JNIEnv *env) |
| 459 | { |
Bookatz | 50df711 | 2017-08-04 14:53:26 -0700 | [diff] [blame] | 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 | |
Dianne Hackborn | c51cf03 | 2014-03-02 19:08:15 -0800 | [diff] [blame] | 480 | return jniRegisterNativeMethods(env, "com/android/server/am/BatteryStatsService", |
| 481 | method_table, NELEM(method_table)); |
| 482 | } |
| 483 | |
| 484 | }; |