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 | |
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
-rw-r--r-- | core/java/android/os/BatteryStats.java | 325 | ||||
-rw-r--r-- | core/java/com/android/internal/os/BatteryStatsImpl.java | 78 | ||||
-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 |
7 files changed, 527 insertions, 152 deletions
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 @@ public abstract class BatteryStats implements Parcelable { // 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 @@ public abstract class BatteryStats implements Parcelable { // 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 @@ public abstract class BatteryStats implements Parcelable { } 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 @@ public abstract class BatteryStats implements Parcelable { } 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 @@ public abstract class BatteryStats implements Parcelable { batteryVoltage = 0; states = 0; wakelockTag = null; + wakeReasonTag = null; eventCode = EVENT_NONE; eventTag = null; } @@ -719,6 +736,12 @@ public abstract class BatteryStats implements Parcelable { } 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 @@ public abstract class BatteryStats implements Parcelable { 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 @@ public abstract class BatteryStats implements Parcelable { 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 @@ public abstract class BatteryStats implements Parcelable { 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 abstract class BatteryStats implements Parcelable { 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 @@ public abstract class BatteryStats implements Parcelable { } 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 @@ public abstract class BatteryStats implements Parcelable { 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); + 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(); } - 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 @@ public abstract class BatteryStats implements Parcelable { 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 @@ public abstract class BatteryStats implements Parcelable { } } - if (historyOnly) { + if (filtering && (flags&(DUMP_UNPLUGGED_ONLY|DUMP_CHARGED_ONLY)) == 0) { return; } @@ -3076,11 +3160,10 @@ public abstract class BatteryStats implements Parcelable { } } } - 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { + " 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 @@ public final class BatteryStatsImpl extends BatteryStats { } 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 final class BatteryStatsImpl extends BatteryStats { } } + 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ public final class BatteryStatsImpl extends BatteryStats { 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 @@ 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; } |