Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d8371008 authored by Dmitri Plotnikov's avatar Dmitri Plotnikov
Browse files

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

Bug: 244623253
Test: atest FrameworksServicesTests:BatteryStatsTests
Change-Id: Idce38be9badf8f2460acb5eafe3367c9a2cbb0c3
parent 9e1f0003
Loading
Loading
Loading
Loading
+74 −7
Original line number Diff line number Diff line
@@ -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");
                }
            }
        }
    }
}
+5 −7
Original line number Diff line number Diff line
@@ -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;
+64 −0
Original line number Diff line number Diff line
@@ -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();
    }
}