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

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

Add Screen PowerStatsCollector

Bug: 333941740
Test: atest PowerStatsTestsRavenwood; atest PowerStatsTests
Flag: com.android.server.power.optimization.streamlined_misc_battery_stats
Change-Id: Ibaf53bb486f938f8a50a04b7f77c1b5a6ddfb089
parent 04606353
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -2491,7 +2491,7 @@ public abstract class BatteryStats {
    public static final int SCREEN_BRIGHTNESS_LIGHT = 3;
    public static final int SCREEN_BRIGHTNESS_BRIGHT = 4;

    static final String[] SCREEN_BRIGHTNESS_NAMES = {
    public static final String[] SCREEN_BRIGHTNESS_NAMES = {
        "dark", "dim", "medium", "light", "bright"
    };

+2 −0
Original line number Diff line number Diff line
@@ -636,6 +636,8 @@ public final class BatteryStatsService extends IBatteryStats.Stub
                BatteryConsumer.POWER_COMPONENT_CPU,
                Flags.streamlinedBatteryStats());

        mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_SCREEN,
                Flags.streamlinedMiscBatteryStats());
        mStats.setPowerStatsCollectorEnabled(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO,
                Flags.streamlinedConnectivityBatteryStats());
        mBatteryUsageStatsProvider.setPowerStatsExporterEnabled(
+83 −8
Original line number Diff line number Diff line
@@ -296,6 +296,7 @@ public class BatteryStatsImpl extends BatteryStats {
    private final LongSparseArray<SamplingTimer> mKernelMemoryStats = new LongSparseArray<>();
    private int[] mCpuPowerBracketMap;
    private final CpuPowerStatsCollector mCpuPowerStatsCollector;
    private final ScreenPowerStatsCollector mScreenPowerStatsCollector;
    private final MobileRadioPowerStatsCollector mMobileRadioPowerStatsCollector;
    private final WifiPowerStatsCollector mWifiPowerStatsCollector;
    private final BluetoothPowerStatsCollector mBluetoothPowerStatsCollector;
@@ -303,6 +304,54 @@ public class BatteryStatsImpl extends BatteryStats {
    private final GnssPowerStatsCollector mGnssPowerStatsCollector;
    private final CustomEnergyConsumerPowerStatsCollector mCustomEnergyConsumerPowerStatsCollector;
    private final SparseBooleanArray mPowerStatsCollectorEnabled = new SparseBooleanArray();
    private ScreenPowerStatsCollector.ScreenUsageTimeRetriever mScreenUsageTimeRetriever =
            new ScreenPowerStatsCollector.ScreenUsageTimeRetriever() {
                @Override
                public long getScreenOnTimeMs(int display) {
                    synchronized (BatteryStatsImpl.this) {
                        return getDisplayScreenOnTime(display,
                                mClock.elapsedRealtime() * 1000) / 1000;
                    }
                }
                @Override
                public long getBrightnessLevelTimeMs(int display, int brightnessLevel) {
                    synchronized (BatteryStatsImpl.this) {
                        return getDisplayScreenBrightnessTime(display, brightnessLevel,
                                mClock.elapsedRealtime() * 1000) / 1000;
                    }
                }
                @Override
                public long getScreenDozeTimeMs(int display) {
                    synchronized (BatteryStatsImpl.this) {
                        return getDisplayScreenDozeTime(display,
                                mClock.elapsedRealtime() * 1000) / 1000;
                    }
                }
                @Override
                public void retrieveTopActivityTimes(Callback callback) {
                    synchronized (BatteryStatsImpl.this) {
                        long elapsedTimeUs = mClock.elapsedRealtime() * 1000;
                        for (int i = mUidStats.size() - 1; i >= 0; i--) {
                            Uid uid = mUidStats.valueAt(i);
                            long topStateTime = uid.getProcessStateTime(Uid.PROCESS_STATE_TOP,
                                    elapsedTimeUs, STATS_SINCE_CHARGED) / 1000;
                            Timer timer = uid.getForegroundActivityTimer();
                            if (timer == null) {
                                callback.onUidTopActivityTime(uid.mUid, topStateTime);
                            } else {
                                long topActivityTime = timer.getTotalTimeLocked(elapsedTimeUs,
                                        STATS_SINCE_CHARGED) / 1000;
                                callback.onUidTopActivityTime(uid.mUid, Math.min(topStateTime,
                                        topActivityTime));
                            }
                        }
                    }
                }
            };
    private final WifiPowerStatsCollector.WifiStatsRetriever mWifiStatsRetriever =
            new WifiPowerStatsCollector.WifiStatsRetriever() {
                @Override
@@ -1966,8 +2015,9 @@ public class BatteryStatsImpl extends BatteryStats {
    }
    private class PowerStatsCollectorInjector implements CpuPowerStatsCollector.Injector,
            MobileRadioPowerStatsCollector.Injector, WifiPowerStatsCollector.Injector,
            BluetoothPowerStatsCollector.Injector, EnergyConsumerPowerStatsCollector.Injector {
            ScreenPowerStatsCollector.Injector, MobileRadioPowerStatsCollector.Injector,
            WifiPowerStatsCollector.Injector, BluetoothPowerStatsCollector.Injector,
            EnergyConsumerPowerStatsCollector.Injector {
        private PackageManager mPackageManager;
        private PowerStatsCollector.ConsumedEnergyRetriever mConsumedEnergyRetriever;
        private NetworkStatsManager mNetworkStatsManager;
@@ -2038,6 +2088,16 @@ public class BatteryStatsImpl extends BatteryStats {
            return () -> mBatteryVoltageMv;
        }
        @Override
        public ScreenPowerStatsCollector.ScreenUsageTimeRetriever getScreenUsageTimeRetriever() {
            return mScreenUsageTimeRetriever;
        }
        @Override
        public int getDisplayCount() {
            return BatteryStatsImpl.this.getDisplayCount();
        }
        @Override
        public Supplier<NetworkStats> getMobileNetworkStatsSupplier() {
            return () -> readMobileNetworkStatsLocked(mNetworkStatsManager);
@@ -5736,6 +5796,9 @@ public class BatteryStatsImpl extends BatteryStats {
        maybeUpdateOverallScreenBrightness(overallBin, elapsedRealtimeMs, uptimeMs);
        if (shouldScheduleSync) {
            if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_SCREEN)) {
                mScreenPowerStatsCollector.schedule();
            } else {
                final int numDisplays = mPerDisplayBatteryStats.length;
                final int[] displayStates = new int[numDisplays];
                for (int i = 0; i < numDisplays; i++) {
@@ -5745,6 +5808,7 @@ public class BatteryStatsImpl extends BatteryStats {
                        batteryRunning, batteryScreenOffRunning, state, displayStates);
            }
        }
    }
    /**
     * Note screen brightness change for a display.
@@ -11290,6 +11354,9 @@ public class BatteryStatsImpl extends BatteryStats {
        mCpuPowerStatsCollector = new CpuPowerStatsCollector(mPowerStatsCollectorInjector);
        mCpuPowerStatsCollector.addConsumer(this::recordPowerStats);
        mScreenPowerStatsCollector = new ScreenPowerStatsCollector(mPowerStatsCollectorInjector);
        mScreenPowerStatsCollector.addConsumer(this::recordPowerStats);
        mMobileRadioPowerStatsCollector = new MobileRadioPowerStatsCollector(
                mPowerStatsCollectorInjector, this::onMobileRadioPowerStatsRetrieved);
        mMobileRadioPowerStatsCollector.addConsumer(this::recordPowerStats);
@@ -14750,6 +14817,10 @@ public class BatteryStatsImpl extends BatteryStats {
                mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_CPU));
        mCpuPowerStatsCollector.schedule();
        mScreenPowerStatsCollector.setEnabled(
                mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_SCREEN));
        mScreenPowerStatsCollector.schedule();
        mMobileRadioPowerStatsCollector.setEnabled(
                mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO));
        mMobileRadioPowerStatsCollector.schedule();
@@ -14786,6 +14857,8 @@ public class BatteryStatsImpl extends BatteryStats {
        switch (powerComponent) {
            case BatteryConsumer.POWER_COMPONENT_CPU:
                return mCpuPowerStatsCollector;
            case BatteryConsumer.POWER_COMPONENT_SCREEN:
                return mScreenPowerStatsCollector;
            case BatteryConsumer.POWER_COMPONENT_MOBILE_RADIO:
                return mMobileRadioPowerStatsCollector;
            case BatteryConsumer.POWER_COMPONENT_WIFI:
@@ -16329,6 +16402,7 @@ public class BatteryStatsImpl extends BatteryStats {
     */
    public void schedulePowerStatsSampleCollection() {
        mCpuPowerStatsCollector.forceSchedule();
        mScreenPowerStatsCollector.forceSchedule();
        mMobileRadioPowerStatsCollector.forceSchedule();
        mWifiPowerStatsCollector.forceSchedule();
        mBluetoothPowerStatsCollector.forceSchedule();
@@ -16351,6 +16425,7 @@ public class BatteryStatsImpl extends BatteryStats {
     */
    public void dumpStatsSample(PrintWriter pw) {
        mCpuPowerStatsCollector.collectAndDump(pw);
        mScreenPowerStatsCollector.collectAndDump(pw);
        mMobileRadioPowerStatsCollector.collectAndDump(pw);
        mWifiPowerStatsCollector.collectAndDump(pw);
        mBluetoothPowerStatsCollector.collectAndDump(pw);
+227 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.power.stats;

import android.hardware.power.stats.EnergyConsumerType;
import android.os.BatteryConsumer;
import android.os.BatteryStats;
import android.os.Handler;
import android.os.PersistableBundle;
import android.util.Slog;
import android.util.SparseLongArray;

import com.android.internal.os.Clock;
import com.android.internal.os.PowerStats;

import java.util.Arrays;
import java.util.function.IntSupplier;

public class ScreenPowerStatsCollector extends PowerStatsCollector {
    private static final String TAG = "ScreenPowerStatsCollector";

    interface ScreenUsageTimeRetriever {
        interface Callback {
            void onUidTopActivityTime(int uid, long topActivityTimeMs);
        }

        void retrieveTopActivityTimes(Callback callback);

        long getScreenOnTimeMs(int display);
        long getBrightnessLevelTimeMs(int display, int brightnessLevel);
        long getScreenDozeTimeMs(int display);
    }

    interface Injector {
        Handler getHandler();
        Clock getClock();
        PowerStatsUidResolver getUidResolver();
        long getPowerStatsCollectionThrottlePeriod(String powerComponentName);
        ConsumedEnergyRetriever getConsumedEnergyRetriever();
        IntSupplier getVoltageSupplier();
        ScreenUsageTimeRetriever getScreenUsageTimeRetriever();
        int getDisplayCount();
    }

    private static final long ENERGY_UNSPECIFIED = -1;

    private final Injector mInjector;
    private boolean mIsInitialized;
    private ScreenPowerStatsLayout mLayout;
    private int mDisplayCount;
    private PowerStats mPowerStats;
    private ConsumedEnergyRetriever mConsumedEnergyRetriever;
    private IntSupplier mVoltageSupplier;
    private ScreenUsageTimeRetriever mScreenUsageTimeRetriever;
    private int[] mEnergyConsumerIds = new int[0];
    private long[] mLastConsumedEnergyUws;
    private int mLastVoltageMv;
    private boolean mFirstSample = true;
    private long[] mLastScreenOnTime;
    private long[][] mLastBrightnessLevelTime;
    private long[] mLastDozeTime;
    private final SparseLongArray mLastTopActivityTime = new SparseLongArray();
    private long mLastCollectionTime;

    ScreenPowerStatsCollector(Injector injector) {
        super(injector.getHandler(),
                injector.getPowerStatsCollectionThrottlePeriod(
                        BatteryConsumer.powerComponentIdToString(
                                BatteryConsumer.POWER_COMPONENT_SCREEN)),
                injector.getUidResolver(), injector.getClock());
        mInjector = injector;
    }

    private boolean ensureInitialized() {
        if (mIsInitialized) {
            return true;
        }

        if (!isEnabled()) {
            return false;
        }

        mDisplayCount = mInjector.getDisplayCount();
        mConsumedEnergyRetriever = mInjector.getConsumedEnergyRetriever();
        mVoltageSupplier = mInjector.getVoltageSupplier();
        mScreenUsageTimeRetriever = mInjector.getScreenUsageTimeRetriever();
        mEnergyConsumerIds = mConsumedEnergyRetriever.getEnergyConsumerIds(
                EnergyConsumerType.DISPLAY);
        mLastConsumedEnergyUws = new long[mEnergyConsumerIds.length];
        Arrays.fill(mLastConsumedEnergyUws, ENERGY_UNSPECIFIED);

        mLayout = new ScreenPowerStatsLayout();
        mLayout.addDeviceScreenUsageDurationSection(mInjector.getDisplayCount());
        mLayout.addDeviceSectionEnergyConsumers(mEnergyConsumerIds.length);
        mLayout.addDeviceSectionUsageDuration();
        mLayout.addDeviceSectionPowerEstimate();
        mLayout.addUidTopActivitiyDuration();
        mLayout.addUidSectionPowerEstimate();

        PersistableBundle extras = new PersistableBundle();
        mLayout.toExtras(extras);
        PowerStats.Descriptor powerStatsDescriptor = new PowerStats.Descriptor(
                BatteryConsumer.POWER_COMPONENT_SCREEN, mLayout.getDeviceStatsArrayLength(),
                null, 0, mLayout.getUidStatsArrayLength(),
                extras);

        mLastScreenOnTime = new long[mDisplayCount];
        mLastBrightnessLevelTime = new long[mDisplayCount][BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS];
        mLastDozeTime = new long[mDisplayCount];

        mPowerStats = new PowerStats(powerStatsDescriptor);

        mIsInitialized = true;
        return true;
    }

    @Override
    protected PowerStats collectStats() {
        if (!ensureInitialized()) {
            return null;
        }

        if (mEnergyConsumerIds.length != 0) {
            collectEnergyConsumers();
        }

        for (int display = 0; display < mDisplayCount; display++) {
            long screenOnTimeMs = mScreenUsageTimeRetriever.getScreenOnTimeMs(display);
            if (!mFirstSample) {
                mLayout.setScreenOnDuration(mPowerStats.stats, display,
                        screenOnTimeMs - mLastScreenOnTime[display]);
            }
            mLastScreenOnTime[display] = screenOnTimeMs;

            for (int level = 0; level < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; level++) {
                long brightnessLevelTimeMs =
                        mScreenUsageTimeRetriever.getBrightnessLevelTimeMs(display, level);
                if (!mFirstSample) {
                    mLayout.setBrightnessLevelDuration(mPowerStats.stats, display, level,
                            brightnessLevelTimeMs - mLastBrightnessLevelTime[display][level]);
                }
                mLastBrightnessLevelTime[display][level] = brightnessLevelTimeMs;
            }
            long screenDozeTimeMs = mScreenUsageTimeRetriever.getScreenDozeTimeMs(display);
            if (!mFirstSample) {
                mLayout.setScreenDozeDuration(mPowerStats.stats, display,
                        screenDozeTimeMs - mLastDozeTime[display]);
            }
            mLastDozeTime[display] = screenDozeTimeMs;
        }

        mPowerStats.uidStats.clear();

        mScreenUsageTimeRetriever.retrieveTopActivityTimes((uid, topActivityTimeMs) -> {
            long topActivityDuration = topActivityTimeMs - mLastTopActivityTime.get(uid);
            if (topActivityDuration == 0) {
                return;
            }
            mLastTopActivityTime.put(uid, topActivityTimeMs);

            int mappedUid = mUidResolver.mapUid(uid);
            long[] uidStats = mPowerStats.uidStats.get(mappedUid);
            if (uidStats == null) {
                uidStats = new long[mLayout.getUidStatsArrayLength()];
                mPowerStats.uidStats.put(mappedUid, uidStats);
            }

            mLayout.setUidTopActivityDuration(uidStats,
                    mLayout.getUidTopActivityDuration(uidStats) + topActivityDuration);
        });

        long elapsedRealtime = mClock.elapsedRealtime();
        mPowerStats.durationMs = elapsedRealtime - mLastCollectionTime;
        mLastCollectionTime = elapsedRealtime;

        mFirstSample = false;

        return mPowerStats;
    }

    private void collectEnergyConsumers() {
        int voltageMv = mVoltageSupplier.getAsInt();
        if (voltageMv <= 0) {
            Slog.wtf(TAG, "Unexpected battery voltage (" + voltageMv
                    + " mV) when querying energy consumers");
            return;
        }

        int averageVoltage = mLastVoltageMv != 0 ? (mLastVoltageMv + voltageMv) / 2 : voltageMv;
        mLastVoltageMv = voltageMv;

        long[] energyUws = mConsumedEnergyRetriever.getConsumedEnergyUws(mEnergyConsumerIds);
        if (energyUws == null) {
            return;
        }

        for (int i = energyUws.length - 1; i >= 0; i--) {
            long energyDelta = mLastConsumedEnergyUws[i] != ENERGY_UNSPECIFIED
                    ? energyUws[i] - mLastConsumedEnergyUws[i] : 0;
            if (energyDelta < 0) {
                // Likely, restart of powerstats HAL
                energyDelta = 0;
            }
            mLayout.setConsumedEnergy(mPowerStats.stats, i, uJtoUc(energyDelta, averageVoltage));
            mLastConsumedEnergyUws[i] = energyUws[i];
        }
    }

    @Override
    protected void onUidRemoved(int uid) {
        mLastTopActivityTime.delete(uid);
    }
}
+136 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.power.stats;

import android.os.BatteryStats;
import android.os.PersistableBundle;

/**
 * Captures the positions and lengths of sections of the stats array, such as time-in-state,
 * power usage estimates etc.
 */
public class ScreenPowerStatsLayout extends PowerStatsLayout {
    private static final String EXTRA_DEVICE_SCREEN_COUNT = "dsc";
    private static final String EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION = "dsd";
    private static final String EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS = "dbd";
    private static final String EXTRA_DEVICE_DOZE_DURATION_POSITION = "ddd";
    private static final String EXTRA_UID_FOREGROUND_DURATION = "uf";

    private int mDisplayCount;
    private int mDeviceScreenOnDurationPosition;
    private int[] mDeviceBrightnessDurationPositions;
    private int mDeviceScreenDozeDurationPosition;
    private int mUidTopActivityTimePosition;

    void addDeviceScreenUsageDurationSection(int displayCount) {
        mDisplayCount = displayCount;
        mDeviceScreenOnDurationPosition = addDeviceSection(displayCount, "on");
        mDeviceBrightnessDurationPositions = new int[BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS];
        for (int level = 0; level < BatteryStats.NUM_SCREEN_BRIGHTNESS_BINS; level++) {
            mDeviceBrightnessDurationPositions[level] =
                    addDeviceSection(displayCount, BatteryStats.SCREEN_BRIGHTNESS_NAMES[level]);
        }
        mDeviceScreenDozeDurationPosition = addDeviceSection(displayCount, "doze");
    }

    public int getDisplayCount() {
        return mDisplayCount;
    }

    /**
     * Stores screen-on time for the specified display.
     */
    public void setScreenOnDuration(long[] stats, int display, long durationMs) {
        stats[mDeviceScreenOnDurationPosition + display] = durationMs;
    }

    /**
     * Returns screen-on time for the specified display.
     */
    public long getScreenOnDuration(long[] stats, int display) {
        return stats[mDeviceScreenOnDurationPosition + display];
    }

    /**
     * Stores time at the specified brightness level for the specified display.
     */
    public void setBrightnessLevelDuration(long[] stats, int display, int brightnessLevel,
            long durationMs) {
        stats[mDeviceBrightnessDurationPositions[brightnessLevel] + display] = durationMs;
    }

    /**
     * Returns time at the specified brightness level for the specified display.
     */
    public long getBrightnessLevelDuration(long[] stats, int display, int brightnessLevel) {
        return stats[mDeviceBrightnessDurationPositions[brightnessLevel] + display];
    }

    /**
     * Stores time in the doze (ambient) state for the specified display.
     */
    public void setScreenDozeDuration(long[] stats, int display, long durationMs) {
        stats[mDeviceScreenDozeDurationPosition + display] = durationMs;
    }

    /**
     * Retrieves time in the doze (ambient) state for the specified display.
     */
    public long getScreenDozeDuration(long[] stats, int display) {
        return stats[mDeviceScreenDozeDurationPosition + display];
    }

    void addUidTopActivitiyDuration() {
        mUidTopActivityTimePosition = addUidSection(1, "top");
    }

    /**
     * Stores time the UID spent in the TOP state.
     */
    public void setUidTopActivityDuration(long[] stats, long durationMs) {
        stats[mUidTopActivityTimePosition] = durationMs;
    }

    /**
     * Returns time the UID spent in the TOP state.
     */
    public long getUidTopActivityDuration(long[] stats) {
        return stats[mUidTopActivityTimePosition];
    }

    @Override
    public void toExtras(PersistableBundle extras) {
        super.toExtras(extras);
        extras.putInt(EXTRA_DEVICE_SCREEN_COUNT, mDisplayCount);
        extras.putInt(EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION, mDeviceScreenOnDurationPosition);
        extras.putIntArray(EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS,
                mDeviceBrightnessDurationPositions);
        extras.putInt(EXTRA_DEVICE_DOZE_DURATION_POSITION, mDeviceScreenDozeDurationPosition);
        extras.putInt(EXTRA_UID_FOREGROUND_DURATION, mUidTopActivityTimePosition);
    }

    @Override
    public void fromExtras(PersistableBundle extras) {
        super.fromExtras(extras);
        mDisplayCount = extras.getInt(EXTRA_DEVICE_SCREEN_COUNT, 1);
        mDeviceScreenOnDurationPosition = extras.getInt(EXTRA_DEVICE_SCREEN_ON_DURATION_POSITION);
        mDeviceBrightnessDurationPositions = extras.getIntArray(
                EXTRA_DEVICE_BRIGHTNESS_DURATION_POSITIONS);
        mDeviceScreenDozeDurationPosition = extras.getInt(EXTRA_DEVICE_DOZE_DURATION_POSITION);
        mUidTopActivityTimePosition = extras.getInt(EXTRA_UID_FOREGROUND_DURATION);
    }
}
Loading