Use varint to record cpu usage data and measured energy in battery history

Bug: 244623253
Test: atest FrameworksServicesTests:BatteryStatsTests
Change-Id: Idce38be9badf8f2460acb5eafe3367c9a2cbb0c3
This commit is contained in:
Dmitri Plotnikov
2022-09-14 18:04:22 -07:00
parent 9e1f0003d3
commit d837100844
3 changed files with 143 additions and 14 deletions

View File

@@ -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";
@@ -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;
@@ -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) {
@@ -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);
}
}
}
@@ -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");
}
}
}
}
}

View File

@@ -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;
@@ -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;
@@ -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;
@@ -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;

View File

@@ -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();
}
}