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

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

Limit amount of battery history included in BatteryUsageStats

Bug: 381939861
Flag: com.android.server.power.optimization.extended_battery_history_continuous_collection_enabled
Test: atest PowerStatsTests; atest PowerStatsTestsRavenwood
Change-Id: I2b4eb1a13653d4cf49991a92bdcb7d1892fd3077
parent 0bf4c09e
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -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;
        }

+25 −0
Original line number Diff line number Diff line
@@ -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.
@@ -310,6 +325,16 @@ public final class BatteryUsageStatsQuery implements Parcelable {
            return this;
        }

        /**
         * 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
+37 −14
Original line number Diff line number Diff line
@@ -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);
                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) {
+6 −2
Original line number Diff line number Diff line
@@ -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(),
+66 −1
Original line number Diff line number Diff line
@@ -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));
    }
}