Loading core/java/com/android/internal/os/BatteryStatsHistory.java +74 −7 Original line number Diff line number Diff line Loading @@ -79,7 +79,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 = 208; private static final int VERSION = 209; private static final String HISTORY_DIR = "battery-history"; private static final String FILE_SUFFIX = ".bin"; Loading Loading @@ -198,6 +198,7 @@ public class BatteryStatsHistory { private long mHistoryBaseTimeMs; private boolean mMeasuredEnergyHeaderWritten = false; private boolean mCpuUsageHeaderWritten = false; private final VarintParceler mVarintParceler = new VarintParceler(); private byte mLastHistoryStepLevel = 0; Loading Loading @@ -1665,9 +1666,7 @@ public class BatteryStatsHistory { } mMeasuredEnergyHeaderWritten = true; } for (long chargeUC : cur.measuredEnergyDetails.chargeUC) { dest.writeLong(chargeUC); } mVarintParceler.writeLongArray(dest, cur.measuredEnergyDetails.chargeUC); } if (cur.cpuUsageDetails != null) { Loading @@ -1679,9 +1678,7 @@ public class BatteryStatsHistory { mCpuUsageHeaderWritten = true; } dest.writeInt(cur.cpuUsageDetails.uid); for (long cpuUsageMs: cur.cpuUsageDetails.cpuUsageMs) { dest.writeLong(cpuUsageMs); } mVarintParceler.writeLongArray(dest, cur.cpuUsageDetails.cpuUsageMs); } } } Loading Loading @@ -1930,4 +1927,74 @@ public class BatteryStatsHistory { entry.getKey()); } } /** * Writes/reads an array of longs into Parcel using a compact format, where small integers use * fewer bytes. It is a bit more expensive than just writing the long into the parcel, * but at scale saves a lot of storage and allows recording of longer battery history. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public static final class VarintParceler { /** * Writes an array of longs into Parcel using the varint format, see * https://developers.google.com/protocol-buffers/docs/encoding#varints */ public void writeLongArray(Parcel parcel, long[] values) { int out = 0; int shift = 0; for (long value : values) { boolean done = false; while (!done) { final byte b; if ((value & ~0x7FL) == 0) { b = (byte) value; done = true; } else { b = (byte) (((int) value & 0x7F) | 0x80); value >>>= 7; } if (shift == 32) { parcel.writeInt(out); shift = 0; out = 0; } out |= (b & 0xFF) << shift; shift += 8; } } if (shift != 0) { parcel.writeInt(out); } } /** * Reads a long written with {@link #writeLongArray} */ public void readLongArray(Parcel parcel, long[] values) { int in = parcel.readInt(); int available = 4; for (int i = 0; i < values.length; i++) { long result = 0; int shift; for (shift = 0; shift < 64; shift += 7) { if (available == 0) { in = parcel.readInt(); available = 4; } final byte b = (byte) in; in >>= 8; available--; result |= (long) (b & 0x7F) << shift; if ((b & 0x80) == 0) { values[i] = result; break; } } if (shift >= 64) { throw new ParcelFormatException("Invalid varint format"); } } } } } core/java/com/android/internal/os/BatteryStatsHistoryIterator.java +5 −7 Original line number Diff line number Diff line Loading @@ -35,6 +35,8 @@ public class BatteryStatsHistoryIterator { private final SparseArray<BatteryStats.HistoryTag> mHistoryTags = new SparseArray<>(); private BatteryStats.MeasuredEnergyDetails mMeasuredEnergyDetails; private BatteryStats.CpuUsageDetails mCpuUsageDetails; private final BatteryStatsHistory.VarintParceler mVarintParceler = new BatteryStatsHistory.VarintParceler(); public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history) { mBatteryStatsHistory = history; Loading @@ -61,7 +63,7 @@ public class BatteryStatsHistoryIterator { return true; } void readHistoryDelta(Parcel src, BatteryStats.HistoryItem cur) { private void readHistoryDelta(Parcel src, BatteryStats.HistoryItem cur) { int firstToken = src.readInt(); int deltaTimeToken = firstToken & BatteryStatsHistory.DELTA_TIME_MASK; cur.cmd = BatteryStats.HistoryItem.CMD_UPDATE; Loading Loading @@ -226,9 +228,7 @@ public class BatteryStatsHistoryIterator { throw new IllegalStateException("MeasuredEnergyDetails without a header"); } for (int i = 0; i < mMeasuredEnergyDetails.chargeUC.length; i++) { mMeasuredEnergyDetails.chargeUC[i] = src.readLong(); } mVarintParceler.readLongArray(src, mMeasuredEnergyDetails.chargeUC); cur.measuredEnergyDetails = mMeasuredEnergyDetails; } else { cur.measuredEnergyDetails = null; Loading @@ -249,9 +249,7 @@ public class BatteryStatsHistoryIterator { } mCpuUsageDetails.uid = src.readInt(); for (int i = 0; i < mCpuUsageDetails.cpuUsageMs.length; i++) { mCpuUsageDetails.cpuUsageMs[i] = src.readLong(); } mVarintParceler.readLongArray(src, mCpuUsageDetails.cpuUsageMs); cur.cpuUsageDetails = mCpuUsageDetails; } else { cur.cpuUsageDetails = null; Loading services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java +64 −0 Original line number Diff line number Diff line Loading @@ -382,4 +382,68 @@ public class BatteryStatsHistoryTest { pw.flush(); return writer.toString(); } @Test public void testVarintParceler() { long[] values = { 0, 1, 42, 0x1234, 0x10000000, 0x12345678, 0x7fffffff, 0xffffffffL, 0x100000000000L, 0x123456789012L, 0x1000000000000000L, 0x1234567890123456L, 0x7fffffffffffffffL, 0xffffffffffffffffL}; // Parcel subarrays of different lengths and assert the size of the resulting parcel testVarintParceler(Arrays.copyOfRange(values, 0, 1), 4); // v. 8 testVarintParceler(Arrays.copyOfRange(values, 0, 2), 4); // v. 16 testVarintParceler(Arrays.copyOfRange(values, 0, 3), 4); // v. 24 testVarintParceler(Arrays.copyOfRange(values, 0, 4), 8); // v. 32 testVarintParceler(Arrays.copyOfRange(values, 0, 5), 12); // v. 40 testVarintParceler(Arrays.copyOfRange(values, 0, 6), 16); // v. 48 testVarintParceler(Arrays.copyOfRange(values, 0, 7), 20); // v. 56 testVarintParceler(Arrays.copyOfRange(values, 0, 8), 28); // v. 64 testVarintParceler(Arrays.copyOfRange(values, 0, 9), 32); // v. 72 testVarintParceler(Arrays.copyOfRange(values, 0, 10), 40); // v. 80 testVarintParceler(Arrays.copyOfRange(values, 0, 11), 48); // v. 88 testVarintParceler(Arrays.copyOfRange(values, 0, 12), 60); // v. 96 testVarintParceler(Arrays.copyOfRange(values, 0, 13), 68); // v. 104 testVarintParceler(Arrays.copyOfRange(values, 0, 14), 76); // v. 112 } private void testVarintParceler(long[] values, int expectedLength) { BatteryStatsHistory.VarintParceler parceler = new BatteryStatsHistory.VarintParceler(); Parcel parcel = Parcel.obtain(); parcel.writeString("begin"); int pos = parcel.dataPosition(); parceler.writeLongArray(parcel, values); int length = parcel.dataPosition() - pos; parcel.writeString("end"); byte[] bytes = parcel.marshall(); parcel.recycle(); parcel = Parcel.obtain(); parcel.unmarshall(bytes, 0, bytes.length); parcel.setDataPosition(0); assertThat(parcel.readString()).isEqualTo("begin"); long[] result = new long[values.length]; parceler.readLongArray(parcel, result); assertThat(result).isEqualTo(values); assertThat(length).isEqualTo(expectedLength); assertThat(parcel.readString()).isEqualTo("end"); parcel.recycle(); } } Loading
core/java/com/android/internal/os/BatteryStatsHistory.java +74 −7 Original line number Diff line number Diff line Loading @@ -79,7 +79,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 = 208; private static final int VERSION = 209; private static final String HISTORY_DIR = "battery-history"; private static final String FILE_SUFFIX = ".bin"; Loading Loading @@ -198,6 +198,7 @@ public class BatteryStatsHistory { private long mHistoryBaseTimeMs; private boolean mMeasuredEnergyHeaderWritten = false; private boolean mCpuUsageHeaderWritten = false; private final VarintParceler mVarintParceler = new VarintParceler(); private byte mLastHistoryStepLevel = 0; Loading Loading @@ -1665,9 +1666,7 @@ public class BatteryStatsHistory { } mMeasuredEnergyHeaderWritten = true; } for (long chargeUC : cur.measuredEnergyDetails.chargeUC) { dest.writeLong(chargeUC); } mVarintParceler.writeLongArray(dest, cur.measuredEnergyDetails.chargeUC); } if (cur.cpuUsageDetails != null) { Loading @@ -1679,9 +1678,7 @@ public class BatteryStatsHistory { mCpuUsageHeaderWritten = true; } dest.writeInt(cur.cpuUsageDetails.uid); for (long cpuUsageMs: cur.cpuUsageDetails.cpuUsageMs) { dest.writeLong(cpuUsageMs); } mVarintParceler.writeLongArray(dest, cur.cpuUsageDetails.cpuUsageMs); } } } Loading Loading @@ -1930,4 +1927,74 @@ public class BatteryStatsHistory { entry.getKey()); } } /** * Writes/reads an array of longs into Parcel using a compact format, where small integers use * fewer bytes. It is a bit more expensive than just writing the long into the parcel, * but at scale saves a lot of storage and allows recording of longer battery history. */ @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) public static final class VarintParceler { /** * Writes an array of longs into Parcel using the varint format, see * https://developers.google.com/protocol-buffers/docs/encoding#varints */ public void writeLongArray(Parcel parcel, long[] values) { int out = 0; int shift = 0; for (long value : values) { boolean done = false; while (!done) { final byte b; if ((value & ~0x7FL) == 0) { b = (byte) value; done = true; } else { b = (byte) (((int) value & 0x7F) | 0x80); value >>>= 7; } if (shift == 32) { parcel.writeInt(out); shift = 0; out = 0; } out |= (b & 0xFF) << shift; shift += 8; } } if (shift != 0) { parcel.writeInt(out); } } /** * Reads a long written with {@link #writeLongArray} */ public void readLongArray(Parcel parcel, long[] values) { int in = parcel.readInt(); int available = 4; for (int i = 0; i < values.length; i++) { long result = 0; int shift; for (shift = 0; shift < 64; shift += 7) { if (available == 0) { in = parcel.readInt(); available = 4; } final byte b = (byte) in; in >>= 8; available--; result |= (long) (b & 0x7F) << shift; if ((b & 0x80) == 0) { values[i] = result; break; } } if (shift >= 64) { throw new ParcelFormatException("Invalid varint format"); } } } } }
core/java/com/android/internal/os/BatteryStatsHistoryIterator.java +5 −7 Original line number Diff line number Diff line Loading @@ -35,6 +35,8 @@ public class BatteryStatsHistoryIterator { private final SparseArray<BatteryStats.HistoryTag> mHistoryTags = new SparseArray<>(); private BatteryStats.MeasuredEnergyDetails mMeasuredEnergyDetails; private BatteryStats.CpuUsageDetails mCpuUsageDetails; private final BatteryStatsHistory.VarintParceler mVarintParceler = new BatteryStatsHistory.VarintParceler(); public BatteryStatsHistoryIterator(@NonNull BatteryStatsHistory history) { mBatteryStatsHistory = history; Loading @@ -61,7 +63,7 @@ public class BatteryStatsHistoryIterator { return true; } void readHistoryDelta(Parcel src, BatteryStats.HistoryItem cur) { private void readHistoryDelta(Parcel src, BatteryStats.HistoryItem cur) { int firstToken = src.readInt(); int deltaTimeToken = firstToken & BatteryStatsHistory.DELTA_TIME_MASK; cur.cmd = BatteryStats.HistoryItem.CMD_UPDATE; Loading Loading @@ -226,9 +228,7 @@ public class BatteryStatsHistoryIterator { throw new IllegalStateException("MeasuredEnergyDetails without a header"); } for (int i = 0; i < mMeasuredEnergyDetails.chargeUC.length; i++) { mMeasuredEnergyDetails.chargeUC[i] = src.readLong(); } mVarintParceler.readLongArray(src, mMeasuredEnergyDetails.chargeUC); cur.measuredEnergyDetails = mMeasuredEnergyDetails; } else { cur.measuredEnergyDetails = null; Loading @@ -249,9 +249,7 @@ public class BatteryStatsHistoryIterator { } mCpuUsageDetails.uid = src.readInt(); for (int i = 0; i < mCpuUsageDetails.cpuUsageMs.length; i++) { mCpuUsageDetails.cpuUsageMs[i] = src.readLong(); } mVarintParceler.readLongArray(src, mCpuUsageDetails.cpuUsageMs); cur.cpuUsageDetails = mCpuUsageDetails; } else { cur.cpuUsageDetails = null; Loading
services/tests/servicestests/src/com/android/server/power/stats/BatteryStatsHistoryTest.java +64 −0 Original line number Diff line number Diff line Loading @@ -382,4 +382,68 @@ public class BatteryStatsHistoryTest { pw.flush(); return writer.toString(); } @Test public void testVarintParceler() { long[] values = { 0, 1, 42, 0x1234, 0x10000000, 0x12345678, 0x7fffffff, 0xffffffffL, 0x100000000000L, 0x123456789012L, 0x1000000000000000L, 0x1234567890123456L, 0x7fffffffffffffffL, 0xffffffffffffffffL}; // Parcel subarrays of different lengths and assert the size of the resulting parcel testVarintParceler(Arrays.copyOfRange(values, 0, 1), 4); // v. 8 testVarintParceler(Arrays.copyOfRange(values, 0, 2), 4); // v. 16 testVarintParceler(Arrays.copyOfRange(values, 0, 3), 4); // v. 24 testVarintParceler(Arrays.copyOfRange(values, 0, 4), 8); // v. 32 testVarintParceler(Arrays.copyOfRange(values, 0, 5), 12); // v. 40 testVarintParceler(Arrays.copyOfRange(values, 0, 6), 16); // v. 48 testVarintParceler(Arrays.copyOfRange(values, 0, 7), 20); // v. 56 testVarintParceler(Arrays.copyOfRange(values, 0, 8), 28); // v. 64 testVarintParceler(Arrays.copyOfRange(values, 0, 9), 32); // v. 72 testVarintParceler(Arrays.copyOfRange(values, 0, 10), 40); // v. 80 testVarintParceler(Arrays.copyOfRange(values, 0, 11), 48); // v. 88 testVarintParceler(Arrays.copyOfRange(values, 0, 12), 60); // v. 96 testVarintParceler(Arrays.copyOfRange(values, 0, 13), 68); // v. 104 testVarintParceler(Arrays.copyOfRange(values, 0, 14), 76); // v. 112 } private void testVarintParceler(long[] values, int expectedLength) { BatteryStatsHistory.VarintParceler parceler = new BatteryStatsHistory.VarintParceler(); Parcel parcel = Parcel.obtain(); parcel.writeString("begin"); int pos = parcel.dataPosition(); parceler.writeLongArray(parcel, values); int length = parcel.dataPosition() - pos; parcel.writeString("end"); byte[] bytes = parcel.marshall(); parcel.recycle(); parcel = Parcel.obtain(); parcel.unmarshall(bytes, 0, bytes.length); parcel.setDataPosition(0); assertThat(parcel.readString()).isEqualTo("begin"); long[] result = new long[values.length]; parceler.readLongArray(parcel, result); assertThat(result).isEqualTo(values); assertThat(length).isEqualTo(expectedLength); assertThat(parcel.readString()).isEqualTo("end"); parcel.recycle(); } }