Loading services/core/java/com/android/server/power/ThermalManagerService.java +21 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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; Loading services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java +103 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); Loading @@ -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); Loading Loading @@ -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 Loading Loading @@ -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))); } } Loading
services/core/java/com/android/server/power/ThermalManagerService.java +21 −7 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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; Loading
services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java +103 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); Loading @@ -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); Loading Loading @@ -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 Loading Loading @@ -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))); } }