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

Commit 9c841db3 authored by Michael Wachenschwanz's avatar Michael Wachenschwanz
Browse files

Rate limit BatteryStats' ModemActivityInfo querying

Frequent mobile radio power state changes results in frequent
ModemActivityInfo queries. To limit the unnecessary churn, a Modem
update due to mobile radio power state change will only occur some
amount of time after the previous update. Modem updates due to other
signals (plug/unplug events, battery percent change, etc) will be
unaffected.

Fixes: 239715259
Test: atest BatteryStatsNoteTest
Merged-In: Ie0af28dbc2ad4836ef6437c0656dc20eec2285ea
Change-Id: Ib3ee18113ac381b8912bb58974b5d41ceefb7131
parent 5cc7eec0
Loading
Loading
Loading
Loading
+16 −0
Original line number Original line Diff line number Diff line
@@ -1326,6 +1326,13 @@ public class BatteryStatsImpl extends BatteryStats {
    LongSamplingCounter mMobileRadioActiveUnknownTime;
    LongSamplingCounter mMobileRadioActiveUnknownTime;
    LongSamplingCounter mMobileRadioActiveUnknownCount;
    LongSamplingCounter mMobileRadioActiveUnknownCount;
    /**
     * The soonest the Mobile Radio stats can be updated due to a mobile radio power state change
     * after it was last updated.
     */
    @VisibleForTesting
    protected static final long MOBILE_RADIO_POWER_STATE_UPDATE_FREQ_MS = 1000 * 60 * 10;
    int mWifiRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
    int mWifiRadioPowerState = DataConnectionRealTimeInfo.DC_POWER_STATE_LOW;
    @GuardedBy("this")
    @GuardedBy("this")
@@ -6260,6 +6267,15 @@ public class BatteryStatsImpl extends BatteryStats {
            } else {
            } else {
                mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs);
                mMobileRadioActiveTimer.stopRunningLocked(realElapsedRealtimeMs);
                mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs);
                mMobileRadioActivePerAppTimer.stopRunningLocked(realElapsedRealtimeMs);
                if (mLastModemActivityInfo != null) {
                    if (elapsedRealtimeMs < mLastModemActivityInfo.getTimestampMillis()
                            + MOBILE_RADIO_POWER_STATE_UPDATE_FREQ_MS) {
                        // Modem Activity info has been collected recently, don't bother
                        // triggering another update.
                        return false;
                    }
                }
                // Tell the caller to collect radio network/power stats.
                // Tell the caller to collect radio network/power stats.
                return true;
                return true;
            }
            }
+111 −0
Original line number Original line Diff line number Diff line
@@ -47,6 +47,7 @@ import android.telephony.DataConnectionRealTimeInfo;
import android.telephony.ModemActivityInfo;
import android.telephony.ModemActivityInfo;
import android.telephony.ServiceState;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.util.MutableInt;
import android.util.MutableInt;
import android.util.SparseIntArray;
import android.util.SparseIntArray;
import android.util.SparseLongArray;
import android.util.SparseLongArray;
@@ -82,6 +83,7 @@ import java.util.function.IntConsumer;
 *      com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
 *      com.android.frameworks.coretests/androidx.test.runner.AndroidJUnitRunner
 */
 */
public class BatteryStatsNoteTest extends TestCase {
public class BatteryStatsNoteTest extends TestCase {
    private static final String TAG = BatteryStatsNoteTest.class.getSimpleName();


