From aa4594aad251bdde8306180f07983dde173bc9e1 Mon Sep 17 00:00:00 2001 From: Bookatz Date: Fri, 24 Mar 2017 12:39:56 -0700 Subject: [PATCH] Collect background stats: scheduled jobs Added background times and counts for an app's scheduled job usage. Changes DualTimer to be a subclass of Timer so that background timers can be easily accessed from the main timer (which is convenient for jobs, whose ArrayMap of Timers is directly accessed outside of BatteryStatsImpl). Bug: 35669746 Test: runtest -x frameworks/base/core/tests/coretests/src/com/android/internal/os/BatteryStatsTests.java Change-Id: Ic1d85db34346ebda94ed39f134fc0794a5877815 (cherry picked from commit d253f537a0cc15851fb0308f3ea287e6bd93dfe6) --- core/java/android/os/BatteryStats.java | 29 +++- .../android/internal/os/BatteryStatsImpl.java | 137 ++++++++---------- .../os/BatteryStatsBackgroundStatsTest.java | 62 ++++++++ 3 files changed, 147 insertions(+), 81 deletions(-) diff --git a/core/java/android/os/BatteryStats.java b/core/java/android/os/BatteryStats.java index dd11f68ffe509..8349510a14620 100644 --- a/core/java/android/os/BatteryStats.java +++ b/core/java/android/os/BatteryStats.java @@ -182,7 +182,7 @@ public abstract class BatteryStats implements Parcelable { * New in version 19: * - Wakelock data (wl) gets current and max times. * New in version 20: - * - Sensor, BluetoothScan, WifiScan get background timers and counter. + * - Background timers and counters for: Sensor, BluetoothScan, WifiScan, Jobs. */ static final String CHECKIN_VERSION = "20"; @@ -391,6 +391,16 @@ public abstract class BatteryStats implements Parcelable { return -1; } + /** + * Returns the secondary Timer held by the Timer, if one exists. This secondary timer may be + * used, for example, for tracking background usage. Secondary timers are never pooled. + * + * Not all Timer subclasses have a secondary timer; those that don't return null. + */ + public Timer getSubTimer() { + return null; + } + /** * Returns whether the timer is currently running. Some types of timers * (e.g. BatchTimers) don't know whether the event is currently active, @@ -3393,9 +3403,13 @@ public abstract class BatteryStats implements Parcelable { // Convert from microseconds to milliseconds with rounding final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000; final int count = timer.getCountLocked(which); + final Timer bgTimer = timer.getSubTimer(); + final long bgTime = bgTimer != null ? + (bgTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000 : -1; + final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : -1; if (totalTime != 0) { dumpLine(pw, uid, category, JOB_DATA, "\"" + jobs.keyAt(ij) + "\"", - totalTime, count); + totalTime, count, bgTime, bgCount); } } @@ -4616,6 +4630,10 @@ public abstract class BatteryStats implements Parcelable { // Convert from microseconds to milliseconds with rounding final long totalTime = (timer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000; final int count = timer.getCountLocked(which); + final Timer bgTimer = timer.getSubTimer(); + final long bgTime = bgTimer != null ? + (bgTimer.getTotalTimeLocked(rawRealtime, which) + 500) / 1000 : -1; + final int bgCount = bgTimer != null ? bgTimer.getCountLocked(which) : -1; sb.setLength(0); sb.append(prefix); sb.append(" Job "); @@ -4626,6 +4644,13 @@ public abstract class BatteryStats implements Parcelable { sb.append("realtime ("); sb.append(count); sb.append(" times)"); + if (bgTime > 0) { + sb.append(", "); + formatTimeMs(sb, bgTime); + sb.append("background ("); + sb.append(bgCount); + sb.append(" times)"); + } } else { sb.append("(not used)"); } diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java index b2636578d6a28..d19ffad37c958 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -114,7 +114,7 @@ public class BatteryStatsImpl extends BatteryStats { private static final int MAGIC = 0xBA757475; // 'BATSTATS' // Current on-disk Parcel version - private static final int VERSION = 152 + (USE_OLD_HISTORY ? 1000 : 0); + private static final int VERSION = 153 + (USE_OLD_HISTORY ? 1000 : 0); // Maximum number of items we will record in the history. private static final int MAX_HISTORY_ITEMS = 2000; @@ -2006,107 +2006,92 @@ public class BatteryStatsImpl extends BatteryStats { * State for keeping track of two DurationTimers with different TimeBases, presumably where one * TimeBase is effectively a subset of the other. */ - public static class DualTimer { - // mMainTimer typically tracks the total time. May be pooled (but since it's a durationTimer, - // it also has the unpooled getTotalDurationMsLocked() for STATS_SINCE_CHARGED). - private final DurationTimer mMainTimer; + public static class DualTimer extends DurationTimer { + // This class both is a DurationTimer and also holds a second DurationTimer. + // The main timer (this) typically tracks the total time. It may be pooled (but since it's a + // durationTimer, it also has the unpooled getTotalDurationMsLocked() for + // STATS_SINCE_CHARGED). // mSubTimer typically tracks only part of the total time, such as background time, as // determined by a subTimeBase. It is NOT pooled. private final DurationTimer mSubTimer; /** - * Creates a DualTimer to hold a mMainTimer and a mSubTimer. - * The mMainTimer is based on the given timeBase and timerPool. + * Creates a DualTimer to hold a main timer (this) and a mSubTimer. + * The main timer (this) is based on the given timeBase and timerPool. * The mSubTimer is based on the given subTimeBase. The mSubTimer is not pooled, even if - * the mMainTimer is. + * the main timer is. */ public DualTimer(Clocks clocks, Uid uid, int type, ArrayList timerPool, TimeBase timeBase, TimeBase subTimeBase, Parcel in) { - mMainTimer = new DurationTimer(clocks, uid, type, timerPool, timeBase, in); + super(clocks, uid, type, timerPool, timeBase, in); mSubTimer = new DurationTimer(clocks, uid, type, null, subTimeBase, in); } /** - * Creates a DualTimer to hold a mMainTimer and a mSubTimer. - * The mMainTimer is based on the given timeBase and timerPool. + * Creates a DualTimer to hold a main timer (this) and a mSubTimer. + * The main timer (this) is based on the given timeBase and timerPool. * The mSubTimer is based on the given subTimeBase. The mSubTimer is not pooled, even if - * the mMainTimer is. + * the main timer is. */ public DualTimer(Clocks clocks, Uid uid, int type, ArrayList timerPool, TimeBase timeBase, TimeBase subTimeBase) { - mMainTimer = new DurationTimer(clocks, uid, type, timerPool, timeBase); + super(clocks, uid, type, timerPool, timeBase); mSubTimer = new DurationTimer(clocks, uid, type, null, subTimeBase); } - /** Get the main timer. */ - public DurationTimer getMainTimer() { - return mMainTimer; - } - /** Get the secondary timer. */ + @Override public DurationTimer getSubTimer() { return mSubTimer; } + @Override public void startRunningLocked(long elapsedRealtimeMs) { - mMainTimer.startRunningLocked(elapsedRealtimeMs); + super.startRunningLocked(elapsedRealtimeMs); mSubTimer.startRunningLocked(elapsedRealtimeMs); } + @Override public void stopRunningLocked(long elapsedRealtimeMs) { - mMainTimer.stopRunningLocked(elapsedRealtimeMs); + super.stopRunningLocked(elapsedRealtimeMs); mSubTimer.stopRunningLocked(elapsedRealtimeMs); } + @Override public void stopAllRunningLocked(long elapsedRealtimeMs) { - mMainTimer.stopAllRunningLocked(elapsedRealtimeMs); + super.stopAllRunningLocked(elapsedRealtimeMs); mSubTimer.stopAllRunningLocked(elapsedRealtimeMs); } - public void setMark(long elapsedRealtimeMs) { - mMainTimer.setMark(elapsedRealtimeMs); - mSubTimer.setMark(elapsedRealtimeMs); - } - + @Override public boolean reset(boolean detachIfReset) { boolean active = false; - active |= !mMainTimer.reset(detachIfReset); + active |= !super.reset(detachIfReset); active |= !mSubTimer.reset(detachIfReset); return !active; } + @Override public void detach() { - mMainTimer.detach(); + super.detach(); mSubTimer.detach(); } - /** - * Writes a possibly null DualTimer to a Parcel. - * - * @param out the Parcel to which to write. - * @param t a DualTimer, or null. - */ - public static void writeDualTimerToParcel(Parcel out, DualTimer t, long elapsedRealtimeUs) { - if (t != null) { - out.writeInt(1); - t.writeToParcel(out, elapsedRealtimeUs); - } else { - out.writeInt(0); - } - } - + @Override public void writeToParcel(Parcel out, long elapsedRealtimeUs) { - mMainTimer.writeToParcel(out, elapsedRealtimeUs); + super.writeToParcel(out, elapsedRealtimeUs); mSubTimer.writeToParcel(out, elapsedRealtimeUs); } + @Override public void writeSummaryFromParcelLocked(Parcel out, long elapsedRealtimeUs) { - mMainTimer.writeSummaryFromParcelLocked(out, elapsedRealtimeUs); + super.writeSummaryFromParcelLocked(out, elapsedRealtimeUs); mSubTimer.writeSummaryFromParcelLocked(out, elapsedRealtimeUs); } + @Override public void readSummaryFromParcelLocked(Parcel in) { - mMainTimer.readSummaryFromParcelLocked(in); + super.readSummaryFromParcelLocked(in); mSubTimer.readSummaryFromParcelLocked(in); } } @@ -5488,7 +5473,7 @@ public class BatteryStatsImpl extends BatteryStats { /** * The statistics we have collected for this uid's jobs. */ - final OverflowArrayMap mJobStats; + final OverflowArrayMap mJobStats; /** * The statistics we have collected for this uid's sensor activations. @@ -5533,10 +5518,10 @@ public class BatteryStatsImpl extends BatteryStats { mBsi.mOnBatteryTimeBase); } }; - mJobStats = mBsi.new OverflowArrayMap(uid) { - @Override public StopwatchTimer instantiateObject() { - return new StopwatchTimer(mBsi.mClocks, Uid.this, JOB, null, - mBsi.mOnBatteryTimeBase); + mJobStats = mBsi.new OverflowArrayMap(uid) { + @Override public DualTimer instantiateObject() { + return new DualTimer(mBsi.mClocks, Uid.this, JOB, null, + mBsi.mOnBatteryTimeBase, mOnBatteryBackgroundTimeBase); } }; @@ -5918,7 +5903,7 @@ public class BatteryStatsImpl extends BatteryStats { if (mWifiScanTimer == null) { return 0; } - return mWifiScanTimer.getMainTimer().getTotalTimeLocked(elapsedRealtimeUs, which); + return mWifiScanTimer.getTotalTimeLocked(elapsedRealtimeUs, which); } @Override @@ -5926,12 +5911,12 @@ public class BatteryStatsImpl extends BatteryStats { if (mWifiScanTimer == null) { return 0; } - return mWifiScanTimer.getMainTimer().getCountLocked(which); + return mWifiScanTimer.getCountLocked(which); } @Override public int getWifiScanBackgroundCount(int which) { - if (mWifiScanTimer == null) { + if (mWifiScanTimer == null || mWifiScanTimer.getSubTimer() == null) { return 0; } return mWifiScanTimer.getSubTimer().getCountLocked(which); @@ -5943,12 +5928,12 @@ public class BatteryStatsImpl extends BatteryStats { return 0; } final long elapsedRealtimeMs = (elapsedRealtimeUs + 500) / 1000; - return mWifiScanTimer.getMainTimer().getTotalDurationMsLocked(elapsedRealtimeMs) * 1000; + return mWifiScanTimer.getTotalDurationMsLocked(elapsedRealtimeMs) * 1000; } @Override public long getWifiScanBackgroundTime(final long elapsedRealtimeUs) { - if (mWifiScanTimer == null) { + if (mWifiScanTimer == null || mWifiScanTimer.getSubTimer() == null) { return 0; } final long elapsedRealtimeMs = (elapsedRealtimeUs + 500) / 1000; @@ -6008,10 +5993,7 @@ public class BatteryStatsImpl extends BatteryStats { @Override public Timer getBluetoothScanTimer() { - if (mBluetoothScanTimer == null) { - return null; - } - return mBluetoothScanTimer.getMainTimer(); + return mBluetoothScanTimer; } @Override @@ -6361,9 +6343,9 @@ public class BatteryStatsImpl extends BatteryStats { } } mSyncStats.cleanup(); - final ArrayMap jobStats = mJobStats.getMap(); + final ArrayMap jobStats = mJobStats.getMap(); for (int ij=jobStats.size()-1; ij>=0; ij--) { - StopwatchTimer timer = jobStats.valueAt(ij); + DualTimer timer = jobStats.valueAt(ij); if (timer.reset(false)) { jobStats.removeAt(ij); timer.detach(); @@ -6531,12 +6513,12 @@ public class BatteryStatsImpl extends BatteryStats { Timer.writeTimerToParcel(out, timer, elapsedRealtimeUs); } - final ArrayMap jobStats = mJobStats.getMap(); + final ArrayMap jobStats = mJobStats.getMap(); int NJ = jobStats.size(); out.writeInt(NJ); for (int ij=0; ij 0) { // Set the new mark so that next time we get new data since this point. @@ -9444,7 +9423,7 @@ public class BatteryStatsImpl extends BatteryStats { continue; } - totalScanTimeMs += u.mBluetoothScanTimer.getMainTimer().getTimeSinceMarkLocked( + totalScanTimeMs += u.mBluetoothScanTimer.getTimeSinceMarkLocked( elapsedRealtimeMs * 1000) / 1000; } @@ -9465,7 +9444,7 @@ public class BatteryStatsImpl extends BatteryStats { continue; } - long scanTimeSinceMarkMs = u.mBluetoothScanTimer.getMainTimer().getTimeSinceMarkLocked( + long scanTimeSinceMarkMs = u.mBluetoothScanTimer.getTimeSinceMarkLocked( elapsedRealtimeMs * 1000) / 1000; if (scanTimeSinceMarkMs > 0) { // Set the new mark so that next time we get new data since this point. @@ -11526,7 +11505,7 @@ public class BatteryStatsImpl extends BatteryStats { syncStats.valueAt(is).writeSummaryFromParcelLocked(out, NOWREAL_SYS); } - final ArrayMap jobStats = u.mJobStats.getMap(); + final ArrayMap jobStats = u.mJobStats.getMap(); int NJ = jobStats.size(); out.writeInt(NJ); for (int ij=0; ij jobs = + bi.getUidStats().get(UID).getJobStats(); + assertEquals(1, jobs.size()); + BatteryStats.Timer timer = jobs.valueAt(0); + BatteryStats.Timer bgTimer = timer.getSubTimer(); + long time = timer.getTotalTimeLocked(curr, STATS_SINCE_CHARGED); + int count = timer.getCountLocked(STATS_SINCE_CHARGED); + int bgCount = bgTimer.getCountLocked(STATS_SINCE_CHARGED); + long bgTime = bgTimer.getTotalTimeLocked(curr, STATS_SINCE_CHARGED); + assertEquals((161 - 151 + 305 - 202) * 1000, time); + assertEquals(2, count); + assertEquals(1, bgCount); + assertEquals((305 - 254) * 1000, bgTime); + + // Test that a second job is separate. + curr = 1000 * (clocks.realtime = clocks.uptime = 3000); + final String jobName2 = "second_job"; + bi.noteJobStartLocked(jobName2, UID); + assertEquals(2, bi.getUidStats().get(UID).getJobStats().size()); + bi.noteJobFinishLocked(jobName2, UID); + } }