Merge "Use varint to record cpu usage data and measured energy in battery history"
This commit is contained in:
committed by
Android (Google) Code Review
commit
a64fd1b4db
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user