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