summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorDianne Hackborn <hackbod@google.com>2014-03-02 19:08:15 -0800
committerDianne Hackborn <hackbod@google.com>2014-03-07 15:18:45 -0800
commitc51cf03cf2458c8c137f60c7379f2cccf681d16f (patch)
tree61e1f59bcd3c181d0725d0e2e6ee4f8115f89ee6 /services
parent71c8fb92a151f2f64ffbaf551c072179bc46c0da (diff)
downloadframeworks_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.java5
-rw-r--r--services/core/java/com/android/server/am/BatteryStatsService.java72
-rw-r--r--services/core/jni/Android.mk1
-rw-r--r--services/core/jni/com_android_server_am_BatteryStatsService.cpp196
-rw-r--r--services/core/jni/onload.cpp2
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;
}