Loading core/java/android/os/flags.aconfig +8 −0 Original line number Diff line number Diff line Loading @@ -109,6 +109,14 @@ flag { is_exported: true } flag { name: "allow_thermal_hal_skin_forecast" is_exported: true namespace: "game" description: "Enable thermal HAL skin temperature forecast to be used by headroom API" bug: "383211885" } flag { name: "allow_thermal_headroom_thresholds" is_exported: true Loading services/core/java/com/android/server/power/ThermalManagerService.java +202 −111 Original line number Diff line number Diff line Loading @@ -155,6 +155,9 @@ public class ThermalManagerService extends SystemService { @VisibleForTesting final TemperatureWatcher mTemperatureWatcher; @VisibleForTesting final AtomicBoolean mIsHalSkinForecastSupported = new AtomicBoolean(false); private final ThermalHalWrapper.WrapperThermalChangedCallback mWrapperCallback = new ThermalHalWrapper.WrapperThermalChangedCallback() { @Override Loading Loading @@ -254,6 +257,18 @@ public class ThermalManagerService extends SystemService { } onTemperatureMapChangedLocked(); mTemperatureWatcher.getAndUpdateThresholds(); // we only check forecast if a single SKIN sensor threshold is reported synchronized (mTemperatureWatcher.mSamples) { if (mTemperatureWatcher.mSevereThresholds.size() == 1) { try { mIsHalSkinForecastSupported.set( Flags.allowThermalHalSkinForecast() && !Float.isNaN(mHalWrapper.forecastSkinTemperature(10))); } catch (UnsupportedOperationException e) { Slog.i(TAG, "Thermal HAL does not support forecastSkinTemperature"); } } } mHalReady.set(true); } } Loading Loading @@ -1092,6 +1107,8 @@ public class ThermalManagerService extends SystemService { protected abstract List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter, int type); protected abstract float forecastSkinTemperature(int forecastSeconds); protected abstract boolean connectToHal(); protected abstract void dump(PrintWriter pw, String prefix); Loading Loading @@ -1124,8 +1141,16 @@ public class ThermalManagerService extends SystemService { @VisibleForTesting static class ThermalHalAidlWrapper extends ThermalHalWrapper implements IBinder.DeathRecipient { /* Proxy object for the Thermal HAL AIDL service. */ @GuardedBy("mHalLock") private IThermal mInstance = null; private IThermal getHalInstance() { synchronized (mHalLock) { return mInstance; } } /** Callback for Thermal HAL AIDL. */ private final IThermalChangedCallback mThermalCallbackAidl = new IThermalChangedCallback.Stub() { Loading Loading @@ -1169,15 +1194,15 @@ public class ThermalManagerService extends SystemService { @Override protected List<Temperature> getCurrentTemperatures(boolean shouldFilter, int type) { synchronized (mHalLock) { final IThermal instance = getHalInstance(); final List<Temperature> ret = new ArrayList<>(); if (mInstance == null) { if (instance == null) { return ret; } try { final android.hardware.thermal.Temperature[] halRet = shouldFilter ? mInstance.getTemperaturesWithType(type) : mInstance.getTemperatures(); shouldFilter ? instance.getTemperaturesWithType(type) : instance.getTemperatures(); if (halRet == null) { return ret; } Loading @@ -1196,24 +1221,25 @@ public class ThermalManagerService extends SystemService { Slog.e(TAG, "Couldn't getCurrentCoolingDevices due to invalid status", e); } catch (RemoteException e) { Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting", e); connectToHal(); synchronized (mHalLock) { connectToHalIfNeededLocked(instance); } return ret; } return ret; } @Override protected List<CoolingDevice> getCurrentCoolingDevices(boolean shouldFilter, int type) { synchronized (mHalLock) { final IThermal instance = getHalInstance(); final List<CoolingDevice> ret = new ArrayList<>(); if (mInstance == null) { if (instance == null) { return ret; } try { final android.hardware.thermal.CoolingDevice[] halRet = shouldFilter ? mInstance.getCoolingDevicesWithType(type) : mInstance.getCoolingDevices(); ? instance.getCoolingDevicesWithType(type) : instance.getCoolingDevices(); if (halRet == null) { return ret; } Loading @@ -1231,25 +1257,26 @@ public class ThermalManagerService extends SystemService { Slog.e(TAG, "Couldn't getCurrentCoolingDevices due to invalid status", e); } catch (RemoteException e) { Slog.e(TAG, "Couldn't getCurrentCoolingDevices, reconnecting", e); connectToHal(); synchronized (mHalLock) { connectToHalIfNeededLocked(instance); } return ret; } return ret; } @Override @NonNull protected List<TemperatureThreshold> getTemperatureThresholds( boolean shouldFilter, int type) { synchronized (mHalLock) { final IThermal instance = getHalInstance(); final List<TemperatureThreshold> ret = new ArrayList<>(); if (mInstance == null) { if (instance == null) { return ret; } try { final TemperatureThreshold[] halRet = shouldFilter ? mInstance.getTemperatureThresholdsWithType(type) : mInstance.getTemperatureThresholds(); shouldFilter ? instance.getTemperatureThresholdsWithType(type) : instance.getTemperatureThresholds(); if (halRet == null) { return ret; } Loading @@ -1262,25 +1289,58 @@ public class ThermalManagerService extends SystemService { Slog.e(TAG, "Couldn't getTemperatureThresholds due to invalid status", e); } catch (RemoteException e) { Slog.e(TAG, "Couldn't getTemperatureThresholds, reconnecting...", e); connectToHal(); synchronized (mHalLock) { connectToHalIfNeededLocked(instance); } } return ret; } @Override protected float forecastSkinTemperature(int forecastSeconds) { final IThermal instance = getHalInstance(); if (instance == null) { return Float.NaN; } try { return instance.forecastSkinTemperature(forecastSeconds); } catch (RemoteException e) { Slog.e(TAG, "Couldn't forecastSkinTemperature, reconnecting...", e); synchronized (mHalLock) { connectToHalIfNeededLocked(instance); } } return Float.NaN; } @Override protected boolean connectToHal() { synchronized (mHalLock) { return connectToHalIfNeededLocked(mInstance); } } @GuardedBy("mHalLock") protected boolean connectToHalIfNeededLocked(IThermal instance) { if (instance != mInstance) { // instance has been updated since last used return true; } IBinder binder = Binder.allowBlocking(ServiceManager.waitForDeclaredService( IThermal.DESCRIPTOR + "/default")); initProxyAndRegisterCallback(binder); } initProxyAndRegisterCallbackLocked(binder); return mInstance != null; } @VisibleForTesting void initProxyAndRegisterCallback(IBinder binder) { synchronized (mHalLock) { initProxyAndRegisterCallbackLocked(binder); } } @GuardedBy("mHalLock") protected void initProxyAndRegisterCallbackLocked(IBinder binder) { if (binder != null) { mInstance = IThermal.Stub.asInterface(binder); try { Loading @@ -1298,14 +1358,6 @@ public class ThermalManagerService extends SystemService { connectToHal(); return; } registerThermalChangedCallback(); } } } } @VisibleForTesting void registerThermalChangedCallback() { try { mInstance.registerThermalChangedCallback(mThermalCallbackAidl); } catch (IllegalArgumentException | IllegalStateException e) { Loading @@ -1316,6 +1368,8 @@ public class ThermalManagerService extends SystemService { connectToHal(); } } } } @Override protected void dump(PrintWriter pw, String prefix) { Loading Loading @@ -1444,6 +1498,11 @@ public class ThermalManagerService extends SystemService { } } @Override protected float forecastSkinTemperature(int forecastSeconds) { throw new UnsupportedOperationException("Not supported in Thermal HAL 1.0"); } @Override protected void dump(PrintWriter pw, String prefix) { synchronized (mHalLock) { Loading Loading @@ -1582,6 +1641,11 @@ public class ThermalManagerService extends SystemService { } } @Override protected float forecastSkinTemperature(int forecastSeconds) { throw new UnsupportedOperationException("Not supported in Thermal HAL 1.1"); } @Override protected void dump(PrintWriter pw, String prefix) { synchronized (mHalLock) { Loading Loading @@ -1748,6 +1812,11 @@ public class ThermalManagerService extends SystemService { } } @Override protected float forecastSkinTemperature(int forecastSeconds) { throw new UnsupportedOperationException("Not supported in Thermal HAL 2.0"); } @Override protected void dump(PrintWriter pw, String prefix) { synchronized (mHalLock) { Loading Loading @@ -1976,6 +2045,39 @@ public class ThermalManagerService extends SystemService { private static final int MINIMUM_SAMPLE_COUNT = 3; float getForecast(int forecastSeconds) { synchronized (mSamples) { // If we don't have any thresholds, we can't normalize the temperatures, // so return early if (mSevereThresholds.isEmpty()) { Slog.e(TAG, "No temperature thresholds found"); FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED, Binder.getCallingUid(), THERMAL_HEADROOM_CALLED__API_STATUS__NO_TEMPERATURE_THRESHOLD, Float.NaN, forecastSeconds); return Float.NaN; } } if (mIsHalSkinForecastSupported.get()) { float threshold = -1f; synchronized (mSamples) { // we only do forecast if a single SKIN sensor threshold is reported if (mSevereThresholds.size() == 1) { threshold = mSevereThresholds.valueAt(0); } } if (threshold > 0) { try { final float forecastTemperature = mHalWrapper.forecastSkinTemperature(forecastSeconds); return normalizeTemperature(forecastTemperature, threshold); } catch (UnsupportedOperationException e) { Slog.wtf(TAG, "forecastSkinTemperature returns unsupported"); } catch (Exception e) { Slog.e(TAG, "forecastSkinTemperature fails"); } return Float.NaN; } } synchronized (mSamples) { mLastForecastCallTimeMillis = SystemClock.elapsedRealtime(); if (mSamples.isEmpty()) { Loading @@ -1993,17 +2095,6 @@ public class ThermalManagerService extends SystemService { return Float.NaN; } // If we don't have any thresholds, we can't normalize the temperatures, // so return early if (mSevereThresholds.isEmpty()) { Slog.e(TAG, "No temperature thresholds found"); FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED, Binder.getCallingUid(), THERMAL_HEADROOM_CALLED__API_STATUS__NO_TEMPERATURE_THRESHOLD, Float.NaN, forecastSeconds); return Float.NaN; } if (mCachedHeadrooms.contains(forecastSeconds)) { // TODO(b/360486877): replace with metrics Slog.d(TAG, Loading services/tests/mockingservicestests/src/com/android/server/power/ThermalManagerServiceMockingTest.java +10 −0 Original line number Diff line number Diff line Loading @@ -573,4 +573,14 @@ public class ThermalManagerServiceMockingTest { assertNotNull(ret); assertEquals(0, ret.size()); } @Test public void forecastSkinTemperature() throws RemoteException { Mockito.when(mAidlHalMock.forecastSkinTemperature(Mockito.anyInt())).thenReturn( 0.55f ); float forecast = mAidlWrapper.forecastSkinTemperature(10); Mockito.verify(mAidlHalMock, Mockito.times(1)).forecastSkinTemperature(10); assertEquals(0.55f, forecast, 0.01f); } } services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java +118 −6 Original line number Diff line number Diff line Loading @@ -118,7 +118,6 @@ public class ThermalManagerServiceTest { private class ThermalHalFake extends ThermalHalWrapper { private static final int INIT_STATUS = Temperature.THROTTLING_NONE; private List<Temperature> mTemperatureList = new ArrayList<>(); private List<Temperature> mOverrideTemperatures = null; private List<CoolingDevice> mCoolingDeviceList = new ArrayList<>(); private List<TemperatureThreshold> mTemperatureThresholdList = initializeThresholds(); Loading @@ -132,6 +131,9 @@ public class ThermalManagerServiceTest { INIT_STATUS); private CoolingDevice mCpu = new CoolingDevice(40, CoolingDevice.TYPE_BATTERY, "cpu"); private CoolingDevice mGpu = new CoolingDevice(43, CoolingDevice.TYPE_BATTERY, "gpu"); private Map<Integer, Float> mForecastSkinTemperatures = null; private int mForecastSkinTemperaturesCalled = 0; private boolean mForecastSkinTemperaturesError = false; private List<TemperatureThreshold> initializeThresholds() { ArrayList<TemperatureThreshold> thresholds = new ArrayList<>(); Loading Loading @@ -173,12 +175,17 @@ public class ThermalManagerServiceTest { mCoolingDeviceList.add(mGpu); } void setOverrideTemperatures(List<Temperature> temperatures) { mOverrideTemperatures = temperatures; void enableForecastSkinTemperature() { mForecastSkinTemperatures = Map.of(0, 22.0f, 10, 25.0f, 20, 28.0f, 30, 31.0f, 40, 34.0f, 50, 37.0f, 60, 40.0f); } void resetOverrideTemperatures() { mOverrideTemperatures = null; void disableForecastSkinTemperature() { mForecastSkinTemperatures = null; } void failForecastSkinTemperature() { mForecastSkinTemperaturesError = true; } @Override Loading Loading @@ -218,6 +225,18 @@ public class ThermalManagerServiceTest { return ret; } @Override protected float forecastSkinTemperature(int forecastSeconds) { mForecastSkinTemperaturesCalled++; if (mForecastSkinTemperaturesError) { throw new RuntimeException(); } if (mForecastSkinTemperatures == null) { throw new UnsupportedOperationException(); } return mForecastSkinTemperatures.get(forecastSeconds); } @Override protected boolean connectToHal() { return true; Loading Loading @@ -388,7 +407,7 @@ public class ThermalManagerServiceTest { Thread.sleep(CALLBACK_TIMEOUT_MILLI_SEC); resetListenerMock(); int status = Temperature.THROTTLING_SEVERE; mFakeHal.setOverrideTemperatures(new ArrayList<>()); mFakeHal.mTemperatureList = new ArrayList<>(); // Should not notify on non-skin type Temperature newBattery = new Temperature(37, Temperature.TYPE_BATTERY, "batt", status); Loading Loading @@ -517,6 +536,99 @@ public class ThermalManagerServiceTest { ThermalManagerService.MAX_FORECAST_SEC + 1))); } @Test @EnableFlags({Flags.FLAG_ALLOW_THERMAL_HAL_SKIN_FORECAST}) public void testGetThermalHeadroom_halForecast() throws RemoteException { mFakeHal.mForecastSkinTemperaturesCalled = 0; mFakeHal.enableForecastSkinTemperature(); mService = new ThermalManagerService(mContext, mFakeHal); mService.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY); assertTrue(mService.mIsHalSkinForecastSupported.get()); assertEquals(1, mFakeHal.mForecastSkinTemperaturesCalled); mFakeHal.mForecastSkinTemperaturesCalled = 0; assertEquals(1.0f, mService.mService.getThermalHeadroom(60), 0.01f); assertEquals(0.9f, mService.mService.getThermalHeadroom(50), 0.01f); assertEquals(0.8f, mService.mService.getThermalHeadroom(40), 0.01f); assertEquals(0.7f, mService.mService.getThermalHeadroom(30), 0.01f); assertEquals(0.6f, mService.mService.getThermalHeadroom(20), 0.01f); assertEquals(0.5f, mService.mService.getThermalHeadroom(10), 0.01f); assertEquals(0.4f, mService.mService.getThermalHeadroom(0), 0.01f); assertEquals(7, mFakeHal.mForecastSkinTemperaturesCalled); } @Test @EnableFlags({Flags.FLAG_ALLOW_THERMAL_HAL_SKIN_FORECAST}) public void testGetThermalHeadroom_halForecast_disabledOnMultiThresholds() throws RemoteException { mFakeHal.mForecastSkinTemperaturesCalled = 0; List<TemperatureThreshold> thresholds = mFakeHal.initializeThresholds(); TemperatureThreshold skinThreshold = new TemperatureThreshold(); skinThreshold.type = Temperature.TYPE_SKIN; skinThreshold.name = "skin2"; skinThreshold.hotThrottlingThresholds = new float[7 /*ThrottlingSeverity#len*/]; skinThreshold.coldThrottlingThresholds = new float[7 /*ThrottlingSeverity#len*/]; for (int i = 0; i < skinThreshold.hotThrottlingThresholds.length; ++i) { // Sets NONE to 45.0f, SEVERE to 60.0f, and SHUTDOWN to 75.0f skinThreshold.hotThrottlingThresholds[i] = 45.0f + 5.0f * i; } thresholds.add(skinThreshold); mFakeHal.mTemperatureThresholdList = thresholds; mFakeHal.enableForecastSkinTemperature(); mService = new ThermalManagerService(mContext, mFakeHal); mService.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY); assertFalse("HAL skin forecast should be disabled on multiple SKIN thresholds", mService.mIsHalSkinForecastSupported.get()); mService.mService.getThermalHeadroom(10); assertEquals(0, mFakeHal.mForecastSkinTemperaturesCalled); } @Test @EnableFlags({Flags.FLAG_ALLOW_THERMAL_HAL_SKIN_FORECAST, Flags.FLAG_ALLOW_THERMAL_THRESHOLDS_CALLBACK}) public void testGetThermalHeadroom_halForecast_disabledOnMultiThresholdsCallback() throws RemoteException { mFakeHal.mForecastSkinTemperaturesCalled = 0; mFakeHal.enableForecastSkinTemperature(); mService = new ThermalManagerService(mContext, mFakeHal); mService.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY); assertTrue(mService.mIsHalSkinForecastSupported.get()); assertEquals(1, mFakeHal.mForecastSkinTemperaturesCalled); mFakeHal.mForecastSkinTemperaturesCalled = 0; TemperatureThreshold newThreshold = new TemperatureThreshold(); newThreshold.name = "skin2"; newThreshold.type = Temperature.TYPE_SKIN; newThreshold.hotThrottlingThresholds = new float[]{ Float.NaN, 43.0f, 46.0f, 49.0f, Float.NaN, Float.NaN, Float.NaN }; mFakeHal.mCallback.onThresholdChanged(newThreshold); mService.mService.getThermalHeadroom(10); assertEquals(0, mFakeHal.mForecastSkinTemperaturesCalled); } @Test @EnableFlags({Flags.FLAG_ALLOW_THERMAL_HAL_SKIN_FORECAST}) public void testGetThermalHeadroom_halForecast_errorOnHal() throws RemoteException { mFakeHal.mForecastSkinTemperaturesCalled = 0; mFakeHal.enableForecastSkinTemperature(); mService = new ThermalManagerService(mContext, mFakeHal); mService.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY); assertTrue(mService.mIsHalSkinForecastSupported.get()); assertEquals(1, mFakeHal.mForecastSkinTemperaturesCalled); mFakeHal.mForecastSkinTemperaturesCalled = 0; mFakeHal.disableForecastSkinTemperature(); assertTrue(Float.isNaN(mService.mService.getThermalHeadroom(10))); assertEquals(1, mFakeHal.mForecastSkinTemperaturesCalled); mFakeHal.enableForecastSkinTemperature(); assertFalse(Float.isNaN(mService.mService.getThermalHeadroom(10))); assertEquals(2, mFakeHal.mForecastSkinTemperaturesCalled); mFakeHal.failForecastSkinTemperature(); assertTrue(Float.isNaN(mService.mService.getThermalHeadroom(10))); assertEquals(3, mFakeHal.mForecastSkinTemperaturesCalled); } @Test @EnableFlags({Flags.FLAG_ALLOW_THERMAL_THRESHOLDS_CALLBACK, Flags.FLAG_ALLOW_THERMAL_HEADROOM_THRESHOLDS}) Loading Loading
core/java/android/os/flags.aconfig +8 −0 Original line number Diff line number Diff line Loading @@ -109,6 +109,14 @@ flag { is_exported: true } flag { name: "allow_thermal_hal_skin_forecast" is_exported: true namespace: "game" description: "Enable thermal HAL skin temperature forecast to be used by headroom API" bug: "383211885" } flag { name: "allow_thermal_headroom_thresholds" is_exported: true Loading
services/core/java/com/android/server/power/ThermalManagerService.java +202 −111 Original line number Diff line number Diff line Loading @@ -155,6 +155,9 @@ public class ThermalManagerService extends SystemService { @VisibleForTesting final TemperatureWatcher mTemperatureWatcher; @VisibleForTesting final AtomicBoolean mIsHalSkinForecastSupported = new AtomicBoolean(false); private final ThermalHalWrapper.WrapperThermalChangedCallback mWrapperCallback = new ThermalHalWrapper.WrapperThermalChangedCallback() { @Override Loading Loading @@ -254,6 +257,18 @@ public class ThermalManagerService extends SystemService { } onTemperatureMapChangedLocked(); mTemperatureWatcher.getAndUpdateThresholds(); // we only check forecast if a single SKIN sensor threshold is reported synchronized (mTemperatureWatcher.mSamples) { if (mTemperatureWatcher.mSevereThresholds.size() == 1) { try { mIsHalSkinForecastSupported.set( Flags.allowThermalHalSkinForecast() && !Float.isNaN(mHalWrapper.forecastSkinTemperature(10))); } catch (UnsupportedOperationException e) { Slog.i(TAG, "Thermal HAL does not support forecastSkinTemperature"); } } } mHalReady.set(true); } } Loading Loading @@ -1092,6 +1107,8 @@ public class ThermalManagerService extends SystemService { protected abstract List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter, int type); protected abstract float forecastSkinTemperature(int forecastSeconds); protected abstract boolean connectToHal(); protected abstract void dump(PrintWriter pw, String prefix); Loading Loading @@ -1124,8 +1141,16 @@ public class ThermalManagerService extends SystemService { @VisibleForTesting static class ThermalHalAidlWrapper extends ThermalHalWrapper implements IBinder.DeathRecipient { /* Proxy object for the Thermal HAL AIDL service. */ @GuardedBy("mHalLock") private IThermal mInstance = null; private IThermal getHalInstance() { synchronized (mHalLock) { return mInstance; } } /** Callback for Thermal HAL AIDL. */ private final IThermalChangedCallback mThermalCallbackAidl = new IThermalChangedCallback.Stub() { Loading Loading @@ -1169,15 +1194,15 @@ public class ThermalManagerService extends SystemService { @Override protected List<Temperature> getCurrentTemperatures(boolean shouldFilter, int type) { synchronized (mHalLock) { final IThermal instance = getHalInstance(); final List<Temperature> ret = new ArrayList<>(); if (mInstance == null) { if (instance == null) { return ret; } try { final android.hardware.thermal.Temperature[] halRet = shouldFilter ? mInstance.getTemperaturesWithType(type) : mInstance.getTemperatures(); shouldFilter ? instance.getTemperaturesWithType(type) : instance.getTemperatures(); if (halRet == null) { return ret; } Loading @@ -1196,24 +1221,25 @@ public class ThermalManagerService extends SystemService { Slog.e(TAG, "Couldn't getCurrentCoolingDevices due to invalid status", e); } catch (RemoteException e) { Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting", e); connectToHal(); synchronized (mHalLock) { connectToHalIfNeededLocked(instance); } return ret; } return ret; } @Override protected List<CoolingDevice> getCurrentCoolingDevices(boolean shouldFilter, int type) { synchronized (mHalLock) { final IThermal instance = getHalInstance(); final List<CoolingDevice> ret = new ArrayList<>(); if (mInstance == null) { if (instance == null) { return ret; } try { final android.hardware.thermal.CoolingDevice[] halRet = shouldFilter ? mInstance.getCoolingDevicesWithType(type) : mInstance.getCoolingDevices(); ? instance.getCoolingDevicesWithType(type) : instance.getCoolingDevices(); if (halRet == null) { return ret; } Loading @@ -1231,25 +1257,26 @@ public class ThermalManagerService extends SystemService { Slog.e(TAG, "Couldn't getCurrentCoolingDevices due to invalid status", e); } catch (RemoteException e) { Slog.e(TAG, "Couldn't getCurrentCoolingDevices, reconnecting", e); connectToHal(); synchronized (mHalLock) { connectToHalIfNeededLocked(instance); } return ret; } return ret; } @Override @NonNull protected List<TemperatureThreshold> getTemperatureThresholds( boolean shouldFilter, int type) { synchronized (mHalLock) { final IThermal instance = getHalInstance(); final List<TemperatureThreshold> ret = new ArrayList<>(); if (mInstance == null) { if (instance == null) { return ret; } try { final TemperatureThreshold[] halRet = shouldFilter ? mInstance.getTemperatureThresholdsWithType(type) : mInstance.getTemperatureThresholds(); shouldFilter ? instance.getTemperatureThresholdsWithType(type) : instance.getTemperatureThresholds(); if (halRet == null) { return ret; } Loading @@ -1262,25 +1289,58 @@ public class ThermalManagerService extends SystemService { Slog.e(TAG, "Couldn't getTemperatureThresholds due to invalid status", e); } catch (RemoteException e) { Slog.e(TAG, "Couldn't getTemperatureThresholds, reconnecting...", e); connectToHal(); synchronized (mHalLock) { connectToHalIfNeededLocked(instance); } } return ret; } @Override protected float forecastSkinTemperature(int forecastSeconds) { final IThermal instance = getHalInstance(); if (instance == null) { return Float.NaN; } try { return instance.forecastSkinTemperature(forecastSeconds); } catch (RemoteException e) { Slog.e(TAG, "Couldn't forecastSkinTemperature, reconnecting...", e); synchronized (mHalLock) { connectToHalIfNeededLocked(instance); } } return Float.NaN; } @Override protected boolean connectToHal() { synchronized (mHalLock) { return connectToHalIfNeededLocked(mInstance); } } @GuardedBy("mHalLock") protected boolean connectToHalIfNeededLocked(IThermal instance) { if (instance != mInstance) { // instance has been updated since last used return true; } IBinder binder = Binder.allowBlocking(ServiceManager.waitForDeclaredService( IThermal.DESCRIPTOR + "/default")); initProxyAndRegisterCallback(binder); } initProxyAndRegisterCallbackLocked(binder); return mInstance != null; } @VisibleForTesting void initProxyAndRegisterCallback(IBinder binder) { synchronized (mHalLock) { initProxyAndRegisterCallbackLocked(binder); } } @GuardedBy("mHalLock") protected void initProxyAndRegisterCallbackLocked(IBinder binder) { if (binder != null) { mInstance = IThermal.Stub.asInterface(binder); try { Loading @@ -1298,14 +1358,6 @@ public class ThermalManagerService extends SystemService { connectToHal(); return; } registerThermalChangedCallback(); } } } } @VisibleForTesting void registerThermalChangedCallback() { try { mInstance.registerThermalChangedCallback(mThermalCallbackAidl); } catch (IllegalArgumentException | IllegalStateException e) { Loading @@ -1316,6 +1368,8 @@ public class ThermalManagerService extends SystemService { connectToHal(); } } } } @Override protected void dump(PrintWriter pw, String prefix) { Loading Loading @@ -1444,6 +1498,11 @@ public class ThermalManagerService extends SystemService { } } @Override protected float forecastSkinTemperature(int forecastSeconds) { throw new UnsupportedOperationException("Not supported in Thermal HAL 1.0"); } @Override protected void dump(PrintWriter pw, String prefix) { synchronized (mHalLock) { Loading Loading @@ -1582,6 +1641,11 @@ public class ThermalManagerService extends SystemService { } } @Override protected float forecastSkinTemperature(int forecastSeconds) { throw new UnsupportedOperationException("Not supported in Thermal HAL 1.1"); } @Override protected void dump(PrintWriter pw, String prefix) { synchronized (mHalLock) { Loading Loading @@ -1748,6 +1812,11 @@ public class ThermalManagerService extends SystemService { } } @Override protected float forecastSkinTemperature(int forecastSeconds) { throw new UnsupportedOperationException("Not supported in Thermal HAL 2.0"); } @Override protected void dump(PrintWriter pw, String prefix) { synchronized (mHalLock) { Loading Loading @@ -1976,6 +2045,39 @@ public class ThermalManagerService extends SystemService { private static final int MINIMUM_SAMPLE_COUNT = 3; float getForecast(int forecastSeconds) { synchronized (mSamples) { // If we don't have any thresholds, we can't normalize the temperatures, // so return early if (mSevereThresholds.isEmpty()) { Slog.e(TAG, "No temperature thresholds found"); FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED, Binder.getCallingUid(), THERMAL_HEADROOM_CALLED__API_STATUS__NO_TEMPERATURE_THRESHOLD, Float.NaN, forecastSeconds); return Float.NaN; } } if (mIsHalSkinForecastSupported.get()) { float threshold = -1f; synchronized (mSamples) { // we only do forecast if a single SKIN sensor threshold is reported if (mSevereThresholds.size() == 1) { threshold = mSevereThresholds.valueAt(0); } } if (threshold > 0) { try { final float forecastTemperature = mHalWrapper.forecastSkinTemperature(forecastSeconds); return normalizeTemperature(forecastTemperature, threshold); } catch (UnsupportedOperationException e) { Slog.wtf(TAG, "forecastSkinTemperature returns unsupported"); } catch (Exception e) { Slog.e(TAG, "forecastSkinTemperature fails"); } return Float.NaN; } } synchronized (mSamples) { mLastForecastCallTimeMillis = SystemClock.elapsedRealtime(); if (mSamples.isEmpty()) { Loading @@ -1993,17 +2095,6 @@ public class ThermalManagerService extends SystemService { return Float.NaN; } // If we don't have any thresholds, we can't normalize the temperatures, // so return early if (mSevereThresholds.isEmpty()) { Slog.e(TAG, "No temperature thresholds found"); FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED, Binder.getCallingUid(), THERMAL_HEADROOM_CALLED__API_STATUS__NO_TEMPERATURE_THRESHOLD, Float.NaN, forecastSeconds); return Float.NaN; } if (mCachedHeadrooms.contains(forecastSeconds)) { // TODO(b/360486877): replace with metrics Slog.d(TAG, Loading
services/tests/mockingservicestests/src/com/android/server/power/ThermalManagerServiceMockingTest.java +10 −0 Original line number Diff line number Diff line Loading @@ -573,4 +573,14 @@ public class ThermalManagerServiceMockingTest { assertNotNull(ret); assertEquals(0, ret.size()); } @Test public void forecastSkinTemperature() throws RemoteException { Mockito.when(mAidlHalMock.forecastSkinTemperature(Mockito.anyInt())).thenReturn( 0.55f ); float forecast = mAidlWrapper.forecastSkinTemperature(10); Mockito.verify(mAidlHalMock, Mockito.times(1)).forecastSkinTemperature(10); assertEquals(0.55f, forecast, 0.01f); } }
services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java +118 −6 Original line number Diff line number Diff line Loading @@ -118,7 +118,6 @@ public class ThermalManagerServiceTest { private class ThermalHalFake extends ThermalHalWrapper { private static final int INIT_STATUS = Temperature.THROTTLING_NONE; private List<Temperature> mTemperatureList = new ArrayList<>(); private List<Temperature> mOverrideTemperatures = null; private List<CoolingDevice> mCoolingDeviceList = new ArrayList<>(); private List<TemperatureThreshold> mTemperatureThresholdList = initializeThresholds(); Loading @@ -132,6 +131,9 @@ public class ThermalManagerServiceTest { INIT_STATUS); private CoolingDevice mCpu = new CoolingDevice(40, CoolingDevice.TYPE_BATTERY, "cpu"); private CoolingDevice mGpu = new CoolingDevice(43, CoolingDevice.TYPE_BATTERY, "gpu"); private Map<Integer, Float> mForecastSkinTemperatures = null; private int mForecastSkinTemperaturesCalled = 0; private boolean mForecastSkinTemperaturesError = false; private List<TemperatureThreshold> initializeThresholds() { ArrayList<TemperatureThreshold> thresholds = new ArrayList<>(); Loading Loading @@ -173,12 +175,17 @@ public class ThermalManagerServiceTest { mCoolingDeviceList.add(mGpu); } void setOverrideTemperatures(List<Temperature> temperatures) { mOverrideTemperatures = temperatures; void enableForecastSkinTemperature() { mForecastSkinTemperatures = Map.of(0, 22.0f, 10, 25.0f, 20, 28.0f, 30, 31.0f, 40, 34.0f, 50, 37.0f, 60, 40.0f); } void resetOverrideTemperatures() { mOverrideTemperatures = null; void disableForecastSkinTemperature() { mForecastSkinTemperatures = null; } void failForecastSkinTemperature() { mForecastSkinTemperaturesError = true; } @Override Loading Loading @@ -218,6 +225,18 @@ public class ThermalManagerServiceTest { return ret; } @Override protected float forecastSkinTemperature(int forecastSeconds) { mForecastSkinTemperaturesCalled++; if (mForecastSkinTemperaturesError) { throw new RuntimeException(); } if (mForecastSkinTemperatures == null) { throw new UnsupportedOperationException(); } return mForecastSkinTemperatures.get(forecastSeconds); } @Override protected boolean connectToHal() { return true; Loading Loading @@ -388,7 +407,7 @@ public class ThermalManagerServiceTest { Thread.sleep(CALLBACK_TIMEOUT_MILLI_SEC); resetListenerMock(); int status = Temperature.THROTTLING_SEVERE; mFakeHal.setOverrideTemperatures(new ArrayList<>()); mFakeHal.mTemperatureList = new ArrayList<>(); // Should not notify on non-skin type Temperature newBattery = new Temperature(37, Temperature.TYPE_BATTERY, "batt", status); Loading Loading @@ -517,6 +536,99 @@ public class ThermalManagerServiceTest { ThermalManagerService.MAX_FORECAST_SEC + 1))); } @Test @EnableFlags({Flags.FLAG_ALLOW_THERMAL_HAL_SKIN_FORECAST}) public void testGetThermalHeadroom_halForecast() throws RemoteException { mFakeHal.mForecastSkinTemperaturesCalled = 0; mFakeHal.enableForecastSkinTemperature(); mService = new ThermalManagerService(mContext, mFakeHal); mService.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY); assertTrue(mService.mIsHalSkinForecastSupported.get()); assertEquals(1, mFakeHal.mForecastSkinTemperaturesCalled); mFakeHal.mForecastSkinTemperaturesCalled = 0; assertEquals(1.0f, mService.mService.getThermalHeadroom(60), 0.01f); assertEquals(0.9f, mService.mService.getThermalHeadroom(50), 0.01f); assertEquals(0.8f, mService.mService.getThermalHeadroom(40), 0.01f); assertEquals(0.7f, mService.mService.getThermalHeadroom(30), 0.01f); assertEquals(0.6f, mService.mService.getThermalHeadroom(20), 0.01f); assertEquals(0.5f, mService.mService.getThermalHeadroom(10), 0.01f); assertEquals(0.4f, mService.mService.getThermalHeadroom(0), 0.01f); assertEquals(7, mFakeHal.mForecastSkinTemperaturesCalled); } @Test @EnableFlags({Flags.FLAG_ALLOW_THERMAL_HAL_SKIN_FORECAST}) public void testGetThermalHeadroom_halForecast_disabledOnMultiThresholds() throws RemoteException { mFakeHal.mForecastSkinTemperaturesCalled = 0; List<TemperatureThreshold> thresholds = mFakeHal.initializeThresholds(); TemperatureThreshold skinThreshold = new TemperatureThreshold(); skinThreshold.type = Temperature.TYPE_SKIN; skinThreshold.name = "skin2"; skinThreshold.hotThrottlingThresholds = new float[7 /*ThrottlingSeverity#len*/]; skinThreshold.coldThrottlingThresholds = new float[7 /*ThrottlingSeverity#len*/]; for (int i = 0; i < skinThreshold.hotThrottlingThresholds.length; ++i) { // Sets NONE to 45.0f, SEVERE to 60.0f, and SHUTDOWN to 75.0f skinThreshold.hotThrottlingThresholds[i] = 45.0f + 5.0f * i; } thresholds.add(skinThreshold); mFakeHal.mTemperatureThresholdList = thresholds; mFakeHal.enableForecastSkinTemperature(); mService = new ThermalManagerService(mContext, mFakeHal); mService.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY); assertFalse("HAL skin forecast should be disabled on multiple SKIN thresholds", mService.mIsHalSkinForecastSupported.get()); mService.mService.getThermalHeadroom(10); assertEquals(0, mFakeHal.mForecastSkinTemperaturesCalled); } @Test @EnableFlags({Flags.FLAG_ALLOW_THERMAL_HAL_SKIN_FORECAST, Flags.FLAG_ALLOW_THERMAL_THRESHOLDS_CALLBACK}) public void testGetThermalHeadroom_halForecast_disabledOnMultiThresholdsCallback() throws RemoteException { mFakeHal.mForecastSkinTemperaturesCalled = 0; mFakeHal.enableForecastSkinTemperature(); mService = new ThermalManagerService(mContext, mFakeHal); mService.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY); assertTrue(mService.mIsHalSkinForecastSupported.get()); assertEquals(1, mFakeHal.mForecastSkinTemperaturesCalled); mFakeHal.mForecastSkinTemperaturesCalled = 0; TemperatureThreshold newThreshold = new TemperatureThreshold(); newThreshold.name = "skin2"; newThreshold.type = Temperature.TYPE_SKIN; newThreshold.hotThrottlingThresholds = new float[]{ Float.NaN, 43.0f, 46.0f, 49.0f, Float.NaN, Float.NaN, Float.NaN }; mFakeHal.mCallback.onThresholdChanged(newThreshold); mService.mService.getThermalHeadroom(10); assertEquals(0, mFakeHal.mForecastSkinTemperaturesCalled); } @Test @EnableFlags({Flags.FLAG_ALLOW_THERMAL_HAL_SKIN_FORECAST}) public void testGetThermalHeadroom_halForecast_errorOnHal() throws RemoteException { mFakeHal.mForecastSkinTemperaturesCalled = 0; mFakeHal.enableForecastSkinTemperature(); mService = new ThermalManagerService(mContext, mFakeHal); mService.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY); assertTrue(mService.mIsHalSkinForecastSupported.get()); assertEquals(1, mFakeHal.mForecastSkinTemperaturesCalled); mFakeHal.mForecastSkinTemperaturesCalled = 0; mFakeHal.disableForecastSkinTemperature(); assertTrue(Float.isNaN(mService.mService.getThermalHeadroom(10))); assertEquals(1, mFakeHal.mForecastSkinTemperaturesCalled); mFakeHal.enableForecastSkinTemperature(); assertFalse(Float.isNaN(mService.mService.getThermalHeadroom(10))); assertEquals(2, mFakeHal.mForecastSkinTemperaturesCalled); mFakeHal.failForecastSkinTemperature(); assertTrue(Float.isNaN(mService.mService.getThermalHeadroom(10))); assertEquals(3, mFakeHal.mForecastSkinTemperaturesCalled); } @Test @EnableFlags({Flags.FLAG_ALLOW_THERMAL_THRESHOLDS_CALLBACK, Flags.FLAG_ALLOW_THERMAL_HEADROOM_THRESHOLDS}) Loading