diff options
author | Dianne Hackborn <hackbod@google.com> | 2014-03-02 19:08:15 -0800 |
---|---|---|
committer | Dianne Hackborn <hackbod@google.com> | 2014-03-07 15:18:45 -0800 |
commit | c51cf03cf2458c8c137f60c7379f2cccf681d16f (patch) | |
tree | 61e1f59bcd3c181d0725d0e2e6ee4f8115f89ee6 /services | |
parent | 71c8fb92a151f2f64ffbaf551c072179bc46c0da (diff) | |
download | frameworks_base-c51cf03cf2458c8c137f60c7379f2cccf681d16f.zip frameworks_base-c51cf03cf2458c8c137f60c7379f2cccf681d16f.tar.gz frameworks_base-c51cf03cf2458c8c137f60c7379f2cccf681d16f.tar.bz2 |
Start recording wakeup reasons in battery history.
Depends on a modification to libsuspend so that we can get
a callback each time the device wakes up, to read the current
wakeup reasons from the kernel. These are then stuffed in
to a new field in the battery history.
Also add new dump options --history-start and --charged
to better control what is dumped.
Finally the alarm manager uses a "*walarm*" tag for history
item wake locks that are coming from a wakeup alarm.
Change-Id: I457571973d5b2b5fdc4e4b63ab16275db20d7edd
Diffstat (limited to 'services')
-rw-r--r-- | services/core/java/com/android/server/AlarmManagerService.java | 5 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/BatteryStatsService.java | 72 | ||||
-rw-r--r-- | services/core/jni/Android.mk | 1 | ||||
-rw-r--r-- | services/core/jni/com_android_server_am_BatteryStatsService.cpp | 196 | ||||
-rw-r--r-- | services/core/jni/onload.cpp | 2 |
5 files changed, 261 insertions, 15 deletions
diff --git a/services/core/java/com/android/server/AlarmManagerService.java b/services/core/java/com/android/server/AlarmManagerService.java index f03a8e0..bda0183 100644 --- a/services/core/java/com/android/server/AlarmManagerService.java +++ b/services/core/java/com/android/server/AlarmManagerService.java @@ -1283,7 +1283,10 @@ class AlarmManagerService extends SystemService { setWakelockWorkSource(alarm.operation, alarm.workSource); mWakeLock.setUnimportantForLogging( alarm.operation == mTimeTickSender); - mWakeLock.setHistoryTag(alarm.operation.getTag("*alarm*:")); + mWakeLock.setHistoryTag(alarm.operation.getTag( + alarm.type == ELAPSED_REALTIME_WAKEUP + || alarm.type == RTC_WAKEUP + ? "*walarm*:" : "*alarm*:")); mWakeLock.acquire(); } final InFlight inflight = new InFlight(AlarmManagerService.this, diff --git a/services/core/java/com/android/server/am/BatteryStatsService.java b/services/core/java/com/android/server/am/BatteryStatsService.java index fc7aac2..39bfc23 100644 --- a/services/core/java/com/android/server/am/BatteryStatsService.java +++ b/services/core/java/com/android/server/am/BatteryStatsService.java @@ -48,6 +48,8 @@ import java.util.List; * battery life. */ public final class BatteryStatsService extends IBatteryStats.Stub { + static final String TAG = "BatteryStatsService"; + static IBatteryStats sService; final BatteryStatsImpl mStats; @@ -66,7 +68,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub { mStats.setRadioScanningTimeout(mContext.getResources().getInteger( com.android.internal.R.integer.config_radioScanningTimeout) * 1000L); - } + (new WakeupReasonThread()).start(); + } public void shutdown() { Slog.w("BatteryStats", "Writing battery stats before shutdown..."); @@ -538,14 +541,45 @@ public final class BatteryStatsService extends IBatteryStats.Stub { mContext.enforcePermission(android.Manifest.permission.UPDATE_DEVICE_STATS, Binder.getCallingPid(), Binder.getCallingUid(), null); } - + + final class WakeupReasonThread extends Thread { + final int[] mIrqs = new int[32]; + final String[] mReasons = new String[32]; + + WakeupReasonThread() { + super("BatteryStats_wakeupReason"); + } + + public void run() { + Process.setThreadPriority(Process.THREAD_PRIORITY_FOREGROUND); + + try { + int num; + while ((num=nativeWaitWakeup(mIrqs, mReasons)) >= 0) { + synchronized (mStats) { + for (int i=0; i<num; i++) { + //Slog.i(TAG, "Wakeup: irq #" + mIrqs[i] + " reason=" + mReasons[i]); + mStats.noteWakeupReasonLocked(mIrqs[i], mReasons[i]); + } + } + } + } catch (RuntimeException e) { + Slog.e(TAG, "Failure reading wakeup reasons", e); + } + } + } + + private static native int nativeWaitWakeup(int[] outIrqs, String[] outReasons); + private void dumpHelp(PrintWriter pw) { pw.println("Battery stats (batterystats) dump options:"); - pw.println(" [--checkin] [--history] [-c] [--unplugged] [--reset] [--write]"); - pw.println(" [-h] [<package.name>]"); + pw.println(" [--checkin] [--history] [--history-start] [--unplugged] [--charged] [-c]"); + pw.println(" [--reset] [--write] [-h] [<package.name>]"); pw.println(" --checkin: format output for a checkin report."); pw.println(" --history: show only history data."); + pw.println(" --history-start <num>: show only history data starting at given time offset."); pw.println(" --unplugged: only output data since last unplugged."); + pw.println(" --charged: only output data since last charged."); pw.println(" --reset: reset the stats, clearing all current data."); pw.println(" --write: force write current collected stats to disk."); pw.println(" -h: print this help text."); @@ -562,23 +596,34 @@ public final class BatteryStatsService extends IBatteryStats.Stub { return; } + int flags = 0; boolean isCheckin = false; - boolean includeHistory = false; - boolean historyOnly = false; - boolean isUnpluggedOnly = false; boolean noOutput = false; + long historyStart = -1; int reqUid = -1; if (args != null) { - for (String arg : args) { + for (int i=0; i<args.length; i++) { + String arg = args[i]; if ("--checkin".equals(arg)) { isCheckin = true; } else if ("--history".equals(arg)) { - historyOnly = true; + flags |= BatteryStats.DUMP_HISTORY_ONLY; + } else if ("--history-start".equals(arg)) { + flags |= BatteryStats.DUMP_HISTORY_ONLY; + i++; + if (i >= args.length) { + pw.println("Missing time argument for --history-since"); + dumpHelp(pw); + return; + } + historyStart = Long.parseLong(args[i]); } else if ("-c".equals(arg)) { isCheckin = true; - includeHistory = true; + flags |= BatteryStats.DUMP_INCLUDE_HISTORY; } else if ("--unplugged".equals(arg)) { - isUnpluggedOnly = true; + flags |= BatteryStats.DUMP_UNPLUGGED_ONLY; + } else if ("--charged".equals(arg)) { + flags |= BatteryStats.DUMP_CHARGED_ONLY; } else if ("--reset".equals(arg)) { synchronized (mStats) { mStats.resetAllStatsCmdLocked(); @@ -619,12 +664,11 @@ public final class BatteryStatsService extends IBatteryStats.Stub { if (isCheckin) { List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0); synchronized (mStats) { - mStats.dumpCheckinLocked(mContext, pw, apps, isUnpluggedOnly, includeHistory, - historyOnly); + mStats.dumpCheckinLocked(mContext, pw, apps, flags, historyStart); } } else { synchronized (mStats) { - mStats.dumpLocked(mContext, pw, isUnpluggedOnly, reqUid, historyOnly); + mStats.dumpLocked(mContext, pw, flags, reqUid, historyStart); } } } diff --git a/services/core/jni/Android.mk b/services/core/jni/Android.mk index 56144b0..0607925 100644 --- a/services/core/jni/Android.mk +++ b/services/core/jni/Android.mk @@ -6,6 +6,7 @@ LOCAL_CFLAGS += -Wno-unused-parameter LOCAL_SRC_FILES += \ $(LOCAL_REL_DIR)/com_android_server_AlarmManagerService.cpp \ + $(LOCAL_REL_DIR)/com_android_server_am_BatteryStatsService.cpp \ $(LOCAL_REL_DIR)/com_android_server_AssetAtlasService.cpp \ $(LOCAL_REL_DIR)/com_android_server_ConsumerIrService.cpp \ $(LOCAL_REL_DIR)/com_android_server_dreams_McuHal.cpp \ diff --git a/services/core/jni/com_android_server_am_BatteryStatsService.cpp b/services/core/jni/com_android_server_am_BatteryStatsService.cpp new file mode 100644 index 0000000..22cc519 --- /dev/null +++ b/services/core/jni/com_android_server_am_BatteryStatsService.cpp @@ -0,0 +1,196 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "BatteryStatsService" +//#define LOG_NDEBUG 0 + +#include <android_runtime/AndroidRuntime.h> +#include <jni.h> + +#include <ScopedLocalRef.h> +#include <ScopedPrimitiveArray.h> + +#include <cutils/log.h> +#include <utils/misc.h> +#include <utils/Log.h> +#include <hardware/hardware.h> +#include <suspend/autosuspend.h> + +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <semaphore.h> +#include <stddef.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +namespace android +{ + +#define LAST_RESUME_REASON "/sys/kernel/wakeup_reasons/last_resume_reason" +#define MAX_REASON_SIZE 512 + +static bool wakeup_init = false; +static sem_t wakeup_sem; + +static void wakeup_callback(void) +{ + ALOGV("In wakeup_callback"); + int ret = sem_post(&wakeup_sem); + if (ret < 0) { + char buf[80]; + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error posting wakeup sem: %s\n", buf); + } +} + +static jint nativeWaitWakeup(JNIEnv *env, jobject clazz, jintArray outIrqs, + jobjectArray outReasons) +{ + bool first_time = false; + + if (outIrqs == NULL || outReasons == NULL) { + jniThrowException(env, "java/lang/NullPointerException", "null argument"); + return -1; + } + + // Register our wakeup callback if not yet done. + if (!wakeup_init) { + wakeup_init = true; + ALOGV("Creating semaphore..."); + int ret = sem_init(&wakeup_sem, 0, 0); + if (ret < 0) { + char buf[80]; + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error creating semaphore: %s\n", buf); + jniThrowException(env, "java/lang/IllegalStateException", buf); + return -1; + } + ALOGV("Registering callback..."); + set_wakeup_callback(&wakeup_callback); + // First time through, we will just drain the current wakeup reasons. + first_time = true; + } else { + // On following calls, we need to wait for wakeup. + ALOGV("Waiting for wakeup..."); + int ret = sem_wait(&wakeup_sem); + if (ret < 0) { + char buf[80]; + strerror_r(errno, buf, sizeof(buf)); + ALOGE("Error waiting on semaphore: %s\n", buf); + // Return 0 here to let it continue looping but not return results. + return 0; + } + } + + FILE *fp = fopen(LAST_RESUME_REASON, "r"); + if (fp == NULL) { + ALOGE("Failed to open %s", LAST_RESUME_REASON); + return -1; + } + + int numOut = env->GetArrayLength(outIrqs); + ScopedIntArrayRW irqs(env, outIrqs); + + ALOGV("Reading up to %d wakeup reasons", numOut); + + char mergedreason[MAX_REASON_SIZE]; + char* mergedreasonpos = mergedreason; + int remainreasonlen = MAX_REASON_SIZE; + int firstirq = 0; + char reasonline[128]; + int i = 0; + while (fgets(reasonline, sizeof(reasonline), fp) != NULL && i < numOut) { + char* pos = reasonline; + char* endPos; + // First field is the index. + int irq = (int)strtol(pos, &endPos, 10); + if (pos == endPos) { + // Ooops. + ALOGE("Bad reason line: %s", reasonline); + continue; + } + pos = endPos; + // Skip whitespace; rest of the buffer is the reason string. + while (*pos == ' ') { + pos++; + } + // Chop newline at end. + char* endpos = pos; + while (*endpos != 0) { + if (*endpos == '\n') { + *endpos = 0; + break; + } + endpos++; + } + if (i == 0) { + firstirq = irq; + } else { + int len = snprintf(mergedreasonpos, remainreasonlen, ":%d", irq); + if (len >= 0 && len < remainreasonlen) { + mergedreasonpos += len; + remainreasonlen -= len; + } + } + int len = snprintf(mergedreasonpos, remainreasonlen, i == 0 ? "%s" : ":%s", pos); + if (len >= 0 && len < remainreasonlen) { + mergedreasonpos += len; + remainreasonlen -= len; + } + // For now it is better to combine all of these in to one entry in the + // battery history. In the future, it might be nice to figure out a way + // to efficiently store multiple lines as a single entry in the history. + //irqs[i] = irq; + //ScopedLocalRef<jstring> reasonString(env, env->NewStringUTF(pos)); + //env->SetObjectArrayElement(outReasons, i, reasonString.get()); + //ALOGV("Wakeup reason #%d: irw %d reason %s", i, irq, pos); + i++; + } + + ALOGV("Got %d reasons", i); + if (first_time) { + i = 0; + } + if (i > 0) { + irqs[0] = firstirq; + *mergedreasonpos = 0; + ScopedLocalRef<jstring> reasonString(env, env->NewStringUTF(mergedreason)); + env->SetObjectArrayElement(outReasons, 0, reasonString.get()); + i = 1; + } + + if (fclose(fp) != 0) { + ALOGE("Failed to close %s", LAST_RESUME_REASON); + return -1; + } + + return first_time ? 0 : i; +} + +static JNINativeMethod method_table[] = { + { "nativeWaitWakeup", "([I[Ljava/lang/String;)I", (void*)nativeWaitWakeup }, +}; + +int register_android_server_BatteryStatsService(JNIEnv *env) +{ + return jniRegisterNativeMethods(env, "com/android/server/am/BatteryStatsService", + method_table, NELEM(method_table)); +} + +}; diff --git a/services/core/jni/onload.cpp b/services/core/jni/onload.cpp index 00986d5..ac6585b 100644 --- a/services/core/jni/onload.cpp +++ b/services/core/jni/onload.cpp @@ -21,6 +21,7 @@ namespace android { int register_android_server_AlarmManagerService(JNIEnv* env); +int register_android_server_BatteryStatsService(JNIEnv* env); int register_android_server_ConsumerIrService(JNIEnv *env); int register_android_server_InputApplicationHandle(JNIEnv* env); int register_android_server_InputWindowHandle(JNIEnv* env); @@ -69,6 +70,7 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) register_android_server_AssetAtlasService(env); register_android_server_ConsumerIrService(env); register_android_server_dreams_McuHal(env); + register_android_server_BatteryStatsService(env); return JNI_VERSION_1_4; } |