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
diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java
index cc4bb51..0da77ea 100644
--- a/core/java/android/os/BatteryStats.java
+++ b/core/java/android/os/BatteryStats.java
@@ -578,6 +578,9 @@
// The wake lock that was acquired at this point.
public HistoryTag wakelockTag;
+ // Kernel wakeup reason at this point.
+ public HistoryTag wakeReasonTag;
+
public static final int EVENT_FLAG_START = 0x8000;
public static final int EVENT_FLAG_FINISH = 0x4000;
@@ -612,6 +615,7 @@
// Pre-allocated objects.
public final HistoryTag localWakelockTag = new HistoryTag();
+ public final HistoryTag localWakeReasonTag = new HistoryTag();
public final HistoryTag localEventTag = new HistoryTag();
public HistoryItem() {
@@ -645,6 +649,12 @@
} else {
dest.writeInt(0);
}
+ if (wakeReasonTag != null) {
+ dest.writeInt(1);
+ wakeReasonTag.writeToParcel(dest, flags);
+ } else {
+ dest.writeInt(0);
+ }
dest.writeInt(eventCode);
if (eventCode != EVENT_NONE) {
dest.writeInt(eventCode);
@@ -670,6 +680,12 @@
} else {
wakelockTag = null;
}
+ if (src.readInt() != 0) {
+ wakeReasonTag = localWakeReasonTag;
+ wakeReasonTag.readFromParcel(src);
+ } else {
+ wakeReasonTag = null;
+ }
eventCode = src.readInt();
if (eventCode != EVENT_NONE) {
eventTag = localEventTag;
@@ -689,6 +705,7 @@
batteryVoltage = 0;
states = 0;
wakelockTag = null;
+ wakeReasonTag = null;
eventCode = EVENT_NONE;
eventTag = null;
}
@@ -719,6 +736,12 @@
} else {
wakelockTag = null;
}
+ if (o.wakeReasonTag != null) {
+ wakeReasonTag = localWakeReasonTag;
+ wakeReasonTag.setTo(o.wakeReasonTag);
+ } else {
+ wakeReasonTag = null;
+ }
eventCode = o.eventCode;
if (o.eventTag != null) {
eventTag = localEventTag;
@@ -750,6 +773,14 @@
return false;
}
}
+ if (wakeReasonTag != o.wakeReasonTag) {
+ if (wakeReasonTag == null || o.wakeReasonTag == null) {
+ return false;
+ }
+ if (!wakeReasonTag.equals(o.wakeReasonTag)) {
+ return false;
+ }
+ }
if (eventTag != o.eventTag) {
if (eventTag == null || o.eventTag == null) {
return false;
@@ -1916,51 +1947,6 @@
long fullWakeLockTimeTotalMicros = 0;
long partialWakeLockTimeTotalMicros = 0;
- final Comparator<TimerEntry> timerComparator = new Comparator<TimerEntry>() {
- @Override
- public int compare(TimerEntry lhs, TimerEntry rhs) {
- long lhsTime = lhs.mTime;
- long rhsTime = rhs.mTime;
- if (lhsTime < rhsTime) {
- return 1;
- }
- if (lhsTime > rhsTime) {
- return -1;
- }
- return 0;
- }
- };
-
- if (reqUid < 0) {
- Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
- if (kernelWakelocks.size() > 0) {
- final ArrayList<TimerEntry> timers = new ArrayList<TimerEntry>();
- for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
- BatteryStats.Timer timer = ent.getValue();
- long totalTimeMillis = computeWakeLock(timer, rawRealtime, which);
- if (totalTimeMillis > 0) {
- timers.add(new TimerEntry(ent.getKey(), 0, timer, totalTimeMillis));
- }
- }
- Collections.sort(timers, timerComparator);
- for (int i=0; i<timers.size(); i++) {
- TimerEntry timer = timers.get(i);
- String linePrefix = ": ";
- sb.setLength(0);
- sb.append(prefix);
- sb.append(" Kernel Wake lock ");
- sb.append(timer.mName);
- linePrefix = printWakeLock(sb, timer.mTimer, rawRealtime, null,
- which, linePrefix);
- if (!linePrefix.equals(": ")) {
- sb.append(" realtime");
- // Only print out wake locks that were held
- pw.println(sb.toString());
- }
- }
- }
- }
-
final ArrayList<TimerEntry> timers = new ArrayList<TimerEntry>();
for (int iu = 0; iu < NU; iu++) {
@@ -2292,6 +2278,55 @@
pw.println();
}
+ final Comparator<TimerEntry> timerComparator = new Comparator<TimerEntry>() {
+ @Override
+ public int compare(TimerEntry lhs, TimerEntry rhs) {
+ long lhsTime = lhs.mTime;
+ long rhsTime = rhs.mTime;
+ if (lhsTime < rhsTime) {
+ return 1;
+ }
+ if (lhsTime > rhsTime) {
+ return -1;
+ }
+ return 0;
+ }
+ };
+
+ if (reqUid < 0) {
+ Map<String, ? extends BatteryStats.Timer> kernelWakelocks = getKernelWakelockStats();
+ if (kernelWakelocks.size() > 0) {
+ final ArrayList<TimerEntry> ktimers = new ArrayList<TimerEntry>();
+ for (Map.Entry<String, ? extends BatteryStats.Timer> ent : kernelWakelocks.entrySet()) {
+ BatteryStats.Timer timer = ent.getValue();
+ long totalTimeMillis = computeWakeLock(timer, rawRealtime, which);
+ if (totalTimeMillis > 0) {
+ ktimers.add(new TimerEntry(ent.getKey(), 0, timer, totalTimeMillis));
+ }
+ }
+ if (ktimers.size() > 0) {
+ Collections.sort(ktimers, timerComparator);
+ pw.print(prefix); pw.println(" All kernel wake locks:");
+ for (int i=0; i<ktimers.size(); i++) {
+ TimerEntry timer = ktimers.get(i);
+ String linePrefix = ": ";
+ sb.setLength(0);
+ sb.append(prefix);
+ sb.append(" Kernel Wake lock ");
+ sb.append(timer.mName);
+ linePrefix = printWakeLock(sb, timer.mTimer, rawRealtime, null,
+ which, linePrefix);
+ if (!linePrefix.equals(": ")) {
+ sb.append(" realtime");
+ // Only print out wake locks that were held
+ pw.println(sb.toString());
+ }
+ }
+ pw.println();
+ }
+ }
+ }
+
if (timers.size() > 0) {
Collections.sort(timers, timerComparator);
pw.print(prefix); pw.println(" All partial wake locks:");
@@ -2727,14 +2762,22 @@
public void printNextItem(PrintWriter pw, HistoryItem rec, long now, boolean checkin) {
if (!checkin) {
pw.print(" ");
- TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
+ if (now >= 0) {
+ TimeUtils.formatDuration(rec.time-now, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
+ } else {
+ TimeUtils.formatDuration(rec.time, pw, TimeUtils.HUNDRED_DAY_FIELD_LEN);
+ }
pw.print(" (");
pw.print(rec.numReadInts);
pw.print(") ");
} else {
if (lastTime < 0) {
- pw.print("@");
- pw.print(rec.time-now);
+ if (now >= 0) {
+ pw.print("@");
+ pw.print(rec.time-now);
+ } else {
+ pw.print(rec.time);
+ }
} else {
pw.print(rec.time-lastTime);
}
@@ -2858,6 +2901,18 @@
}
printBitDescriptions(pw, oldState, rec.states, rec.wakelockTag,
HISTORY_STATE_DESCRIPTIONS, !checkin);
+ if (rec.wakeReasonTag != null) {
+ if (checkin) {
+ pw.print(",Wr=");
+ pw.print(rec.wakeReasonTag.poolIdx);
+ } else {
+ pw.print(" wake_reason=");
+ pw.print(rec.wakeReasonTag.uid);
+ pw.print(":\"");
+ pw.print(rec.wakeReasonTag.string);
+ pw.print("\"");
+ }
+ }
if (rec.eventCode != HistoryItem.EVENT_NONE) {
pw.print(checkin ? "," : " ");
if ((rec.eventCode&HistoryItem.EVENT_FLAG_START) != 0) {
@@ -2918,108 +2973,130 @@
pw.print(suffix);
}
+ public static final int DUMP_UNPLUGGED_ONLY = 1<<0;
+ public static final int DUMP_CHARGED_ONLY = 1<<1;
+ public static final int DUMP_HISTORY_ONLY = 1<<2;
+ public static final int DUMP_INCLUDE_HISTORY = 1<<3;
+
/**
* Dumps a human-readable summary of the battery statistics to the given PrintWriter.
*
* @param pw a Printer to receive the dump output.
*/
@SuppressWarnings("unused")
- public void dumpLocked(Context context, PrintWriter pw, boolean isUnpluggedOnly, int reqUid,
- boolean historyOnly) {
+ public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
prepareForDumpLocked();
- long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
+ final boolean filtering =
+ (flags&(DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) != 0;
- final HistoryItem rec = new HistoryItem();
- final long historyTotalSize = getHistoryTotalSize();
- final long historyUsedSize = getHistoryUsedSize();
- if (startIteratingHistoryLocked()) {
- try {
- pw.print("Battery History (");
- pw.print((100*historyUsedSize)/historyTotalSize);
- pw.print("% used, ");
- printSizeValue(pw, historyUsedSize);
- pw.print(" used of ");
- printSizeValue(pw, historyTotalSize);
- pw.print(", ");
- pw.print(getHistoryStringPoolSize());
- pw.print(" strings using ");
- printSizeValue(pw, getHistoryStringPoolBytes());
- pw.println("):");
- HistoryPrinter hprinter = new HistoryPrinter();
- while (getNextHistoryLocked(rec)) {
- hprinter.printNextItem(pw, rec, now, false);
+ if ((flags&DUMP_HISTORY_ONLY) != 0 || !filtering) {
+ long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
+
+ final HistoryItem rec = new HistoryItem();
+ final long historyTotalSize = getHistoryTotalSize();
+ final long historyUsedSize = getHistoryUsedSize();
+ if (startIteratingHistoryLocked()) {
+ try {
+ pw.print("Battery History (");
+ pw.print((100*historyUsedSize)/historyTotalSize);
+ pw.print("% used, ");
+ printSizeValue(pw, historyUsedSize);
+ pw.print(" used of ");
+ printSizeValue(pw, historyTotalSize);
+ pw.print(", ");
+ pw.print(getHistoryStringPoolSize());
+ pw.print(" strings using ");
+ printSizeValue(pw, getHistoryStringPoolBytes());
+ pw.println("):");
+ HistoryPrinter hprinter = new HistoryPrinter();
+ long lastTime = -1;
+ while (getNextHistoryLocked(rec)) {
+ lastTime = rec.time;
+ if (rec.time >= histStart) {
+ hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, false);
+ }
+ }
+ if (histStart >= 0) {
+ pw.print(" NEXT: "); pw.println(lastTime+1);
+ }
+ pw.println();
+ } finally {
+ finishIteratingHistoryLocked();
}
- pw.println();
- } finally {
- finishIteratingHistoryLocked();
+ }
+
+ if (startIteratingOldHistoryLocked()) {
+ try {
+ pw.println("Old battery History:");
+ HistoryPrinter hprinter = new HistoryPrinter();
+ while (getNextOldHistoryLocked(rec)) {
+ hprinter.printNextItem(pw, rec, now, false);
+ }
+ pw.println();
+ } finally {
+ finishIteratingOldHistoryLocked();
+ }
}
}
- if (startIteratingOldHistoryLocked()) {
- try {
- pw.println("Old battery History:");
- HistoryPrinter hprinter = new HistoryPrinter();
- while (getNextOldHistoryLocked(rec)) {
- hprinter.printNextItem(pw, rec, now, false);
- }
- pw.println();
- } finally {
- finishIteratingOldHistoryLocked();
- }
- }
-
- if (historyOnly) {
+ if (filtering && (flags&(DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) == 0) {
return;
}
- SparseArray<? extends Uid> uidStats = getUidStats();
- final int NU = uidStats.size();
- boolean didPid = false;
- long nowRealtime = SystemClock.elapsedRealtime();
- for (int i=0; i<NU; i++) {
- Uid uid = uidStats.valueAt(i);
- SparseArray<? extends Uid.Pid> pids = uid.getPidStats();
- if (pids != null) {
- for (int j=0; j<pids.size(); j++) {
- Uid.Pid pid = pids.valueAt(j);
- if (!didPid) {
- pw.println("Per-PID Stats:");
- didPid = true;
+ if (!filtering) {
+ SparseArray<? extends Uid> uidStats = getUidStats();
+ final int NU = uidStats.size();
+ boolean didPid = false;
+ long nowRealtime = SystemClock.elapsedRealtime();
+ for (int i=0; i<NU; i++) {
+ Uid uid = uidStats.valueAt(i);
+ SparseArray<? extends Uid.Pid> pids = uid.getPidStats();
+ if (pids != null) {
+ for (int j=0; j<pids.size(); j++) {
+ Uid.Pid pid = pids.valueAt(j);
+ if (!didPid) {
+ pw.println("Per-PID Stats:");
+ didPid = true;
+ }
+ long time = pid.mWakeSum + (pid.mWakeStart != 0
+ ? (nowRealtime - pid.mWakeStart) : 0);
+ pw.print(" PID "); pw.print(pids.keyAt(j));
+ pw.print(" wake time: ");
+ TimeUtils.formatDuration(time, pw);
+ pw.println("");
}
- long time = pid.mWakeSum + (pid.mWakeStart != 0
- ? (nowRealtime - pid.mWakeStart) : 0);
- pw.print(" PID "); pw.print(pids.keyAt(j));
- pw.print(" wake time: ");
- TimeUtils.formatDuration(time, pw);
- pw.println("");
}
}
- }
- if (didPid) {
- pw.println("");
+ if (didPid) {
+ pw.println("");
+ }
}
- if (!isUnpluggedOnly) {
+ if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) {
pw.println("Statistics since last charge:");
pw.println(" System starts: " + getStartCount()
+ ", currently on battery: " + getIsOnBattery());
dumpLocked(context, pw, "", STATS_SINCE_CHARGED, reqUid);
pw.println("");
}
- pw.println("Statistics since last unplugged:");
- dumpLocked(context, pw, "", STATS_SINCE_UNPLUGGED, reqUid);
+ if (!filtering || (flags&DUMP_UNPLUGGED_ONLY) != 0) {
+ pw.println("Statistics since last unplugged:");
+ dumpLocked(context, pw, "", STATS_SINCE_UNPLUGGED, reqUid);
+ }
}
@SuppressWarnings("unused")
- public void dumpCheckinLocked(Context context,
- PrintWriter pw, List<ApplicationInfo> apps, boolean isUnpluggedOnly,
- boolean includeHistory, boolean historyOnly) {
+ public void dumpCheckinLocked(Context context, PrintWriter pw,
+ List<ApplicationInfo> apps, int flags, long histStart) {
prepareForDumpLocked();
long now = getHistoryBaseTime() + SystemClock.elapsedRealtime();
- if (includeHistory || historyOnly) {
+ final boolean filtering =
+ (flags&(DUMP_HISTORY_ONLY|DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) != 0;
+
+ if ((flags&DUMP_INCLUDE_HISTORY) != 0 || (flags&DUMP_HISTORY_ONLY) != 0) {
final HistoryItem rec = new HistoryItem();
if (startIteratingHistoryLocked()) {
try {
@@ -3034,10 +3111,17 @@
pw.println();
}
HistoryPrinter hprinter = new HistoryPrinter();
+ long lastTime = -1;
while (getNextHistoryLocked(rec)) {
- pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
- pw.print(HISTORY_DATA); pw.print(',');
- hprinter.printNextItem(pw, rec, now, true);
+ lastTime = rec.time;
+ if (rec.time >= histStart) {
+ pw.print(BATTERY_STATS_CHECKIN_VERSION); pw.print(',');
+ pw.print(HISTORY_DATA); pw.print(',');
+ hprinter.printNextItem(pw, rec, histStart >= 0 ? -1 : now, true);
+ }
+ }
+ if (histStart >= 0) {
+ pw.print("NEXT: "); pw.println(lastTime+1);
}
} finally {
finishIteratingHistoryLocked();
@@ -3045,7 +3129,7 @@
}
}
- if (historyOnly) {
+ if (filtering && (flags&(DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) == 0) {
return;
}
@@ -3076,11 +3160,10 @@
}
}
}
- if (isUnpluggedOnly) {
- dumpCheckinLocked(context, pw, STATS_SINCE_UNPLUGGED, -1);
- }
- else {
+ if (!filtering || (flags&DUMP_CHARGED_ONLY) != 0) {
dumpCheckinLocked(context, pw, STATS_SINCE_CHARGED, -1);
+ }
+ if (!filtering || (flags&DUMP_UNPLUGGED_ONLY) != 0) {
dumpCheckinLocked(context, pw, STATS_SINCE_UNPLUGGED, -1);
}
}
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index fd93604..db21906 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -87,7 +87,7 @@
private static final int MAGIC = 0xBA757475; // 'BATSTATS'
// Current on-disk Parcel version
- private static final int VERSION = 97 + (USE_OLD_HISTORY ? 1000 : 0);
+ private static final int VERSION = 98 + (USE_OLD_HISTORY ? 1000 : 0);
// Maximum number of items we will record in the history.
private static final int MAX_HISTORY_ITEMS = 2000;
@@ -1712,7 +1712,7 @@
static final int DELTA_STATE_FLAG = 0x00100000;
// Flag in delta int: a new full state2 int follows.
static final int DELTA_STATE2_FLAG = 0x00200000;
- // Flag in delta int: contains a wakelock tag.
+ // Flag in delta int: contains a wakelock or wakeReason tag.
static final int DELTA_WAKELOCK_FLAG = 0x00400000;
// Flag in delta int: contains an event description.
static final int DELTA_EVENT_FLAG = 0x00800000;
@@ -1759,7 +1759,7 @@
if (stateIntChanged) {
firstToken |= DELTA_STATE_FLAG;
}
- if (cur.wakelockTag != null) {
+ if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
firstToken |= DELTA_WAKELOCK_FLAG;
}
if (cur.eventCode != HistoryItem.EVENT_NONE) {
@@ -1795,11 +1795,24 @@
+ " batteryPlugType=" + cur.batteryPlugType
+ " states=0x" + Integer.toHexString(cur.states));
}
- if (cur.wakelockTag != null) {
- int index = writeHistoryTag(cur.wakelockTag);
- dest.writeInt(index);
- if (DEBUG) Slog.i(TAG, "WRITE DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
- + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
+ if (cur.wakelockTag != null || cur.wakeReasonTag != null) {
+ int wakeLockIndex;
+ int wakeReasonIndex;
+ if (cur.wakelockTag != null) {
+ wakeLockIndex = writeHistoryTag(cur.wakelockTag);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
+ + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
+ } else {
+ wakeLockIndex = 0xffff;
+ }
+ if (cur.wakeReasonTag != null) {
+ wakeReasonIndex = writeHistoryTag(cur.wakeReasonTag);
+ if (DEBUG) Slog.i(TAG, "WRITE DELTA: wakeReasonTag=#" + cur.wakeReasonTag.poolIdx
+ + " " + cur.wakeReasonTag.uid + ":" + cur.wakeReasonTag.string);
+ } else {
+ wakeReasonIndex = 0xffff;
+ }
+ dest.writeInt((wakeReasonIndex<<16) | wakeLockIndex);
}
if (cur.eventCode != HistoryItem.EVENT_NONE) {
int index = writeHistoryTag(cur.eventTag);
@@ -1905,14 +1918,29 @@
}
if ((firstToken&DELTA_WAKELOCK_FLAG) != 0) {
- cur.wakelockTag = cur.localWakelockTag;
- int index = src.readInt();
- readHistoryTag(index, cur.wakelockTag);
+ int indexes = src.readInt();
+ int wakeLockIndex = indexes&0xffff;
+ int wakeReasonIndex = (indexes>>16)&0xffff;
+ if (wakeLockIndex != 0xffff) {
+ cur.wakelockTag = cur.localWakelockTag;
+ readHistoryTag(wakeLockIndex, cur.wakelockTag);
+ if (DEBUG) Slog.i(TAG, "READ DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
+ + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
+ } else {
+ cur.wakelockTag = null;
+ }
+ if (wakeReasonIndex != 0xffff) {
+ cur.wakeReasonTag = cur.localWakeReasonTag;
+ readHistoryTag(wakeReasonIndex, cur.wakeReasonTag);
+ if (DEBUG) Slog.i(TAG, "READ DELTA: wakeReasonTag=#" + cur.wakeReasonTag.poolIdx
+ + " " + cur.wakeReasonTag.uid + ":" + cur.wakeReasonTag.string);
+ } else {
+ cur.wakeReasonTag = null;
+ }
cur.numReadInts += 1;
- if (DEBUG) Slog.i(TAG, "READ DELTA: wakelockTag=#" + cur.wakelockTag.poolIdx
- + " " + cur.wakelockTag.uid + ":" + cur.wakelockTag.string);
} else {
cur.wakelockTag = null;
+ cur.wakeReasonTag = null;
}
if ((firstToken&DELTA_EVENT_FLAG) != 0) {
@@ -1944,6 +1972,7 @@
if (mHistoryBufferLastPos >= 0 && mHistoryLastWritten.cmd == HistoryItem.CMD_UPDATE
&& timeDiff < 1000 && (diffStates&lastDiffStates) == 0
&& (mHistoryLastWritten.wakelockTag == null || mHistoryCur.wakelockTag == null)
+ && (mHistoryLastWritten.wakeReasonTag == null || mHistoryCur.wakeReasonTag == null)
&& (mHistoryLastWritten.eventCode == HistoryItem.EVENT_NONE
|| mHistoryCur.eventCode == HistoryItem.EVENT_NONE)
&& mHistoryLastWritten.batteryLevel == mHistoryCur.batteryLevel
@@ -1968,6 +1997,13 @@
mHistoryCur.wakelockTag = mHistoryCur.localWakelockTag;
mHistoryCur.wakelockTag.setTo(mHistoryLastWritten.wakelockTag);
}
+ // If the last written history had a wake reason tag, we need to retain it.
+ // Note that the condition above made sure that we aren't in a case where
+ // both it and the current history item have a wakelock tag.
+ if (mHistoryLastWritten.wakeReasonTag != null) {
+ mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
+ mHistoryCur.wakeReasonTag.setTo(mHistoryLastWritten.wakeReasonTag);
+ }
// If the last written history had an event, we need to retain it.
// Note that the condition above made sure that we aren't in a case where
// both it and the current history item have an event.
@@ -2016,6 +2052,7 @@
writeHistoryDelta(mHistoryBuffer, mHistoryLastWritten, mHistoryLastLastWritten);
mLastHistoryTime = curTime;
mHistoryCur.wakelockTag = null;
+ mHistoryCur.wakeReasonTag = null;
mHistoryCur.eventCode = HistoryItem.EVENT_NONE;
mHistoryCur.eventTag = null;
if (DEBUG_HISTORY) Slog.i(TAG, "Writing history buffer: was " + mHistoryBufferLastPos
@@ -2304,6 +2341,16 @@
}
}
+ public void noteWakeupReasonLocked(int irq, String reason) {
+ final long elapsedRealtime = SystemClock.elapsedRealtime();
+ if (DEBUG_HISTORY) Slog.v(TAG, "Wakeup reason irq #" + irq + "\"" + reason +"\": "
+ + Integer.toHexString(mHistoryCur.states));
+ mHistoryCur.wakeReasonTag = mHistoryCur.localWakeReasonTag;
+ mHistoryCur.wakeReasonTag.string = reason;
+ mHistoryCur.wakeReasonTag.uid = irq;
+ addHistoryRecordLocked(elapsedRealtime);
+ }
+
public int startAddingCpuLocked() {
mHandler.removeMessages(MSG_UPDATE_WAKELOCKS);
@@ -7219,8 +7266,7 @@
pullPendingStateUpdatesLocked();
}
- public void dumpLocked(Context context, PrintWriter pw, boolean isUnpluggedOnly, int reqUid,
- boolean historyOnly) {
+ public void dumpLocked(Context context, PrintWriter pw, int flags, int reqUid, long histStart) {
if (DEBUG) {
pw.println("mOnBatteryTimeBase:");
mOnBatteryTimeBase.dump(pw, " ");
@@ -7264,6 +7310,6 @@
mBluetoothStateTimer[i].logState(pr, " ");
}
}
- super.dumpLocked(context, pw, isUnpluggedOnly, reqUid, historyOnly);
+ super.dumpLocked(context, pw, flags, reqUid, histStart);
}
}
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 @@
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 @@
* battery life.
*/
public final class BatteryStatsService extends IBatteryStats.Stub {
+ static final String TAG = "BatteryStatsService";
+
static IBatteryStats sService;
final BatteryStatsImpl mStats;
@@ -66,7 +68,8 @@
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 @@
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 @@
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 @@
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_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 @@
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;
}