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

Commit d9c8aad2 authored by Xiang Wang's avatar Xiang Wang
Browse files

Fix temperature handler to always run regardless of sample from throttling callback

* Add unit test to verify that the handler will periodically run
* Add and move debug logs behind DEBUG flag

Test: atest ThermalManagerServiceTest#testGetThermalHeadroom_handlerUpdateTemperatures
Flag: EXEMPT bugfix
Bug: 404630413
Change-Id: I31763ff3ac92db6f2d93c7dd7893a6d4ccbfd717
parent 828c910e
Loading
Loading
Loading
Loading
+37 −10
Original line number Diff line number Diff line
@@ -176,7 +176,9 @@ public class ThermalManagerService extends SystemService {
                    try {
                        final HeadroomCallbackData data;
                        synchronized (mTemperatureWatcher.mSamples) {
                            if (DEBUG) {
                                Slog.d(TAG, "Updating skin threshold: " + threshold);
                            }
                            mTemperatureWatcher.updateTemperatureThresholdLocked(threshold, true);
                            data = mTemperatureWatcher.getHeadroomCallbackDataLocked();
                        }
@@ -454,7 +456,9 @@ public class ThermalManagerService extends SystemService {
                && temperature.getType() == Temperature.TYPE_SKIN) {
            final HeadroomCallbackData data;
            synchronized (mTemperatureWatcher.mSamples) {
                if (DEBUG) {
                    Slog.d(TAG, "Updating new temperature: " + temperature);
                }
                mTemperatureWatcher.updateTemperatureSampleLocked(System.currentTimeMillis(),
                        temperature);
                mTemperatureWatcher.mCachedHeadrooms.clear();
@@ -1878,6 +1882,7 @@ public class ThermalManagerService extends SystemService {
        @VisibleForTesting
        long mInactivityThresholdMillis = INACTIVITY_THRESHOLD_MILLIS;

        @GuardedBy("mSamples")
        private final Handler mHandler = BackgroundThread.getHandler();

        /**
@@ -1900,6 +1905,9 @@ public class ThermalManagerService extends SystemService {
        @GuardedBy("mSamples")
        private long mLastForecastCallTimeMillis = 0;

        private final Runnable mGetAndUpdateTemperatureSamplesRunnable =
                this::getAndUpdateTemperatureSamples;

        void getAndUpdateThresholds() {
            List<TemperatureThreshold> thresholds =
                    mHalWrapper.getTemperatureThresholds(true, Temperature.TYPE_SKIN);
@@ -1930,7 +1938,9 @@ public class ThermalManagerService extends SystemService {
                return;
            }
            if (override) {
                if (DEBUG) {
                    Slog.d(TAG, "Headroom cache cleared on threshold update " + threshold);
                }
                mCachedHeadrooms.clear();
                Arrays.fill(mHeadroomThresholds, Float.NaN);
            }
@@ -1962,7 +1972,7 @@ public class ThermalManagerService extends SystemService {
                        < mInactivityThresholdMillis) {
                    // Trigger this again after a second as long as forecast has been called more
                    // recently than the inactivity timeout
                    mHandler.postDelayed(this::getAndUpdateTemperatureSamples, 1000);
                    mHandler.postDelayed(mGetAndUpdateTemperatureSamplesRunnable, 1000);
                } else {
                    // Otherwise, we've been idle for at least 10 seconds, so we should
                    // shut down
@@ -1974,6 +1984,9 @@ public class ThermalManagerService extends SystemService {
                long now = SystemClock.elapsedRealtime();
                final List<Temperature> temperatures = mHalWrapper.getCurrentTemperatures(true,
                        Temperature.TYPE_SKIN);
                if (DEBUG) {
                    Slog.d(TAG, "Thermal HAL getCurrentTemperatures result: " + temperatures);
                }
                for (Temperature temperature : temperatures) {
                    updateTemperatureSampleLocked(now, temperature);
                }
@@ -2080,10 +2093,16 @@ public class ThermalManagerService extends SystemService {
            }
            synchronized (mSamples) {
                mLastForecastCallTimeMillis = SystemClock.elapsedRealtime();
                if (mSamples.isEmpty()) {
                if (!mHandler.hasCallbacks(mGetAndUpdateTemperatureSamplesRunnable)) {
                    if (DEBUG) {
                        Slog.d(TAG, "No temperature update callback, scheduling one");
                    }
                    getAndUpdateTemperatureSamples();
                } else {
                    if (DEBUG) {
                        Slog.d(TAG, "Temperature update callback already exists");
                    }
                }

                // If somehow things take much longer than expected or there are no temperatures
                // to sample, return early
                if (mSamples.isEmpty()) {
@@ -2103,8 +2122,11 @@ public class ThermalManagerService extends SystemService {
                            Binder.getCallingUid(),
                            FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__SUCCESS,
                            headroom, forecastSeconds);
                    Slog.d(TAG, "Headroom forecast in " + forecastSeconds + "s served from cache: "
                    if (DEBUG) {
                        Slog.d(TAG,
                                "Headroom forecast in " + forecastSeconds + "s served from cache: "
                                        + headroom);
                    }
                    return headroom;
                }

@@ -2133,7 +2155,10 @@ public class ThermalManagerService extends SystemService {
                                    Binder.getCallingUid(),
                                    FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__SUCCESS,
                                    headroom, 0);
                            Slog.d(TAG, "Headroom forecast in 0s served from cache: " + headroom);
                            if (DEBUG) {
                                Slog.d(TAG,
                                        "Headroom forecast in 0s served from cache: " + headroom);
                            }
                            return headroom;
                        }
                        // Don't try to forecast, just use the latest one we have
@@ -2182,7 +2207,9 @@ public class ThermalManagerService extends SystemService {
                    getForecast(DEFAULT_FORECAST_SECONDS),
                    DEFAULT_FORECAST_SECONDS,
                    Arrays.copyOf(mHeadroomThresholds, mHeadroomThresholds.length));
            if (DEBUG) {
                Slog.d(TAG, "New headroom callback data: " + data);
            }
            return data;
        }

+56 −6
Original line number Diff line number Diff line
@@ -51,6 +51,7 @@ import android.os.IThermalStatusListener;
import android.os.PowerManager;
import android.os.RemoteException;
import android.os.Temperature;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;

@@ -78,6 +79,7 @@ import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server
@@ -117,7 +119,8 @@ public class ThermalManagerServiceTest {
     */
    private class ThermalHalFake extends ThermalHalWrapper {
        private static final int INIT_STATUS = Temperature.THROTTLING_NONE;
        private List<Temperature> mTemperatureList = new ArrayList<>();
        private final List<Temperature> mTemperatureList = new ArrayList<>();
        private AtomicInteger mGetCurrentTemperaturesCalled = new AtomicInteger();
        private List<CoolingDevice> mCoolingDeviceList = new ArrayList<>();
        private List<TemperatureThreshold> mTemperatureThresholdList = initializeThresholds();

@@ -173,6 +176,7 @@ public class ThermalManagerServiceTest {
            mTemperatureList.add(mUsbPort);
            mCoolingDeviceList.add(mCpu);
            mCoolingDeviceList.add(mGpu);
            mGetCurrentTemperaturesCalled.set(0);
        }

        void enableForecastSkinTemperature() {
@@ -188,15 +192,25 @@ public class ThermalManagerServiceTest {
            mForecastSkinTemperaturesError = true;
        }

        void updateTemperatureList(Temperature... temperatures) {
            synchronized (mTemperatureList) {
                mTemperatureList.clear();
                mTemperatureList.addAll(Arrays.asList(temperatures));
            }
        }

        @Override
        protected List<Temperature> getCurrentTemperatures(boolean shouldFilter, int type) {
            List<Temperature> ret = new ArrayList<>();
            synchronized (mTemperatureList) {
                mGetCurrentTemperaturesCalled.incrementAndGet();
                for (Temperature temperature : mTemperatureList) {
                    if (shouldFilter && type != temperature.getType()) {
                        continue;
                    }
                    ret.add(temperature);
                }
            }
            return ret;
        }

@@ -407,7 +421,7 @@ public class ThermalManagerServiceTest {
        Thread.sleep(CALLBACK_TIMEOUT_MILLI_SEC);
        resetListenerMock();
        int status = Temperature.THROTTLING_SEVERE;
        mFakeHal.mTemperatureList = new ArrayList<>();
        mFakeHal.updateTemperatureList();

        // Should not notify on non-skin type
        Temperature newBattery = new Temperature(37, Temperature.TYPE_BATTERY, "batt", status);
@@ -536,6 +550,42 @@ public class ThermalManagerServiceTest {
                ThermalManagerService.MAX_FORECAST_SEC + 1)));
    }

    @Test
    @DisableFlags({Flags.FLAG_ALLOW_THERMAL_HAL_SKIN_FORECAST})
    public void testGetThermalHeadroom_handlerUpdateTemperatures()
            throws RemoteException, InterruptedException {
        // test that handler will at least enqueue one message to periodically read temperatures
        // even if there is sample seeded from HAL temperature callback
        String temperatureName = "skin1";
        Temperature temperature = new Temperature(100, Temperature.TYPE_SKIN, temperatureName,
                Temperature.THROTTLING_NONE);
        mFakeHal.mCallback.onTemperatureChanged(temperature);
        float headroom = mService.mService.getThermalHeadroom(0);
        // the callback temperature 100C (headroom > 1.0f) sample should have been appended by the
        // immediately scheduled fake HAL current temperatures read (mSkin1, mSkin2), and because
        // there are less samples for prediction, the latest temperature mSkin1 is used to calculate
        // headroom (mSkin2 has no threshold), which is 0.6f (28C vs threshold 40C).
        assertEquals(0.6f, headroom, 0.01f);
        // one called by service onActivityManagerReady, one called by handler on headroom call
        assertEquals(2, mFakeHal.mGetCurrentTemperaturesCalled.get());
        // periodic read should update the samples history, so the headroom should increase 0.1f
        // as current temperature goes up by 3C every 1100ms.
        for (int i = 1; i < 5; i++) {
            Temperature newTemperature = new Temperature(mFakeHal.mSkin1.getValue() + 3 * i,
                    Temperature.TYPE_SKIN,
                    temperatureName,
                    Temperature.THROTTLING_NONE);
            mFakeHal.updateTemperatureList(newTemperature);
            // wait for handler to update temperature
            Thread.sleep(1100);
            // assert that only one callback was scheduled to query HAL when making multiple
            // headroom calls
            assertEquals(2 + i, mFakeHal.mGetCurrentTemperaturesCalled.get());
            headroom = mService.mService.getThermalHeadroom(0);
            assertEquals(0.6f + 0.1f * i, headroom, 0.01f);
        }
    }

    @Test
    @EnableFlags({Flags.FLAG_ALLOW_THERMAL_HAL_SKIN_FORECAST})
    public void testGetThermalHeadroom_halForecast() throws RemoteException {