    private static final int UID = 10500;
    private static final int UID = 10500;
    private static final int ISOLATED_APP_ID = Process.FIRST_ISOLATED_UID + 23;
    private static final int ISOLATED_APP_ID = Process.FIRST_ISOLATED_UID + 23;
@@ -2031,6 +2033,115 @@ public class BatteryStatsNoteTest extends TestCase {
                noRadioProcFlags, lastProcStateChangeFlags.value);
                noRadioProcFlags, lastProcStateChangeFlags.value);
    }
    }




    @SmallTest
    public void testNoteMobileRadioPowerStateLocked() {
        long curr;
        boolean update;
        final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
        final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
        bi.setOnBatteryInternal(true);

        // Note mobile radio is on.
        curr = 1000L * (clocks.realtime = clocks.uptime = 1001);
        bi.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH, curr,
                UID);

        // Note mobile radio is still on.
        curr = 1000L * (clocks.realtime = clocks.uptime = 2001);
        update = bi.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
                curr, UID);
        assertFalse(
                "noteMobileRadioPowerStateLocked should not request an update when the power "
                        + "state does not change from HIGH.",
                update);

        // Note mobile radio is off.
        curr = 1000L * (clocks.realtime = clocks.uptime = 3001);
        update = bi.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
                curr, UID);
        assertTrue(
                "noteMobileRadioPowerStateLocked should request an update when the power state "
                        + "changes from HIGH to LOW.",
                update);

        // Note mobile radio is still off.
        curr = 1000L * (clocks.realtime = clocks.uptime = 4001);
        update = bi.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
                curr, UID);
        assertFalse(
                "noteMobileRadioPowerStateLocked should not request an update when the power "
                        + "state does not change from LOW.",
                update);

        // Note mobile radio is on.
        curr = 1000L * (clocks.realtime = clocks.uptime = 5001);
        update = bi.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH,
                curr, UID);
        assertFalse(
                "noteMobileRadioPowerStateLocked should not request an update when the power "
                        + "state changes from LOW to HIGH.",
                update);
    }

    @SmallTest
    public void testNoteMobileRadioPowerStateLocked_rateLimited() {
        long curr;
        boolean update;
        final MockClock clocks = new MockClock(); // holds realtime and uptime in ms
        final MockBatteryStatsImpl bi = new MockBatteryStatsImpl(clocks);
        bi.setPowerProfile(mock(PowerProfile.class));

        final int txLevelCount = CellSignalStrength.getNumSignalStrengthLevels();
        final ModemActivityInfo mai = new ModemActivityInfo(0L, 0L, 0L, new int[txLevelCount], 0L);

        final long rateLimit = bi.getMobileRadioPowerStateUpdateRateLimit();
        if (rateLimit < 0) {
            Log.w(TAG, "Skipping testNoteMobileRadioPowerStateLocked_rateLimited, rateLimit = "
                    + rateLimit);
            return;
        }
        bi.setOnBatteryInternal(true);

        // Note mobile radio is on.
        curr = 1000L * (clocks.realtime = clocks.uptime = 1001);
        bi.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH, curr,
                UID);

        clocks.realtime = clocks.uptime = 2001;
        mai.setTimestamp(clocks.realtime);
        bi.noteModemControllerActivity(mai, POWER_DATA_UNAVAILABLE,
                clocks.realtime, clocks.uptime, mNetworkStatsManager);

        // Note mobile radio is off within the rate limit duration.
        clocks.realtime = clocks.uptime = clocks.realtime + (long) (rateLimit * 0.7);
        curr = 1000L * clocks.realtime;
        update = bi.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
                curr, UID);
        assertFalse(
                "noteMobileRadioPowerStateLocked should not request an update when the power "
                        + "state so soon after a noteModemControllerActivity",
                update);

        // Note mobile radio is on.
        clocks.realtime = clocks.uptime = clocks.realtime + (long) (rateLimit * 0.7);
        curr = 1000L * clocks.realtime;
        bi.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_HIGH, curr,
                UID);

        // Note mobile radio is off much later
        clocks.realtime = clocks.uptime = clocks.realtime + rateLimit;
        curr = 1000L * clocks.realtime;
        update = bi.noteMobileRadioPowerStateLocked(DataConnectionRealTimeInfo.DC_POWER_STATE_LOW,
                curr, UID);
        assertTrue(
                "noteMobileRadioPowerStateLocked should request an update when the power state "
                        + "changes from HIGH to LOW much later after a "
                        + "noteModemControllerActivity.",
                update);
    }

    private void setFgState(int uid, boolean fgOn, MockBatteryStatsImpl bi) {
    private void setFgState(int uid, boolean fgOn, MockBatteryStatsImpl bi) {
        // Note that noteUidProcessStateLocked uses ActivityManager process states.
        // Note that noteUidProcessStateLocked uses ActivityManager process states.
        if (fgOn) {
        if (fgOn) {
+4 −0
Original line number Original line Diff line number Diff line
@@ -114,6 +114,10 @@ public class MockBatteryStatsImpl extends BatteryStatsImpl {
        return getUidStatsLocked(uid).mOnBatteryScreenOffBackgroundTimeBase;
        return getUidStatsLocked(uid).mOnBatteryScreenOffBackgroundTimeBase;
    }
    }


    public long getMobileRadioPowerStateUpdateRateLimit() {
        return MOBILE_RADIO_POWER_STATE_UPDATE_FREQ_MS;
    }

    public MockBatteryStatsImpl setNetworkStats(NetworkStats networkStats) {
    public MockBatteryStatsImpl setNetworkStats(NetworkStats networkStats) {
        mNetworkStats = networkStats;
        mNetworkStats = networkStats;
        return this;
        return this;