diff options
5 files changed, 143 insertions, 19 deletions
diff --git a/core/java/android/os/BatteryUsageStats.java b/core/java/android/os/BatteryUsageStats.java index f913fcfd56d4..86b8fad16275 100644 --- a/core/java/android/os/BatteryUsageStats.java +++ b/core/java/android/os/BatteryUsageStats.java @@ -161,6 +161,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable { private final List<UserBatteryConsumer> mUserBatteryConsumers; private final AggregateBatteryConsumer[] mAggregateBatteryConsumers; private final BatteryStatsHistory mBatteryStatsHistory; + private final long mPreferredHistoryDurationMs; private final BatteryConsumer.BatteryConsumerDataLayout mBatteryConsumerDataLayout; private CursorWindow mBatteryConsumersCursorWindow; @@ -174,6 +175,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable { mDischargedPowerUpperBound = builder.mDischargedPowerUpperBoundMah; mDischargeDurationMs = builder.mDischargeDurationMs; mBatteryStatsHistory = builder.mBatteryStatsHistory; + mPreferredHistoryDurationMs = builder.mPreferredHistoryDurationMs; mBatteryTimeRemainingMs = builder.mBatteryTimeRemainingMs; mChargeTimeRemainingMs = builder.mChargeTimeRemainingMs; mCustomPowerComponentNames = builder.mCustomPowerComponentNames; @@ -402,8 +404,10 @@ public final class BatteryUsageStats implements Parcelable, Closeable { if (source.readBoolean()) { mBatteryStatsHistory = BatteryStatsHistory.createFromBatteryUsageStatsParcel(source); + mPreferredHistoryDurationMs = source.readLong(); } else { mBatteryStatsHistory = null; + mPreferredHistoryDurationMs = 0; } } @@ -428,7 +432,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable { if (mBatteryStatsHistory != null) { dest.writeBoolean(true); - mBatteryStatsHistory.writeToBatteryUsageStatsParcel(dest); + mBatteryStatsHistory.writeToBatteryUsageStatsParcel(dest, mPreferredHistoryDurationMs); } else { dest.writeBoolean(false); } @@ -919,6 +923,7 @@ public final class BatteryUsageStats implements Parcelable, Closeable { private final SparseArray<UserBatteryConsumer.Builder> mUserBatteryConsumerBuilders = new SparseArray<>(); private BatteryStatsHistory mBatteryStatsHistory; + private long mPreferredHistoryDurationMs; public Builder(@NonNull String[] customPowerComponentNames) { this(customPowerComponentNames, false, false, false, 0); @@ -1092,8 +1097,10 @@ public final class BatteryUsageStats implements Parcelable, Closeable { * Sets the parceled recent history. */ @NonNull - public Builder setBatteryHistory(BatteryStatsHistory batteryStatsHistory) { + public Builder setBatteryHistory(BatteryStatsHistory batteryStatsHistory, + long preferredHistoryDurationMs) { mBatteryStatsHistory = batteryStatsHistory; + mPreferredHistoryDurationMs = preferredHistoryDurationMs; return this; } diff --git a/core/java/android/os/BatteryUsageStatsQuery.java b/core/java/android/os/BatteryUsageStatsQuery.java index 6e67578fadc8..5aed39bd8fa6 100644 --- a/core/java/android/os/BatteryUsageStatsQuery.java +++ b/core/java/android/os/BatteryUsageStatsQuery.java @@ -25,6 +25,7 @@ import com.android.internal.os.MonotonicClock; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.Arrays; +import java.util.concurrent.TimeUnit; /** * Query parameters for the {@link BatteryStatsManager#getBatteryUsageStats()} call. @@ -77,6 +78,7 @@ public final class BatteryUsageStatsQuery implements Parcelable { public static final int FLAG_BATTERY_USAGE_STATS_ACCUMULATED = 0x0080; private static final long DEFAULT_MAX_STATS_AGE_MS = 5 * 60 * 1000; + private static final long DEFAULT_PREFERRED_HISTORY_DURATION_MS = TimeUnit.HOURS.toMillis(2); private final int mFlags; @NonNull @@ -89,6 +91,7 @@ public final class BatteryUsageStatsQuery implements Parcelable { private long mMonotonicEndTime; private final double mMinConsumedPowerThreshold; private final @BatteryConsumer.PowerComponentId int[] mPowerComponents; + private final long mPreferredHistoryDurationMs; private BatteryUsageStatsQuery(@NonNull Builder builder) { mFlags = builder.mFlags; @@ -101,6 +104,7 @@ public final class BatteryUsageStatsQuery implements Parcelable { mMonotonicStartTime = builder.mMonotonicStartTime; mMonotonicEndTime = builder.mMonotonicEndTime; mPowerComponents = builder.mPowerComponents; + mPreferredHistoryDurationMs = builder.mPreferredHistoryDurationMs; } @BatteryUsageStatsFlags @@ -197,6 +201,13 @@ public final class BatteryUsageStatsQuery implements Parcelable { return mAggregatedToTimestamp; } + /** + * Returns the preferred duration of battery history (tail) to be included in the query result. + */ + public long getPreferredHistoryDurationMs() { + return mPreferredHistoryDurationMs; + } + @Override public String toString() { return "BatteryUsageStatsQuery{" @@ -209,6 +220,7 @@ public final class BatteryUsageStatsQuery implements Parcelable { + ", mMonotonicEndTime=" + mMonotonicEndTime + ", mMinConsumedPowerThreshold=" + mMinConsumedPowerThreshold + ", mPowerComponents=" + Arrays.toString(mPowerComponents) + + ", mMaxHistoryDurationMs=" + mPreferredHistoryDurationMs + '}'; } @@ -223,6 +235,7 @@ public final class BatteryUsageStatsQuery implements Parcelable { mAggregatedFromTimestamp = in.readLong(); mAggregatedToTimestamp = in.readLong(); mPowerComponents = in.createIntArray(); + mPreferredHistoryDurationMs = in.readLong(); } @Override @@ -237,6 +250,7 @@ public final class BatteryUsageStatsQuery implements Parcelable { dest.writeLong(mAggregatedFromTimestamp); dest.writeLong(mAggregatedToTimestamp); dest.writeIntArray(mPowerComponents); + dest.writeLong(mPreferredHistoryDurationMs); } @Override @@ -271,6 +285,7 @@ public final class BatteryUsageStatsQuery implements Parcelable { private long mAggregateToTimestamp; private double mMinConsumedPowerThreshold = 0; private @BatteryConsumer.PowerComponentId int[] mPowerComponents; + private long mPreferredHistoryDurationMs = DEFAULT_PREFERRED_HISTORY_DURATION_MS; /** * Builds a read-only BatteryUsageStatsQuery object. @@ -311,6 +326,16 @@ public final class BatteryUsageStatsQuery implements Parcelable { } /** + * Set the preferred amount of battery history to be included in the result, provided + * that `includeBatteryHistory` is also called. The actual amount of history included in + * the result may vary for performance reasons and may exceed the specified preference. + */ + public Builder setPreferredHistoryDurationMs(long preferredHistoryDurationMs) { + mPreferredHistoryDurationMs = preferredHistoryDurationMs; + return this; + } + + /** * Requests that per-process state data be included in the BatteryUsageStats, if * available. Check {@link BatteryUsageStats#isProcessStateDataIncluded()} on the result * to see if the data is available. diff --git a/core/java/com/android/internal/os/BatteryStatsHistory.java b/core/java/com/android/internal/os/BatteryStatsHistory.java index dc440e36ca0d..f49c5f1c2b0f 100644 --- a/core/java/com/android/internal/os/BatteryStatsHistory.java +++ b/core/java/com/android/internal/os/BatteryStatsHistory.java @@ -84,7 +84,7 @@ public class BatteryStatsHistory { private static final String TAG = "BatteryStatsHistory"; // Current on-disk Parcel version. Must be updated when the format of the parcelable changes - private static final int VERSION = 211; + private static final int VERSION = 212; private static final String HISTORY_DIR = "battery-history"; private static final String FILE_SUFFIX = ".bh"; @@ -211,6 +211,8 @@ public class BatteryStatsHistory { private final MonotonicClock mMonotonicClock; // Monotonic time when we started writing to the history buffer private long mHistoryBufferStartTime; + // Monotonic time when the last event was written to the history buffer + private long mHistoryMonotonicEndTime; // Monotonically increasing size of written history private long mMonotonicHistorySize; private final ArraySet<PowerStats.Descriptor> mWrittenPowerStatsDescriptors = new ArraySet<>(); @@ -423,13 +425,22 @@ public class BatteryStatsHistory { return file; } - void writeToParcel(Parcel out, boolean useBlobs) { + void writeToParcel(Parcel out, boolean useBlobs, + long preferredEarliestIncludedTimestampMs) { Trace.traceBegin(TRACE_TAG_SYSTEM_SERVER, "BatteryStatsHistory.writeToParcel"); lock(); try { final long start = SystemClock.uptimeMillis(); - out.writeInt(mHistoryFiles.size() - 1); for (int i = 0; i < mHistoryFiles.size() - 1; i++) { + long monotonicEndTime = Long.MAX_VALUE; + if (i < mHistoryFiles.size() - 1) { + monotonicEndTime = mHistoryFiles.get(i + 1).monotonicTimeMs; + } + + if (monotonicEndTime < preferredEarliestIncludedTimestampMs) { + continue; + } + AtomicFile file = mHistoryFiles.get(i).atomicFile; byte[] raw = new byte[0]; try { @@ -437,6 +448,8 @@ public class BatteryStatsHistory { } catch (Exception e) { Slog.e(TAG, "Error reading file " + file.getBaseFile().getPath(), e); } + + out.writeBoolean(true); if (useBlobs) { out.writeBlob(raw); } else { @@ -444,6 +457,7 @@ public class BatteryStatsHistory { out.writeByteArray(raw); } } + out.writeBoolean(false); if (DEBUG) { Slog.d(TAG, "writeToParcel duration ms:" + (SystemClock.uptimeMillis() - start)); @@ -634,6 +648,7 @@ public class BatteryStatsHistory { mWritableHistory = writableHistory; if (mWritableHistory != null) { mMutable = false; + mHistoryMonotonicEndTime = mWritableHistory.mHistoryMonotonicEndTime; } if (historyBuffer != null) { @@ -937,6 +952,8 @@ public class BatteryStatsHistory { } // skip monotonic time field. p.readLong(); + // skip monotonic end time field + p.readLong(); // skip monotonic size field p.readLong(); @@ -996,6 +1013,8 @@ public class BatteryStatsHistory { } // skip monotonic time field. out.readLong(); + // skip monotonic end time field + out.readLong(); // skip monotonic size field out.readLong(); return true; @@ -1024,6 +1043,7 @@ public class BatteryStatsHistory { p.setDataPosition(0); p.readInt(); // Skip the version field long monotonicTime = p.readLong(); + p.readLong(); // Skip monotonic end time field p.readLong(); // Skip monotonic size field p.setDataPosition(pos); return monotonicTime; @@ -1086,7 +1106,10 @@ public class BatteryStatsHistory { public void writeToParcel(Parcel out) { synchronized (this) { writeHistoryBuffer(out); - writeToParcel(out, false /* useBlobs */); + /* useBlobs */ + if (mHistoryDir != null) { + mHistoryDir.writeToParcel(out, false /* useBlobs */, 0); + } } } @@ -1096,16 +1119,13 @@ public class BatteryStatsHistory { * * @param out the output parcel */ - public void writeToBatteryUsageStatsParcel(Parcel out) { + public void writeToBatteryUsageStatsParcel(Parcel out, long preferredHistoryDurationMs) { synchronized (this) { out.writeBlob(mHistoryBuffer.marshall()); - writeToParcel(out, true /* useBlobs */); - } - } - - private void writeToParcel(Parcel out, boolean useBlobs) { - if (mHistoryDir != null) { - mHistoryDir.writeToParcel(out, useBlobs); + if (mHistoryDir != null) { + mHistoryDir.writeToParcel(out, true /* useBlobs */, + mHistoryMonotonicEndTime - preferredHistoryDurationMs); + } } } @@ -1166,8 +1186,7 @@ public class BatteryStatsHistory { private void readFromParcel(Parcel in, boolean useBlobs) { final long start = SystemClock.uptimeMillis(); mHistoryParcels = new ArrayList<>(); - final int count = in.readInt(); - for (int i = 0; i < count; i++) { + while (in.readBoolean()) { byte[] temp = useBlobs ? in.readBlob() : in.createByteArray(); if (temp == null || temp.length == 0) { continue; @@ -2081,6 +2100,8 @@ public class BatteryStatsHistory { */ @GuardedBy("this") private void writeHistoryDelta(Parcel dest, HistoryItem cur, HistoryItem last) { + mHistoryMonotonicEndTime = cur.time; + if (last == null || cur.cmd != HistoryItem.CMD_UPDATE) { dest.writeInt(BatteryStatsHistory.DELTA_TIME_ABS); cur.writeToParcel(dest, 0); @@ -2396,6 +2417,7 @@ public class BatteryStatsHistory { } mHistoryBufferStartTime = in.readLong(); + mHistoryMonotonicEndTime = in.readLong(); mMonotonicHistorySize = in.readLong(); mHistoryBuffer.setDataSize(0); @@ -2424,6 +2446,7 @@ public class BatteryStatsHistory { private void writeHistoryBuffer(Parcel out) { out.writeInt(BatteryStatsHistory.VERSION); out.writeLong(mHistoryBufferStartTime); + out.writeLong(mHistoryMonotonicEndTime); out.writeLong(mMonotonicHistorySize); out.writeInt(mHistoryBuffer.dataSize()); if (DEBUG) { diff --git a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java index 977c6db66106..a5185a2139db 100644 --- a/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java +++ b/services/core/java/com/android/server/power/stats/BatteryUsageStatsProvider.java @@ -38,6 +38,7 @@ import com.android.internal.os.CpuScalingPolicies; import com.android.internal.os.MonotonicClock; import com.android.internal.os.PowerProfile; import com.android.internal.util.ArrayUtils; +import com.android.server.power.optimization.Flags; import com.android.server.power.stats.BatteryStatsImpl.BatteryStatsSession; import java.io.PrintWriter; @@ -351,7 +352,7 @@ public class BatteryUsageStatsProvider { accumulatedStats.endMonotonicTime = endMonotonicTime; accumulatedStats.builder.setStatsEndTimestamp(endWallClockTime); - accumulatedStats.builder.setStatsDuration(endWallClockTime - startMonotonicTime); + accumulatedStats.builder.setStatsDuration(endMonotonicTime - startMonotonicTime); mPowerAttributor.estimatePowerConsumption(accumulatedStats.builder, session.getHistory(), startMonotonicTime, endMonotonicTime); @@ -403,7 +404,10 @@ public class BatteryUsageStatsProvider { } if ((query.getFlags() & BatteryUsageStatsQuery.FLAG_BATTERY_USAGE_STATS_INCLUDE_HISTORY) != 0) { - batteryUsageStatsBuilder.setBatteryHistory(session.getHistory().copy()); + batteryUsageStatsBuilder.setBatteryHistory(session.getHistory().copy(), + Flags.extendedBatteryHistoryContinuousCollectionEnabled() + ? query.getPreferredHistoryDurationMs() + : Long.MAX_VALUE); } mPowerAttributor.estimatePowerConsumption(batteryUsageStatsBuilder, session.getHistory(), diff --git a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java index d427c9d9ee37..e94ef5bb4871 100644 --- a/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java +++ b/services/tests/powerstatstests/src/com/android/server/power/stats/BatteryUsageStatsProviderTest.java @@ -39,6 +39,8 @@ import android.os.Handler; import android.os.Parcel; import android.os.Process; import android.os.UidBatteryConsumer; +import android.platform.test.annotations.EnableFlags; +import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.ravenwood.RavenwoodRule; import android.util.SparseLongArray; @@ -49,6 +51,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.os.BatteryStatsHistoryIterator; import com.android.internal.os.MonotonicClock; import com.android.internal.os.PowerProfile; +import com.android.server.power.optimization.Flags; import com.android.server.power.stats.processor.MultiStatePowerAttributor; import org.junit.Before; @@ -59,6 +62,7 @@ import org.junit.runner.RunWith; import java.io.File; import java.io.IOException; import java.util.List; +import java.util.concurrent.TimeUnit; @SmallTest @RunWith(AndroidJUnit4.class) @@ -68,11 +72,14 @@ public class BatteryUsageStatsProviderTest { .setProvideMainThread(true) .build(); + @Rule(order = 1) + public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); + private static final int APP_UID = Process.FIRST_APPLICATION_UID + 42; private static final long MINUTE_IN_MS = 60 * 1000; private static final double PRECISION = 0.00001; - @Rule(order = 1) + @Rule(order = 2) public final BatteryUsageStatsRule mStatsRule = new BatteryUsageStatsRule(12345) .createTempDirectory() @@ -868,4 +875,62 @@ public class BatteryUsageStatsProviderTest { stats.close(); } + + @Test + @EnableFlags(Flags.FLAG_EXTENDED_BATTERY_HISTORY_CONTINUOUS_COLLECTION_ENABLED) + public void testIncludeSubsetOfHistory() throws IOException { + MockBatteryStatsImpl batteryStats = mStatsRule.getBatteryStats(); + batteryStats.getHistory().setMaxHistoryBufferSize(100); + synchronized (batteryStats) { + batteryStats.setRecordAllHistoryLocked(true); + } + batteryStats.forceRecordAllHistory(); + batteryStats.setNoAutoReset(true); + + long lastIncludedEventTimestamp = 0; + String tag = "work work work work work work work work work work work work work work work"; + for (int i = 1; i < 50; i++) { + mStatsRule.advanceTime(TimeUnit.MINUTES.toMillis(9)); + synchronized (batteryStats) { + batteryStats.noteJobStartLocked(tag, 42); + } + mStatsRule.advanceTime(TimeUnit.MINUTES.toMillis(1)); + synchronized (batteryStats) { + batteryStats.noteJobFinishLocked(tag, 42, 0); + } + lastIncludedEventTimestamp = mMonotonicClock.monotonicTime(); + } + + BatteryUsageStatsProvider provider = new BatteryUsageStatsProvider(mContext, + mock(PowerAttributor.class), mStatsRule.getPowerProfile(), + mStatsRule.getCpuScalingPolicies(), mock(PowerStatsStore.class), 0, mMockClock, + mMonotonicClock); + + BatteryUsageStatsQuery query = new BatteryUsageStatsQuery.Builder() + .includeBatteryHistory() + .setPreferredHistoryDurationMs(TimeUnit.MINUTES.toMillis(20)) + .build(); + final BatteryUsageStats stats = provider.getBatteryUsageStats(batteryStats, query); + Parcel parcel = Parcel.obtain(); + stats.writeToParcel(parcel, 0); + stats.close(); + + parcel.setDataPosition(0); + BatteryUsageStats actual = BatteryUsageStats.CREATOR.createFromParcel(parcel); + + long firstIncludedEventTimestamp = 0; + try (BatteryStatsHistoryIterator it = actual.iterateBatteryStatsHistory()) { + BatteryStats.HistoryItem item; + while ((item = it.next()) != null) { + if (item.eventCode == BatteryStats.HistoryItem.EVENT_JOB_START) { + firstIncludedEventTimestamp = item.time; + break; + } + } + } + actual.close(); + + assertThat(firstIncludedEventTimestamp) + .isAtLeast(lastIncludedEventTimestamp - TimeUnit.MINUTES.toMillis(30)); + } } |