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

Commit 6f1900c5 authored by Dmitri Plotnikov's avatar Dmitri Plotnikov
Browse files

Fix GNSS power stats processor by properly initializing state

Bug: 354311169
Test: atest PowerStatsTestsRavenwood
Flag: EXEMPT_bugfix
Change-Id: Ia49f2ba56776bee43a4b76202ebe80fb122a80c2
parent 1d2177bf
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -1997,6 +1997,8 @@ public abstract class BatteryStats {
                STATE2_VIDEO_ON_FLAG | STATE2_FLASHLIGHT_FLAG | STATE2_CAMERA_FLAG
                | STATE2_GPS_SIGNAL_QUALITY_MASK;

        public static final int GNSS_SIGNAL_QUALITY_NONE = 2;

        @UnsupportedAppUsage
        public int states2;

@@ -2220,7 +2222,7 @@ public abstract class BatteryStats {
            modemRailChargeMah = 0;
            wifiRailChargeMah = 0;
            states = 0;
            states2 = 0;
            states2 = GNSS_SIGNAL_QUALITY_NONE << HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT;
            wakelockTag = null;
            wakeReasonTag = null;
            eventCode = EVENT_NONE;
+1 −2
Original line number Diff line number Diff line
@@ -1167,7 +1167,6 @@ public class BatteryStatsImpl extends BatteryStats {
    private static final int USB_DATA_CONNECTED = 2;
    int mUsbDataState = USB_DATA_UNKNOWN;
    private static final int GPS_SIGNAL_QUALITY_NONE = 2;
    int mGpsSignalQualityBin = -1;
    final StopwatchTimer[] mGpsSignalQualityTimer =
        new StopwatchTimer[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];
@@ -5528,7 +5527,7 @@ public class BatteryStatsImpl extends BatteryStats {
            mHistory.recordStateStopEvent(elapsedRealtimeMs, uptimeMs,
                    HistoryItem.STATE_GPS_ON_FLAG, uid, "gnss");
            mHistory.recordGpsSignalQualityEvent(elapsedRealtimeMs, uptimeMs,
                    GPS_SIGNAL_QUALITY_NONE);
                    HistoryItem.GNSS_SIGNAL_QUALITY_NONE);
            stopAllGpsSignalQualityTimersLocked(-1, elapsedRealtimeMs);
            mGpsSignalQualityBin = -1;
            if (mPowerStatsCollectorEnabled.get(BatteryConsumer.POWER_COMPONENT_GNSS)) {
+6 −0
Original line number Diff line number Diff line
@@ -115,6 +115,12 @@ abstract class BinaryStatePowerStatsProcessor extends PowerStatsProcessor {
                mInitiatingUid = mUidResolver.mapUid(item.eventTag.uid);
            }
        } else {
            if (mInitiatingUid == Process.INVALID_UID) {
                if (item.eventCode == (BatteryStats.HistoryItem.EVENT_STATE_CHANGE
                        | BatteryStats.HistoryItem.EVENT_FLAG_FINISH)) {
                    mInitiatingUid = mUidResolver.mapUid(item.eventTag.uid);
                }
            }
            recordUsageDuration(mPowerStats, mInitiatingUid, item.time);
            mInitiatingUid = Process.INVALID_UID;
            if (!mEnergyConsumerSupported) {
+28 −15
Original line number Diff line number Diff line
@@ -27,15 +27,15 @@ import com.android.internal.os.PowerStats;
import java.util.Arrays;

public class GnssPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
    private int mGnssSignalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN;
    private long mGnssSignalLevelTimestamp;
    private final long[] mGnssSignalDurations =
            new long[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];
    private static final GnssPowerStatsLayout sStatsLayout = new GnssPowerStatsLayout();
    private final UsageBasedPowerEstimator[] mSignalLevelEstimators =
            new UsageBasedPowerEstimator[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];
    private final boolean mUseSignalLevelEstimators;
    private long[] mTmpDeviceStatsArray;
    private int mGnssSignalLevel;
    private long mGnssSignalLevelTimestamp;
    private final long[] mGnssSignalDurations =
            new long[GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS];

    public GnssPowerStatsProcessor(PowerProfile powerProfile, PowerStatsUidResolver uidResolver) {
        super(BatteryConsumer.POWER_COMPONENT_GNSS, uidResolver,
@@ -55,20 +55,33 @@ public class GnssPowerStatsProcessor extends BinaryStatePowerStatsProcessor {
    }

    @Override
    protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
        if ((item.states & BatteryStats.HistoryItem.STATE_GPS_ON_FLAG) == 0) {
    void start(PowerComponentAggregatedPowerStats stats, long timestampMs) {
        super.start(stats, timestampMs);

        mGnssSignalLevelTimestamp = timestampMs;
        mGnssSignalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN;
            return STATE_OFF;
        Arrays.fill(mGnssSignalDurations, 0);
    }

        noteGnssSignalLevel(item);
        return STATE_ON;
    @Override
    protected @BinaryState int getBinaryState(BatteryStats.HistoryItem item) {
        return (item.states & BatteryStats.HistoryItem.STATE_GPS_ON_FLAG) != 0
                ? STATE_ON : STATE_OFF;
    }

    private void noteGnssSignalLevel(BatteryStats.HistoryItem item) {
        int signalLevel = (item.states2 & BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK)
    @Override
    void noteStateChange(PowerComponentAggregatedPowerStats stats, BatteryStats.HistoryItem item) {
        super.noteStateChange(stats, item);

        int signalLevel;
        if ((item.states & BatteryStats.HistoryItem.STATE_GPS_ON_FLAG) != 0) {
            signalLevel = (item.states2 & BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK)
                    >> BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT;
            if (signalLevel >= GnssSignalQuality.NUM_GNSS_SIGNAL_QUALITY_LEVELS) {
                // Default GNSS signal quality to GOOD for the purposes of power attribution
                signalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD;
            }
        } else {
            signalLevel = GnssSignalQuality.GNSS_SIGNAL_QUALITY_UNKNOWN;
        }
        if (signalLevel == mGnssSignalLevel) {
+135 −39
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ public class GnssPowerStatsTest {
    private static final int APP_UID2 = Process.FIRST_APPLICATION_UID + 101;
    private static final int VOLTAGE_MV = 3500;
    private static final int ENERGY_CONSUMER_ID = 777;
    private static final long START_TIME = 10_000_000_000L;

    private final PowerStatsUidResolver mUidResolver = new PowerStatsUidResolver();
    @Mock
@@ -113,11 +114,13 @@ public class GnssPowerStatsTest {
            };

    private MonotonicClock mMonotonicClock;
    private final BatteryStats.HistoryItem mHistoryItem = new BatteryStats.HistoryItem();

    @Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
        mMonotonicClock = new MonotonicClock(0, mStatsRule.getMockClock());
        mMonotonicClock = new MonotonicClock(START_TIME, mStatsRule.getMockClock());
        mHistoryItem.clear();
    }

    @Test
@@ -129,7 +132,6 @@ public class GnssPowerStatsTest {

        PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(
                () -> new GnssPowerStatsProcessor(mStatsRule.getPowerProfile(), mUidResolver));
        stats.start(0);

        GnssPowerStatsCollector collector = new GnssPowerStatsCollector(mInjector);
        collector.addConsumer(
@@ -142,9 +144,11 @@ public class GnssPowerStatsTest {
        stats.noteStateChange(buildHistoryItem(0, true, APP_UID1));

        // Turn the screen off after 2.5 seconds
        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, START_TIME + 2500);
        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND,
                START_TIME + 2500);
        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
                START_TIME + 5000);

        stats.noteStateChange(buildHistoryItem(6000, false, APP_UID1));

@@ -158,7 +162,87 @@ public class GnssPowerStatsTest {
        mStatsRule.setTime(11_000, 11_000);
        collector.collectAndDeliverStats();

        stats.finish(11_000);
        stats.finish(START_TIME + 11_000);

        PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
        BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
        statsLayout.fromExtras(descriptor.extras);

        // scr-on, GNSS-good: 2500 * 100 = 250000 mA-ms = 0.06944 mAh
        // scr-off GNSS=good: 4500 * 100 = 0.12500 mAh
        // scr-off GNSS=poor: 3000 * 1000 = 0.83333 mAh
        // scr-off GNSS-on: 0.12500 + 0.83333 = 0.95833 mAh
        long[] deviceStats = new long[descriptor.statsArrayLength];
        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_ON));
        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
                .isWithin(PRECISION).of(0.06944);

        stats.getDeviceStats(deviceStats, states(POWER_STATE_OTHER, SCREEN_STATE_OTHER));
        assertThat(statsLayout.getDevicePowerEstimate(deviceStats))
                .isWithin(PRECISION).of(0.12500 + 0.83333);

        // UID1 =
        //   scr-on FG: 2500 -> 0.06944 mAh
        //   scr-off BG: 2500/7500 * 0.95833 = 0.31944 mAh
        //   scr-off FGS: 1000/7500 * 0.95833 = 0.12777 mAh
        long[] uidStats = new long[descriptor.uidStatsArrayLength];
        stats.getUidStats(uidStats, APP_UID1,
                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_FOREGROUND));
        assertThat(statsLayout.getUidPowerEstimate(uidStats))
                .isWithin(PRECISION).of(0.06944);

        stats.getUidStats(uidStats, APP_UID1,
                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_BACKGROUND));
        assertThat(statsLayout.getUidPowerEstimate(uidStats))
                .isWithin(PRECISION).of(0.31944);

        stats.getUidStats(uidStats, APP_UID1,
                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_FOREGROUND_SERVICE));
        assertThat(statsLayout.getUidPowerEstimate(uidStats))
                .isWithin(PRECISION).of(0.12777);

        // UID2 =
        //   scr-off cached: 4000/7500 * 0.95833 = 0.51111 mAh
        stats.getUidStats(uidStats, APP_UID2,
                states(POWER_STATE_OTHER, SCREEN_STATE_OTHER, PROCESS_STATE_CACHED));
        assertThat(statsLayout.getUidPowerEstimate(uidStats))
                .isWithin(PRECISION).of(0.51111);

        stats.getUidStats(uidStats, APP_UID2,
                states(POWER_STATE_OTHER, SCREEN_STATE_ON, PROCESS_STATE_CACHED));
        assertThat(statsLayout.getUidPowerEstimate(uidStats))
                .isWithin(PRECISION).of(0);
    }

    @Test
    public void initialStateGnssOn() {
        // ODPM unsupported
        when(mConsumedEnergyRetriever
                .getEnergyConsumerIds(eq((int) EnergyConsumerType.GNSS), any()))
                .thenReturn(new int[0]);

        PowerComponentAggregatedPowerStats stats = createAggregatedPowerStats(
                () -> new GnssPowerStatsProcessor(mStatsRule.getPowerProfile(), mUidResolver));

        stats.noteStateChange(buildHistoryItemInitialStateGpsOn(0));

        // Turn the screen off after 2.5 seconds
        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, START_TIME + 2500);
        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND,
                START_TIME + 2500);
        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
                START_TIME + 5000);

        stats.noteStateChange(buildHistoryItem(6000, false, APP_UID1));

        stats.noteStateChange(buildHistoryItem(7000, true, APP_UID2));
        stats.noteStateChange(buildHistoryItem(7000,
                GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD));
        stats.noteStateChange(buildHistoryItem(8000,
                GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR));
        mStatsRule.setTime(11_000, 11_000);

        stats.finish(START_TIME + 11_000);

        PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
        BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
@@ -224,8 +308,6 @@ public class GnssPowerStatsTest {
                powerStats -> stats.addPowerStats(powerStats, mMonotonicClock.monotonicTime()));
        collector.setEnabled(true);

        stats.start(0);

        // Establish a baseline
        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
                .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 10000));
@@ -234,9 +316,11 @@ public class GnssPowerStatsTest {
        stats.noteStateChange(buildHistoryItem(0, true, APP_UID1));

        // Turn the screen off after 2.5 seconds
        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, 2500);
        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND, 2500);
        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE, 5000);
        stats.setState(STATE_SCREEN, SCREEN_STATE_OTHER, START_TIME + 2500);
        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_BACKGROUND,
                START_TIME + 2500);
        stats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND_SERVICE,
                START_TIME + 5000);

        stats.noteStateChange(buildHistoryItem(6000, false, APP_UID1));

