diff options
| -rw-r--r-- | services/core/java/com/android/server/power/ThermalManagerService.java | 28 | ||||
| -rw-r--r-- | services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java | 104 |
2 files changed, 124 insertions, 8 deletions
diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java index 74c3a9ec375b..1b7af0574b85 100644 --- a/services/core/java/com/android/server/power/ThermalManagerService.java +++ b/services/core/java/com/android/server/power/ThermalManagerService.java @@ -107,7 +107,8 @@ public class ThermalManagerService extends SystemService { private final AtomicBoolean mHalReady = new AtomicBoolean(); /** Watches temperatures to forecast when throttling will occur */ - private final TemperatureWatcher mTemperatureWatcher = new TemperatureWatcher(); + @VisibleForTesting + final TemperatureWatcher mTemperatureWatcher = new TemperatureWatcher(); /** Invalid throttling status */ private static final int INVALID_THROTTLING = Integer.MIN_VALUE; @@ -1069,16 +1070,19 @@ public class ThermalManagerService extends SystemService { } } - private class TemperatureWatcher { + @VisibleForTesting + class TemperatureWatcher { private final Handler mHandler = BackgroundThread.getHandler(); /** Map of skin temperature sensor name to a corresponding list of samples */ @GuardedBy("mSamples") - private final ArrayMap<String, ArrayList<Sample>> mSamples = new ArrayMap<>(); + @VisibleForTesting + final ArrayMap<String, ArrayList<Sample>> mSamples = new ArrayMap<>(); /** Map of skin temperature sensor name to the corresponding SEVERE temperature threshold */ @GuardedBy("mSamples") - private ArrayMap<String, Float> mSevereThresholds = new ArrayMap<>(); + @VisibleForTesting + ArrayMap<String, Float> mSevereThresholds = new ArrayMap<>(); @GuardedBy("mSamples") private long mLastForecastCallTimeMillis = 0; @@ -1146,7 +1150,8 @@ public class ThermalManagerService extends SystemService { * Calculates the trend using a linear regression. As the samples are degrees Celsius with * associated timestamps in milliseconds, the slope is in degrees Celsius per millisecond. */ - private float getSlopeOf(List<Sample> samples) { + @VisibleForTesting + float getSlopeOf(List<Sample> samples) { long sumTimes = 0L; float sumTemperatures = 0.0f; for (int s = 0; s < samples.size(); ++s) { @@ -1177,7 +1182,8 @@ public class ThermalManagerService extends SystemService { */ private static final float DEGREES_BETWEEN_ZERO_AND_ONE = 30.0f; - private float normalizeTemperature(float temperature, float severeThreshold) { + @VisibleForTesting + float normalizeTemperature(float temperature, float severeThreshold) { synchronized (mSamples) { float zeroNormalized = severeThreshold - DEGREES_BETWEEN_ZERO_AND_ONE; if (temperature <= zeroNormalized) { @@ -1245,7 +1251,15 @@ public class ThermalManagerService extends SystemService { } } - private class Sample { + @VisibleForTesting + // Since Sample is inside an inner class, we can't make it static + // This allows test code to create Sample objects via ThermalManagerService + Sample createSampleForTesting(long time, float temperature) { + return new Sample(time, temperature); + } + + @VisibleForTesting + class Sample { public long time; public float temperature; diff --git a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java index 9e067304fc7f..615fa5c614f0 100644 --- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java @@ -17,6 +17,7 @@ package com.android.server.power; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyInt; @@ -29,6 +30,7 @@ import static org.mockito.Mockito.when; import android.content.Context; import android.hardware.thermal.V2_0.TemperatureThreshold; +import android.hardware.thermal.V2_0.ThrottlingSeverity; import android.os.CoolingDevice; import android.os.IBinder; import android.os.IPowerManager; @@ -91,6 +93,7 @@ public class ThermalManagerServiceTest { private static final int INIT_STATUS = Temperature.THROTTLING_NONE; private ArrayList<Temperature> mTemperatureList = new ArrayList<>(); private ArrayList<CoolingDevice> mCoolingDeviceList = new ArrayList<>(); + private ArrayList<TemperatureThreshold> mTemperatureThresholdList = initializeThresholds(); private Temperature mSkin1 = new Temperature(0, Temperature.TYPE_SKIN, "skin1", INIT_STATUS); @@ -103,6 +106,35 @@ public class ThermalManagerServiceTest { private CoolingDevice mCpu = new CoolingDevice(0, CoolingDevice.TYPE_BATTERY, "cpu"); private CoolingDevice mGpu = new CoolingDevice(0, CoolingDevice.TYPE_BATTERY, "gpu"); + private ArrayList<TemperatureThreshold> initializeThresholds() { + ArrayList<TemperatureThreshold> thresholds = new ArrayList<>(); + + TemperatureThreshold skinThreshold = new TemperatureThreshold(); + skinThreshold.type = Temperature.TYPE_SKIN; + skinThreshold.name = "skin1"; + skinThreshold.hotThrottlingThresholds = new float[7 /*ThrottlingSeverity#len*/]; + for (int i = 0; i < skinThreshold.hotThrottlingThresholds.length; ++i) { + // Sets NONE to 25.0f, SEVERE to 40.0f, and SHUTDOWN to 55.0f + skinThreshold.hotThrottlingThresholds[i] = 25.0f + 5.0f * i; + } + thresholds.add(skinThreshold); + + TemperatureThreshold cpuThreshold = new TemperatureThreshold(); + cpuThreshold.type = Temperature.TYPE_CPU; + cpuThreshold.name = "cpu"; + cpuThreshold.hotThrottlingThresholds = new float[7 /*ThrottlingSeverity#len*/]; + for (int i = 0; i < cpuThreshold.hotThrottlingThresholds.length; ++i) { + if (i == ThrottlingSeverity.SEVERE) { + cpuThreshold.hotThrottlingThresholds[i] = 95.0f; + } else { + cpuThreshold.hotThrottlingThresholds[i] = Float.NaN; + } + } + thresholds.add(cpuThreshold); + + return thresholds; + } + ThermalHalFake() { mTemperatureList.add(mSkin1); mTemperatureList.add(mSkin2); @@ -139,7 +171,14 @@ public class ThermalManagerServiceTest { @Override protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter, int type) { - return new ArrayList<>(); + List<TemperatureThreshold> ret = new ArrayList<>(); + for (TemperatureThreshold threshold : mTemperatureThresholdList) { + if (shouldFilter && type != threshold.type) { + continue; + } + ret.add(threshold); + } + return ret; } @Override @@ -351,4 +390,67 @@ public class ThermalManagerServiceTest { Arrays.asList(mService.mService.getCurrentCoolingDevicesWithType( CoolingDevice.TYPE_CPU))); } + + @Test + public void testTemperatureWatcherUpdateSevereThresholds() throws RemoteException { + ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher; + watcher.mSevereThresholds.erase(); + watcher.updateSevereThresholds(); + assertEquals(1, watcher.mSevereThresholds.size()); + assertEquals("skin1", watcher.mSevereThresholds.keyAt(0)); + Float threshold = watcher.mSevereThresholds.get("skin1"); + assertNotNull(threshold); + assertEquals(40.0f, threshold, 0.0f); + } + + @Test + public void testTemperatureWatcherGetSlopeOf() throws RemoteException { + ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher; + List<ThermalManagerService.TemperatureWatcher.Sample> samples = new ArrayList<>(); + for (int i = 0; i < 30; ++i) { + samples.add(watcher.createSampleForTesting(i, (float) (i / 2 * 2))); + } + assertEquals(1.0f, watcher.getSlopeOf(samples), 0.01f); + } + + @Test + public void testTemperatureWatcherNormalizeTemperature() throws RemoteException { + ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher; + assertEquals(0.5f, watcher.normalizeTemperature(25.0f, 40.0f), 0.0f); + + // Temperatures more than 30 degrees below the SEVERE threshold should be clamped to 0.0f + assertEquals(0.0f, watcher.normalizeTemperature(0.0f, 40.0f), 0.0f); + + // Temperatures above the SEVERE threshold should not be clamped + assertEquals(2.0f, watcher.normalizeTemperature(70.0f, 40.0f), 0.0f); + } + + @Test + public void testTemperatureWatcherGetForecast() throws RemoteException { + ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher; + + ArrayList<ThermalManagerService.TemperatureWatcher.Sample> samples = new ArrayList<>(); + + // Add a single sample + samples.add(watcher.createSampleForTesting(0, 25.0f)); + watcher.mSamples.put("skin1", samples); + + // Because there are not enough samples to compute the linear regression, + // no matter how far ahead we forecast, we should receive the same value + assertEquals(0.5f, watcher.getForecast(0), 0.0f); + assertEquals(0.5f, watcher.getForecast(5), 0.0f); + + // Add some time-series data + for (int i = 1; i < 20; ++i) { + samples.add(0, watcher.createSampleForTesting(1000 * i, 25.0f + 0.5f * i)); + } + + // Now the forecast should vary depending on how far ahead we are trying to predict + assertEquals(0.9f, watcher.getForecast(4), 0.02f); + assertEquals(1.0f, watcher.getForecast(10), 0.02f); + + // If there are no thresholds, then we shouldn't receive a headroom value + watcher.mSevereThresholds.erase(); + assertTrue(Float.isNaN(watcher.getForecast(0))); + } } |