diff --git a/api/current.txt b/api/current.txt index 841483bcf9095..f08ddd063b950 100644 --- a/api/current.txt +++ b/api/current.txt @@ -36633,6 +36633,7 @@ package android.os { method public void addThermalStatusListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.PowerManager.OnThermalStatusChangedListener); method public int getCurrentThermalStatus(); method public int getLocationPowerSaveMode(); + method public float getThermalHeadroom(@IntRange(from=0, to=60) int); method public boolean isDeviceIdleMode(); method public boolean isIgnoringBatteryOptimizations(String); method public boolean isInteractive(); diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 655dd9b41c348..f1559f79996e1 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -148,6 +148,7 @@ import android.os.IHardwarePropertiesManager; import android.os.IPowerManager; import android.os.IRecoverySystem; import android.os.ISystemUpdateManager; +import android.os.IThermalService; import android.os.IUserManager; import android.os.IncidentManager; import android.os.PowerManager; @@ -576,10 +577,12 @@ public final class SystemServiceRegistry { new CachedServiceFetcher() { @Override public PowerManager createService(ContextImpl ctx) throws ServiceNotFoundException { - IBinder b = ServiceManager.getServiceOrThrow(Context.POWER_SERVICE); - IPowerManager service = IPowerManager.Stub.asInterface(b); - return new PowerManager(ctx.getOuterContext(), - service, ctx.mMainThread.getHandler()); + IBinder powerBinder = ServiceManager.getServiceOrThrow(Context.POWER_SERVICE); + IPowerManager powerService = IPowerManager.Stub.asInterface(powerBinder); + IBinder thermalBinder = ServiceManager.getServiceOrThrow(Context.THERMAL_SERVICE); + IThermalService thermalService = IThermalService.Stub.asInterface(thermalBinder); + return new PowerManager(ctx.getOuterContext(), powerService, thermalService, + ctx.mMainThread.getHandler()); }}); registerService(Context.RECOVERY_SERVICE, RecoverySystem.class, diff --git a/core/java/android/os/IThermalService.aidl b/core/java/android/os/IThermalService.aidl index 8c989607e8db3..ad002335a0104 100644 --- a/core/java/android/os/IThermalService.aidl +++ b/core/java/android/os/IThermalService.aidl @@ -103,4 +103,11 @@ interface IThermalService { * {@hide} */ List getCurrentCoolingDevicesWithType(in int type); + + /** + * @param forecastSeconds how many seconds ahead to forecast the provided headroom + * @return forecasted thermal headroom, normalized such that 1.0 indicates that throttling will + * occur; returns NaN if the headroom or forecast is unavailable + */ + float getThermalHeadroom(int forecastSeconds); } diff --git a/core/java/android/os/PowerManager.java b/core/java/android/os/PowerManager.java index a8fa6db232a2e..199b5d55bb39f 100644 --- a/core/java/android/os/PowerManager.java +++ b/core/java/android/os/PowerManager.java @@ -18,7 +18,9 @@ package android.os; import android.Manifest.permission; import android.annotation.CallbackExecutor; +import android.annotation.CurrentTimeMillisLong; import android.annotation.IntDef; +import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; @@ -39,6 +41,7 @@ import com.android.internal.util.Preconditions; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.concurrent.Executor; +import java.util.concurrent.atomic.AtomicLong; /** * This class gives you control of the power state of the device. @@ -916,20 +919,22 @@ public final class PowerManager { final IPowerManager mService; @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P) final Handler mHandler; + final IThermalService mThermalService; /** We lazily initialize it.*/ private DeviceIdleManager mDeviceIdleManager; - IThermalService mThermalService; private final ArrayMap mListenerMap = new ArrayMap<>(); /** * {@hide} */ - public PowerManager(Context context, IPowerManager service, Handler handler) { + public PowerManager(Context context, IPowerManager service, IThermalService thermalService, + Handler handler) { mContext = context; mService = service; + mThermalService = thermalService; mHandler = handler; } @@ -1877,18 +1882,11 @@ public final class PowerManager { * thermal throttling. */ public @ThermalStatus int getCurrentThermalStatus() { - synchronized (this) { - if (mThermalService == null) { - mThermalService = IThermalService.Stub.asInterface( - ServiceManager.getService(Context.THERMAL_SERVICE)); - } - try { - return mThermalService.getCurrentThermalStatus(); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); - } + try { + return mThermalService.getCurrentThermalStatus(); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } - } /** @@ -1915,13 +1913,7 @@ public final class PowerManager { */ public void addThermalStatusListener(@NonNull OnThermalStatusChangedListener listener) { Preconditions.checkNotNull(listener, "listener cannot be null"); - synchronized (this) { - if (mThermalService == null) { - mThermalService = IThermalService.Stub.asInterface( - ServiceManager.getService(Context.THERMAL_SERVICE)); - } - this.addThermalStatusListener(mContext.getMainExecutor(), listener); - } + this.addThermalStatusListener(mContext.getMainExecutor(), listener); } /** @@ -1934,35 +1926,29 @@ public final class PowerManager { @NonNull OnThermalStatusChangedListener listener) { Preconditions.checkNotNull(listener, "listener cannot be null"); Preconditions.checkNotNull(executor, "executor cannot be null"); - synchronized (this) { - if (mThermalService == null) { - mThermalService = IThermalService.Stub.asInterface( - ServiceManager.getService(Context.THERMAL_SERVICE)); - } - Preconditions.checkArgument(!mListenerMap.containsKey(listener), - "Listener already registered: " + listener); - IThermalStatusListener internalListener = new IThermalStatusListener.Stub() { - @Override - public void onStatusChange(int status) { - final long token = Binder.clearCallingIdentity(); - try { - executor.execute(() -> { - listener.onThermalStatusChanged(status); - }); - } finally { - Binder.restoreCallingIdentity(token); - } + Preconditions.checkArgument(!mListenerMap.containsKey(listener), + "Listener already registered: " + listener); + IThermalStatusListener internalListener = new IThermalStatusListener.Stub() { + @Override + public void onStatusChange(int status) { + final long token = Binder.clearCallingIdentity(); + try { + executor.execute(() -> { + listener.onThermalStatusChanged(status); + }); + } finally { + Binder.restoreCallingIdentity(token); } - }; - try { - if (mThermalService.registerThermalStatusListener(internalListener)) { - mListenerMap.put(listener, internalListener); - } else { - throw new RuntimeException("Listener failed to set"); - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); } + }; + try { + if (mThermalService.registerThermalStatusListener(internalListener)) { + mListenerMap.put(listener, internalListener); + } else { + throw new RuntimeException("Listener failed to set"); + } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } } @@ -1973,22 +1959,72 @@ public final class PowerManager { */ public void removeThermalStatusListener(@NonNull OnThermalStatusChangedListener listener) { Preconditions.checkNotNull(listener, "listener cannot be null"); - synchronized (this) { - if (mThermalService == null) { - mThermalService = IThermalService.Stub.asInterface( - ServiceManager.getService(Context.THERMAL_SERVICE)); - } - IThermalStatusListener internalListener = mListenerMap.get(listener); - Preconditions.checkArgument(internalListener != null, "Listener was not added"); - try { - if (mThermalService.unregisterThermalStatusListener(internalListener)) { - mListenerMap.remove(listener); - } else { - throw new RuntimeException("Listener failed to remove"); - } - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); + IThermalStatusListener internalListener = mListenerMap.get(listener); + Preconditions.checkArgument(internalListener != null, "Listener was not added"); + try { + if (mThermalService.unregisterThermalStatusListener(internalListener)) { + mListenerMap.remove(listener); + } else { + throw new RuntimeException("Listener failed to remove"); } + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } + + @CurrentTimeMillisLong + private final AtomicLong mLastHeadroomUpdate = new AtomicLong(0L); + private static final int MINIMUM_HEADROOM_TIME_MILLIS = 500; + + /** + * Provides an estimate of how much thermal headroom the device currently has before hitting + * severe throttling. + * + * Note that this only attempts to track the headroom of slow-moving sensors, such as the skin + * temperature sensor. This means that there is no benefit to calling this function more + * frequently than about once per second, and attempts to call significantly more frequently may + * result in the function returning {@code NaN}. + * + * In addition, in order to be able to provide an accurate forecast, the system does not attempt + * to forecast until it has multiple temperature samples from which to extrapolate. This should + * only take a few seconds from the time of the first call, but during this time, no forecasting + * will occur, and the current headroom will be returned regardless of the value of + * {@code forecastSeconds}. + * + * The value returned is a non-negative float that represents how much of the thermal envelope + * is in use (or is forecasted to be in use). A value of 1.0 indicates that the device is (or + * will be) throttled at {@link #THERMAL_STATUS_SEVERE}. Such throttling can affect the CPU, + * GPU, and other subsystems. Values may exceed 1.0, but there is no implied mapping to specific + * thermal status levels beyond that point. This means that values greater than 1.0 may + * correspond to {@link #THERMAL_STATUS_SEVERE}, but may also represent heavier throttling. + * + * A value of 0.0 corresponds to a fixed distance from 1.0, but does not correspond to any + * particular thermal status or temperature. Values on (0.0, 1.0] may be expected to scale + * linearly with temperature, though temperature changes over time are typically not linear. + * Negative values will be clamped to 0.0 before returning. + * + * @param forecastSeconds how many seconds in the future to forecast. Given that device + * conditions may change at any time, forecasts from further in the + * future will likely be less accurate than forecasts in the near future. + * @return a value greater than or equal to 0.0 where 1.0 indicates the SEVERE throttling + * threshold, as described above. Returns NaN if the device does not support this + * functionality or if this function is called significantly faster than once per + * second. + */ + public float getThermalHeadroom(@IntRange(from = 0, to = 60) int forecastSeconds) { + // Rate-limit calls into the thermal service + long now = SystemClock.elapsedRealtime(); + long timeSinceLastUpdate = now - mLastHeadroomUpdate.get(); + if (timeSinceLastUpdate < MINIMUM_HEADROOM_TIME_MILLIS) { + return Float.NaN; + } + + try { + float forecast = mThermalService.getThermalHeadroom(forecastSeconds); + mLastHeadroomUpdate.set(SystemClock.elapsedRealtime()); + return forecast; + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } } diff --git a/core/tests/coretests/src/android/os/PowerManagerTest.java b/core/tests/coretests/src/android/os/PowerManagerTest.java index ea0a0fd49f9f1..37e8d5937bc51 100644 --- a/core/tests/coretests/src/android/os/PowerManagerTest.java +++ b/core/tests/coretests/src/android/os/PowerManagerTest.java @@ -243,6 +243,28 @@ public class PowerManagerTest extends AndroidTestCase { .times(1)).onThermalStatusChanged(status); } + @Test + public void testGetThermalHeadroom() throws Exception { + float headroom = mPm.getThermalHeadroom(0); + // If the device doesn't support thermal headroom, return early + if (Float.isNaN(headroom)) { + return; + } + assertTrue("Expected non-negative headroom", headroom >= 0.0f); + assertTrue("Expected reasonably small headroom", headroom < 10.0f); + + // Call again immediately to ensure rate limiting works + headroom = mPm.getThermalHeadroom(0); + assertTrue("Expected NaN because of rate limiting", Float.isNaN(headroom)); + + // Sleep for a second before attempting to call again so as to not get rate limited + Thread.sleep(1000); + headroom = mPm.getThermalHeadroom(5); + assertFalse("Expected data to still be available", Float.isNaN(headroom)); + assertTrue("Expected non-negative headroom", headroom >= 0.0f); + assertTrue("Expected reasonably small headroom", headroom < 10.0f); + } + @Test public void testUserspaceRebootNotSupported_throwsUnsupportedOperationException() { // Can't use assumption framework with AndroidTestCase :( diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java index e5ee439d4bb7b..d81b8c2af2467 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java @@ -50,6 +50,7 @@ import android.metrics.LogMaker; import android.os.Binder; import android.os.Handler; import android.os.IPowerManager; +import android.os.IThermalService; import android.os.Looper; import android.os.PowerManager; import android.os.RemoteException; @@ -256,7 +257,8 @@ public class StatusBarTest extends SysuiTestCase { mDependency.injectTestDependency(NotificationFilter.class, mNotificationFilter); IPowerManager powerManagerService = mock(IPowerManager.class); - mPowerManager = new PowerManager(mContext, powerManagerService, + IThermalService thermalService = mock(IThermalService.class); + mPowerManager = new PowerManager(mContext, powerManagerService, thermalService, Handler.createAsync(Looper.myLooper())); mNotificationInterruptionStateProvider = diff --git a/services/core/java/com/android/server/power/ThermalManagerService.java b/services/core/java/com/android/server/power/ThermalManagerService.java index 491c5ab2ac03a..da3cbf9d03b4f 100644 --- a/services/core/java/com/android/server/power/ThermalManagerService.java +++ b/services/core/java/com/android/server/power/ThermalManagerService.java @@ -16,15 +16,18 @@ package com.android.server.power; +import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; import android.hardware.thermal.V1_0.ThermalStatus; import android.hardware.thermal.V1_0.ThermalStatusCode; import android.hardware.thermal.V1_1.IThermalCallback; import android.hardware.thermal.V2_0.IThermalChangedCallback; +import android.hardware.thermal.V2_0.TemperatureThreshold; import android.hardware.thermal.V2_0.ThrottlingSeverity; import android.os.Binder; import android.os.CoolingDevice; +import android.os.Handler; import android.os.HwBinder; import android.os.IThermalEventListener; import android.os.IThermalService; @@ -36,6 +39,7 @@ import android.os.RemoteException; import android.os.ResultReceiver; import android.os.ShellCallback; import android.os.ShellCommand; +import android.os.SystemClock; import android.os.Temperature; import android.util.ArrayMap; import android.util.EventLog; @@ -43,6 +47,7 @@ import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; +import com.android.internal.os.BackgroundThread; import com.android.internal.util.DumpUtils; import com.android.server.EventLogTags; import com.android.server.FgThread; @@ -54,6 +59,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.NoSuchElementException; import java.util.concurrent.atomic.AtomicBoolean; @@ -100,6 +106,9 @@ public class ThermalManagerService extends SystemService { /** Hal ready. */ private final AtomicBoolean mHalReady = new AtomicBoolean(); + /** Watches temperatures to forecast when throttling will occur */ + private final TemperatureWatcher mTemperatureWatcher = new TemperatureWatcher(); + /** Invalid throttling status */ private static final int INVALID_THROTTLING = Integer.MIN_VALUE; @@ -154,6 +163,7 @@ public class ThermalManagerService extends SystemService { onTemperatureChanged(temperatures.get(i), false); } onTemperatureMapChangedLocked(); + mTemperatureWatcher.updateSevereThresholds(); mHalReady.set(true); } } @@ -462,6 +472,15 @@ public class ThermalManagerService extends SystemService { } } + @Override + public float getThermalHeadroom(int forecastSeconds) { + if (!mHalReady.get()) { + return Float.NaN; + } + + return mTemperatureWatcher.getForecast(forecastSeconds); + } + private void dumpItemsLocked(PrintWriter pw, String prefix, Collection items) { for (Iterator iterator = items.iterator(); iterator.hasNext();) { @@ -616,6 +635,10 @@ public class ThermalManagerService extends SystemService { protected abstract List getCurrentCoolingDevices(boolean shouldFilter, int type); + @NonNull + protected abstract List getTemperatureThresholds(boolean shouldFilter, + int type); + protected abstract boolean connectToHal(); protected abstract void dump(PrintWriter pw, String prefix); @@ -727,6 +750,12 @@ public class ThermalManagerService extends SystemService { } } + @Override + protected List getTemperatureThresholds(boolean shouldFilter, + int type) { + return new ArrayList<>(); + } + @Override protected boolean connectToHal() { synchronized (mHalLock) { @@ -856,6 +885,12 @@ public class ThermalManagerService extends SystemService { } } + @Override + protected List getTemperatureThresholds(boolean shouldFilter, + int type) { + return new ArrayList<>(); + } + @Override protected boolean connectToHal() { synchronized (mHalLock) { @@ -974,6 +1009,32 @@ public class ThermalManagerService extends SystemService { } } + @Override + protected List getTemperatureThresholds(boolean shouldFilter, + int type) { + synchronized (mHalLock) { + List ret = new ArrayList<>(); + if (mThermalHal20 == null) { + return ret; + } + try { + mThermalHal20.getTemperatureThresholds(shouldFilter, type, + (status, thresholds) -> { + if (ThermalStatusCode.SUCCESS == status.code) { + ret.addAll(thresholds); + } else { + Slog.e(TAG, + "Couldn't get temperature thresholds because of HAL " + + "error: " + status.debugMessage); + } + }); + } catch (RemoteException e) { + Slog.e(TAG, "Couldn't getTemperatureThresholds, reconnecting...", e); + } + return ret; + } + } + @Override protected boolean connectToHal() { synchronized (mHalLock) { @@ -1001,4 +1062,190 @@ public class ThermalManagerService extends SystemService { } } + private 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> mSamples = new ArrayMap<>(); + + /** Map of skin temperature sensor name to the corresponding SEVERE temperature threshold */ + @GuardedBy("mSamples") + private ArrayMap mSevereThresholds = new ArrayMap<>(); + + @GuardedBy("mSamples") + private long mLastForecastCallTimeMillis = 0; + + void updateSevereThresholds() { + synchronized (mSamples) { + List thresholds = + mHalWrapper.getTemperatureThresholds(true, Temperature.TYPE_SKIN); + for (int t = 0; t < thresholds.size(); ++t) { + TemperatureThreshold threshold = thresholds.get(t); + if (threshold.hotThrottlingThresholds.length <= ThrottlingSeverity.SEVERE) { + continue; + } + float temperature = + threshold.hotThrottlingThresholds[ThrottlingSeverity.SEVERE]; + if (!Float.isNaN(temperature)) { + mSevereThresholds.put(threshold.name, + threshold.hotThrottlingThresholds[ThrottlingSeverity.SEVERE]); + } + } + } + } + + private static final int INACTIVITY_THRESHOLD_MILLIS = 10000; + private static final int RING_BUFFER_SIZE = 30; + + private void updateTemperature() { + synchronized (mSamples) { + if (SystemClock.elapsedRealtime() - mLastForecastCallTimeMillis + < INACTIVITY_THRESHOLD_MILLIS) { + // Trigger this again after a second as long as forecast has been called more + // recently than the inactivity timeout + mHandler.postDelayed(this::updateTemperature, 1000); + } else { + // Otherwise, we've been idle for at least 10 seconds, so we should + // shut down + mSamples.clear(); + return; + } + + long now = SystemClock.elapsedRealtime(); + List temperatures = mHalWrapper.getCurrentTemperatures(true, + Temperature.TYPE_SKIN); + + for (int t = 0; t < temperatures.size(); ++t) { + Temperature temperature = temperatures.get(t); + + // Filter out invalid temperatures. If this results in no values being stored at + // all, the mSamples.empty() check in getForecast() will catch it. + if (Float.isNaN(temperature.getValue())) { + continue; + } + + ArrayList samples = mSamples.computeIfAbsent(temperature.getName(), + k -> new ArrayList<>(RING_BUFFER_SIZE)); + if (samples.size() == RING_BUFFER_SIZE) { + samples.remove(0); + } + samples.add(new Sample(now, temperature.getValue())); + } + } + } + + /** + * 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 samples) { + long sumTimes = 0L; + float sumTemperatures = 0.0f; + for (int s = 0; s < samples.size(); ++s) { + Sample sample = samples.get(s); + sumTimes += sample.time; + sumTemperatures += sample.temperature; + } + long meanTime = sumTimes / samples.size(); + float meanTemperature = sumTemperatures / samples.size(); + + long sampleVariance = 0L; + float sampleCovariance = 0.0f; + for (int s = 0; s < samples.size(); ++s) { + Sample sample = samples.get(s); + long timeDelta = sample.time - meanTime; + float temperatureDelta = sample.temperature - meanTemperature; + sampleVariance += timeDelta * timeDelta; + sampleCovariance += timeDelta * temperatureDelta; + } + + return sampleCovariance / sampleVariance; + } + + /** + * Used to determine the temperature corresponding to 0.0. Given that 1.0 is pinned at the + * temperature corresponding to the SEVERE threshold, we set 0.0 to be that temperature + * minus DEGREES_BETWEEN_ZERO_AND_ONE. + */ + private static final float DEGREES_BETWEEN_ZERO_AND_ONE = 30.0f; + + private float normalizeTemperature(float temperature, float severeThreshold) { + synchronized (mSamples) { + float zeroNormalized = severeThreshold - DEGREES_BETWEEN_ZERO_AND_ONE; + if (temperature <= zeroNormalized) { + return 0.0f; + } + float delta = temperature - zeroNormalized; + return delta / DEGREES_BETWEEN_ZERO_AND_ONE; + } + } + + private static final int MINIMUM_SAMPLE_COUNT = 3; + + float getForecast(int forecastSeconds) { + synchronized (mSamples) { + mLastForecastCallTimeMillis = System.currentTimeMillis(); + if (mSamples.isEmpty()) { + updateTemperature(); + } + + // If somehow things take much longer than expected or there are no temperatures + // to sample, return early + if (mSamples.isEmpty()) { + Slog.e(TAG, "No temperature samples found"); + 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"); + return Float.NaN; + } + + float maxNormalized = Float.NaN; + for (Map.Entry> entry : mSamples.entrySet()) { + String name = entry.getKey(); + ArrayList samples = entry.getValue(); + + Float threshold = mSevereThresholds.get(name); + if (threshold == null) { + Slog.e(TAG, "No threshold found for " + name); + continue; + } + + float currentTemperature = samples.get(0).temperature; + + if (samples.size() < MINIMUM_SAMPLE_COUNT) { + // Don't try to forecast, just use the latest one we have + float normalized = normalizeTemperature(currentTemperature, threshold); + if (Float.isNaN(maxNormalized) || normalized > maxNormalized) { + maxNormalized = normalized; + } + continue; + } + + float slope = getSlopeOf(samples); + float normalized = normalizeTemperature( + currentTemperature + slope * forecastSeconds * 1000, threshold); + if (Float.isNaN(maxNormalized) || normalized > maxNormalized) { + maxNormalized = normalized; + } + } + + return maxNormalized; + } + } + + private class Sample { + public long time; + public float temperature; + + Sample(long time, float temperature) { + this.time = time; + this.temperature = temperature; + } + } + } } diff --git a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java index 2a78b6f6ca241..2c84f2603b5c4 100644 --- a/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/CachedDeviceStateServiceTest.java @@ -26,6 +26,7 @@ import android.content.Intent; import android.os.BatteryManager; import android.os.BatteryManagerInternal; import android.os.IPowerManager; +import android.os.IThermalService; import android.os.OsProtoEnums; import android.os.PowerManager; import android.os.RemoteException; @@ -52,13 +53,14 @@ import org.mockito.MockitoAnnotations; public class CachedDeviceStateServiceTest { @Mock private BatteryManagerInternal mBatteryManager; @Mock private IPowerManager mPowerManager; + @Mock private IThermalService mThermalService; private BroadcastInterceptingContext mContext; @Before public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); Context context = InstrumentationRegistry.getContext(); - PowerManager powerManager = new PowerManager(context, mPowerManager, null); + PowerManager powerManager = new PowerManager(context, mPowerManager, mThermalService, null); mContext = new BroadcastInterceptingContext(context) { @Override public Object getSystemService(String name) { diff --git a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java index 69ca643f1abfc..ae8d5545e0691 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/AbstractAccessibilityServiceConnectionTest.java @@ -74,6 +74,7 @@ import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.IPowerManager; +import android.os.IThermalService; import android.os.PowerManager; import android.os.Process; import android.os.RemoteCallback; @@ -147,6 +148,7 @@ public class AbstractAccessibilityServiceConnectionTest { @Mock private Context mMockContext; @Mock private IPowerManager mMockIPowerManager; + @Mock private IThermalService mMockIThermalService; @Mock private PackageManager mMockPackageManager; @Spy private AccessibilityServiceInfo mSpyServiceInfo = new AccessibilityServiceInfo(); @Mock private AccessibilitySecurityPolicy mMockSecurityPolicy; @@ -174,7 +176,7 @@ public class AbstractAccessibilityServiceConnectionTest { .thenReturn(mMockMagnificationController); PowerManager powerManager = - new PowerManager(mMockContext, mMockIPowerManager, mHandler); + new PowerManager(mMockContext, mMockIPowerManager, mMockIThermalService, mHandler); when(mMockContext.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager); when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager); when(mMockPackageManager.hasSystemFeature(FEATURE_FINGERPRINT)).thenReturn(true); diff --git a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java index 41235560dc916..85b8fcbbcc61d 100644 --- a/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java +++ b/services/tests/servicestests/src/com/android/server/accessibility/KeyEventDispatcherTest.java @@ -34,6 +34,7 @@ import static org.mockito.hamcrest.MockitoHamcrest.argThat; import android.content.Context; import android.os.Handler; import android.os.IPowerManager; +import android.os.IThermalService; import android.os.Looper; import android.os.Message; import android.os.PowerManager; @@ -73,6 +74,7 @@ public class KeyEventDispatcherTest { private KeyEventFilter mKeyEventFilter1; private KeyEventFilter mKeyEventFilter2; private IPowerManager mMockPowerManagerService; + private IThermalService mMockThermalService; private MessageCapturingHandler mMessageCapturingHandler; private ArgumentCaptor mFilter1SequenceCaptor = ArgumentCaptor.forClass(Integer.class); private ArgumentCaptor mFilter2SequenceCaptor = ArgumentCaptor.forClass(Integer.class); @@ -82,10 +84,12 @@ public class KeyEventDispatcherTest { Looper looper = InstrumentationRegistry.getContext().getMainLooper(); mInputEventsHandler = new MessageCapturingHandler(looper, null); mMockPowerManagerService = mock(IPowerManager.class); + mMockThermalService = mock(IThermalService.class); // TODO: It would be better to mock PowerManager rather than its binder, but the class is // final. PowerManager powerManager = - new PowerManager(mock(Context.class), mMockPowerManagerService, new Handler(looper)); + new PowerManager(mock(Context.class), mMockPowerManagerService, mMockThermalService, + new Handler(looper)); mMessageCapturingHandler = new MessageCapturingHandler(looper, null); mKeyEventDispatcher = new KeyEventDispatcher(mInputEventsHandler, SEND_FRAMEWORK_KEY_EVENT, mLock, powerManager, mMessageCapturingHandler); diff --git a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java index e90cb4641752d..ac0cac14be7b7 100644 --- a/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/attention/AttentionManagerServiceTest.java @@ -37,6 +37,7 @@ import android.content.ComponentName; import android.content.Context; import android.os.IBinder; import android.os.IPowerManager; +import android.os.IThermalService; import android.os.PowerManager; import android.os.RemoteException; import android.provider.DeviceConfig; @@ -74,6 +75,8 @@ public class AttentionManagerServiceTest { @Mock private IPowerManager mMockIPowerManager; @Mock + private IThermalService mMockIThermalService; + @Mock Context mContext; @Before @@ -84,7 +87,7 @@ public class AttentionManagerServiceTest { // setup power manager mock PowerManager mPowerManager; doReturn(true).when(mMockIPowerManager).isInteractive(); - mPowerManager = new PowerManager(mContext, mMockIPowerManager, null); + mPowerManager = new PowerManager(mContext, mMockIPowerManager, mMockIThermalService, null); Object mLock = new Object(); // setup a spy on attention manager diff --git a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java index 9e98427db7093..fa19814f401fa 100644 --- a/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/hdmi/HdmiControlServiceTest.java @@ -34,6 +34,7 @@ import android.content.ContextWrapper; import android.hardware.hdmi.HdmiControlManager; import android.hardware.hdmi.HdmiPortInfo; import android.os.IPowerManager; +import android.os.IThermalService; import android.os.Looper; import android.os.PowerManager; import android.os.RemoteException; @@ -123,6 +124,7 @@ public class HdmiControlServiceTest { private HdmiPortInfo[] mHdmiPortInfo; @Mock private IPowerManager mIPowerManagerMock; + @Mock private IThermalService mIThermalServiceMock; @Before public void setUp() throws Exception { @@ -130,7 +132,8 @@ public class HdmiControlServiceTest { mContextSpy = spy(new ContextWrapper(InstrumentationRegistry.getTargetContext())); - PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock, null); + PowerManager powerManager = new PowerManager(mContextSpy, mIPowerManagerMock, + mIThermalServiceMock, null); when(mContextSpy.getSystemService(Context.POWER_SERVICE)).thenReturn(powerManager); when(mIPowerManagerMock.isInteractive()).thenReturn(true); 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 ccf7ca9d3cf05..624cb83f9e19d 100644 --- a/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/power/ThermalManagerServiceTest.java @@ -28,10 +28,12 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.content.Context; +import android.hardware.thermal.V2_0.TemperatureThreshold; import android.os.CoolingDevice; import android.os.IBinder; import android.os.IPowerManager; import android.os.IThermalEventListener; +import android.os.IThermalService; import android.os.IThermalStatusListener; import android.os.PowerManager; import android.os.RemoteException; @@ -72,6 +74,8 @@ public class ThermalManagerServiceTest { @Mock private IPowerManager mIPowerManagerMock; @Mock + private IThermalService mIThermalServiceMock; + @Mock private IThermalEventListener mEventListener1; @Mock private IThermalEventListener mEventListener2; @@ -132,6 +136,12 @@ public class ThermalManagerServiceTest { return ret; } + @Override + protected List getTemperatureThresholds(boolean shouldFilter, + int type) { + return new ArrayList<>(); + } + @Override protected boolean connectToHal() { return true; @@ -153,7 +163,7 @@ public class ThermalManagerServiceTest { public void setUp() throws RemoteException { MockitoAnnotations.initMocks(this); mFakeHal = new ThermalHalFake(); - mPowerManager = new PowerManager(mContext, mIPowerManagerMock, null); + mPowerManager = new PowerManager(mContext, mIPowerManagerMock, mIThermalServiceMock, null); when(mContext.getSystemServiceName(PowerManager.class)).thenReturn(Context.POWER_SERVICE); when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager); resetListenerMock(); diff --git a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java index d5cdbeb121b02..035a2f11112cb 100644 --- a/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/recoverysystem/RecoverySystemServiceTest.java @@ -36,6 +36,7 @@ import android.content.IntentSender; import android.os.Handler; import android.os.IPowerManager; import android.os.IRecoverySystemProgressListener; +import android.os.IThermalService; import android.os.Looper; import android.os.PowerManager; @@ -62,6 +63,7 @@ public class RecoverySystemServiceTest { private RecoverySystemService.UncryptSocket mUncryptSocket; private Context mContext; private IPowerManager mIPowerManager; + private IThermalService mIThermalService; private FileWriter mUncryptUpdateFileWriter; private LockSettingsInternal mLockSettingsInternal; @@ -77,8 +79,9 @@ public class RecoverySystemServiceTest { Looper looper = InstrumentationRegistry.getContext().getMainLooper(); mIPowerManager = mock(IPowerManager.class); + mIThermalService = mock(IThermalService.class); PowerManager powerManager = new PowerManager(mock(Context.class), mIPowerManager, - new Handler(looper)); + mIThermalService, new Handler(looper)); mRecoverySystemService = new RecoverySystemServiceTestable(mContext, mSystemProperties, powerManager, mUncryptUpdateFileWriter, mUncryptSocket, mLockSettingsInternal);