@@ -245,16 +329,14 @@ public class GnssPowerStatsTest {
        collector.collectAndDeliverStats();

        stats.noteStateChange(buildHistoryItem(7000, true, APP_UID2));
        stats.noteStateChange(buildHistoryItem(7000,
                GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD));
        stats.noteStateChange(buildHistoryItem(8000,
                GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR));
        stats.noteStateChange(buildHistoryItem(7000, GnssSignalQuality.GNSS_SIGNAL_QUALITY_GOOD));
        stats.noteStateChange(buildHistoryItem(8000, GnssSignalQuality.GNSS_SIGNAL_QUALITY_POOR));
        mStatsRule.setTime(11_000, 11_000);
        when(mConsumedEnergyRetriever.getConsumedEnergy(new int[]{ENERGY_CONSUMER_ID}))
                .thenReturn(createEnergyConsumerResults(ENERGY_CONSUMER_ID, 3_610_000));
        collector.collectAndDeliverStats();

        stats.finish(11_000);
        stats.finish(START_TIME + 11_000);

        PowerStats.Descriptor descriptor = stats.getPowerStatsDescriptor();
        BinaryStatePowerStatsLayout statsLayout = new BinaryStatePowerStatsLayout();
@@ -313,33 +395,45 @@ public class GnssPowerStatsTest {
                .isWithin(PRECISION).of(0);
    }

    private BatteryStats.HistoryItem buildHistoryItem(int timestamp, boolean stateOn,
    private BatteryStats.HistoryItem buildHistoryItemInitialStateGpsOn(long timestamp) {
        mStatsRule.setTime(timestamp, timestamp);
        mHistoryItem.time = mMonotonicClock.monotonicTime();
        mHistoryItem.states = BatteryStats.HistoryItem.STATE_GPS_ON_FLAG;
        setGnssSignalLevel(BatteryStats.HistoryItem.GNSS_SIGNAL_QUALITY_NONE);
        return mHistoryItem;
    }

    private BatteryStats.HistoryItem buildHistoryItem(long timestamp, boolean stateOn,
            int uid) {
        mStatsRule.setTime(timestamp, timestamp);
        BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem();
        historyItem.time = mMonotonicClock.monotonicTime();
        historyItem.states = stateOn ? BatteryStats.HistoryItem.STATE_GPS_ON_FLAG : 0;
        mHistoryItem.time = mMonotonicClock.monotonicTime();
        mHistoryItem.states = stateOn ? BatteryStats.HistoryItem.STATE_GPS_ON_FLAG : 0;
        if (stateOn) {
            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
            mHistoryItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
                    | BatteryStats.HistoryItem.EVENT_FLAG_START;
        } else {
            historyItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
            mHistoryItem.eventCode = BatteryStats.HistoryItem.EVENT_STATE_CHANGE
                    | BatteryStats.HistoryItem.EVENT_FLAG_FINISH;
        }
        historyItem.eventTag = historyItem.localEventTag;
        historyItem.eventTag.uid = uid;
        historyItem.eventTag.string = "gnss";
        return historyItem;
        mHistoryItem.eventTag = mHistoryItem.localEventTag;
        mHistoryItem.eventTag.uid = uid;
        mHistoryItem.eventTag.string = "gnss";
        return mHistoryItem;
    }

    private BatteryStats.HistoryItem buildHistoryItem(int timestamp, int signalLevel) {
    private BatteryStats.HistoryItem buildHistoryItem(long timestamp, int signalLevel) {
        mStatsRule.setTime(timestamp, timestamp);
        BatteryStats.HistoryItem historyItem = new BatteryStats.HistoryItem();
        historyItem.time = mMonotonicClock.monotonicTime();
        historyItem.states = BatteryStats.HistoryItem.STATE_GPS_ON_FLAG;
        historyItem.states2 =
                signalLevel << BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT;
        return historyItem;
        mHistoryItem.time = mMonotonicClock.monotonicTime();
        setGnssSignalLevel(signalLevel);
        mHistoryItem.eventCode = BatteryStats.HistoryItem.EVENT_NONE;
        mHistoryItem.eventTag = null;
        return mHistoryItem;
    }

    private void setGnssSignalLevel(int signalLevel) {
        mHistoryItem.states2 =
                (mHistoryItem.states2 & ~BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_MASK)
                        | signalLevel << BatteryStats.HistoryItem.STATE2_GPS_SIGNAL_QUALITY_SHIFT;
    }

    private int[] states(int... states) {
@@ -362,12 +456,14 @@ public class GnssPowerStatsTest {
        AggregatedPowerStats aggregatedPowerStats = new AggregatedPowerStats(config);
        PowerComponentAggregatedPowerStats powerComponentStats =
                aggregatedPowerStats.getPowerComponentStats(BatteryConsumer.POWER_COMPONENT_GNSS);
        powerComponentStats.start(0);

        powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, 0);
        powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, 0);
        powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND, 0);
        powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED, 0);
        powerComponentStats.start(START_TIME);

        powerComponentStats.setState(STATE_POWER, POWER_STATE_OTHER, START_TIME);
        powerComponentStats.setState(STATE_SCREEN, SCREEN_STATE_ON, START_TIME);
        powerComponentStats.setUidState(APP_UID1, STATE_PROCESS_STATE, PROCESS_STATE_FOREGROUND,
                START_TIME);
        powerComponentStats.setUidState(APP_UID2, STATE_PROCESS_STATE, PROCESS_STATE_CACHED,
                START_TIME);

        return powerComponentStats;
    }