1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.power; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotNull; 21 import static org.junit.Assert.assertTrue; 22 import static org.mockito.ArgumentMatchers.any; 23 import static org.mockito.ArgumentMatchers.anyInt; 24 import static org.mockito.Mockito.doReturn; 25 import static org.mockito.Mockito.mock; 26 import static org.mockito.Mockito.reset; 27 import static org.mockito.Mockito.timeout; 28 import static org.mockito.Mockito.verify; 29 import static org.mockito.Mockito.when; 30 31 import android.content.Context; 32 import android.hardware.thermal.V2_0.TemperatureThreshold; 33 import android.hardware.thermal.V2_0.ThrottlingSeverity; 34 import android.os.CoolingDevice; 35 import android.os.IBinder; 36 import android.os.IPowerManager; 37 import android.os.IThermalEventListener; 38 import android.os.IThermalService; 39 import android.os.IThermalStatusListener; 40 import android.os.PowerManager; 41 import android.os.RemoteException; 42 import android.os.Temperature; 43 44 import androidx.test.filters.SmallTest; 45 import androidx.test.runner.AndroidJUnit4; 46 47 import com.android.server.SystemService; 48 import com.android.server.power.ThermalManagerService.ThermalHalWrapper; 49 50 import org.junit.Before; 51 import org.junit.Test; 52 import org.junit.runner.RunWith; 53 import org.mockito.ArgumentCaptor; 54 import org.mockito.Mock; 55 import org.mockito.MockitoAnnotations; 56 57 import java.io.PrintWriter; 58 import java.util.ArrayList; 59 import java.util.Arrays; 60 import java.util.HashSet; 61 import java.util.List; 62 63 /** 64 * atest $ANDROID_BUILD_TOP/frameworks/base/services/tests/servicestests/src/com/android/server 65 * /power/ThermalManagerServiceTest.java 66 */ 67 @SmallTest 68 @RunWith(AndroidJUnit4.class) 69 public class ThermalManagerServiceTest { 70 private static final long CALLBACK_TIMEOUT_MILLI_SEC = 5000; 71 private ThermalManagerService mService; 72 private ThermalHalFake mFakeHal; 73 private PowerManager mPowerManager; 74 @Mock 75 private Context mContext; 76 @Mock 77 private IPowerManager mIPowerManagerMock; 78 @Mock 79 private IThermalService mIThermalServiceMock; 80 @Mock 81 private IThermalEventListener mEventListener1; 82 @Mock 83 private IThermalEventListener mEventListener2; 84 @Mock 85 private IThermalStatusListener mStatusListener1; 86 @Mock 87 private IThermalStatusListener mStatusListener2; 88 89 /** 90 * Fake Hal class. 91 */ 92 private class ThermalHalFake extends ThermalHalWrapper { 93 private static final int INIT_STATUS = Temperature.THROTTLING_NONE; 94 private ArrayList<Temperature> mTemperatureList = new ArrayList<>(); 95 private ArrayList<CoolingDevice> mCoolingDeviceList = new ArrayList<>(); 96 private ArrayList<TemperatureThreshold> mTemperatureThresholdList = initializeThresholds(); 97 98 private Temperature mSkin1 = new Temperature(0, Temperature.TYPE_SKIN, "skin1", 99 INIT_STATUS); 100 private Temperature mSkin2 = new Temperature(0, Temperature.TYPE_SKIN, "skin2", 101 INIT_STATUS); 102 private Temperature mBattery = new Temperature(0, Temperature.TYPE_BATTERY, "batt", 103 INIT_STATUS); 104 private Temperature mUsbPort = new Temperature(0, Temperature.TYPE_USB_PORT, "usbport", 105 INIT_STATUS); 106 private CoolingDevice mCpu = new CoolingDevice(0, CoolingDevice.TYPE_BATTERY, "cpu"); 107 private CoolingDevice mGpu = new CoolingDevice(0, CoolingDevice.TYPE_BATTERY, "gpu"); 108 initializeThresholds()109 private ArrayList<TemperatureThreshold> initializeThresholds() { 110 ArrayList<TemperatureThreshold> thresholds = new ArrayList<>(); 111 112 TemperatureThreshold skinThreshold = new TemperatureThreshold(); 113 skinThreshold.type = Temperature.TYPE_SKIN; 114 skinThreshold.name = "skin1"; 115 skinThreshold.hotThrottlingThresholds = new float[7 /*ThrottlingSeverity#len*/]; 116 for (int i = 0; i < skinThreshold.hotThrottlingThresholds.length; ++i) { 117 // Sets NONE to 25.0f, SEVERE to 40.0f, and SHUTDOWN to 55.0f 118 skinThreshold.hotThrottlingThresholds[i] = 25.0f + 5.0f * i; 119 } 120 thresholds.add(skinThreshold); 121 122 TemperatureThreshold cpuThreshold = new TemperatureThreshold(); 123 cpuThreshold.type = Temperature.TYPE_CPU; 124 cpuThreshold.name = "cpu"; 125 cpuThreshold.hotThrottlingThresholds = new float[7 /*ThrottlingSeverity#len*/]; 126 for (int i = 0; i < cpuThreshold.hotThrottlingThresholds.length; ++i) { 127 if (i == ThrottlingSeverity.SEVERE) { 128 cpuThreshold.hotThrottlingThresholds[i] = 95.0f; 129 } else { 130 cpuThreshold.hotThrottlingThresholds[i] = Float.NaN; 131 } 132 } 133 thresholds.add(cpuThreshold); 134 135 return thresholds; 136 } 137 ThermalHalFake()138 ThermalHalFake() { 139 mTemperatureList.add(mSkin1); 140 mTemperatureList.add(mSkin2); 141 mTemperatureList.add(mBattery); 142 mTemperatureList.add(mUsbPort); 143 mCoolingDeviceList.add(mCpu); 144 mCoolingDeviceList.add(mGpu); 145 } 146 147 @Override getCurrentTemperatures(boolean shouldFilter, int type)148 protected List<Temperature> getCurrentTemperatures(boolean shouldFilter, int type) { 149 List<Temperature> ret = new ArrayList<>(); 150 for (Temperature temperature : mTemperatureList) { 151 if (shouldFilter && type != temperature.getType()) { 152 continue; 153 } 154 ret.add(temperature); 155 } 156 return ret; 157 } 158 159 @Override getCurrentCoolingDevices(boolean shouldFilter, int type)160 protected List<CoolingDevice> getCurrentCoolingDevices(boolean shouldFilter, int type) { 161 List<CoolingDevice> ret = new ArrayList<>(); 162 for (CoolingDevice cdev : mCoolingDeviceList) { 163 if (shouldFilter && type != cdev.getType()) { 164 continue; 165 } 166 ret.add(cdev); 167 } 168 return ret; 169 } 170 171 @Override getTemperatureThresholds(boolean shouldFilter, int type)172 protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter, 173 int type) { 174 List<TemperatureThreshold> ret = new ArrayList<>(); 175 for (TemperatureThreshold threshold : mTemperatureThresholdList) { 176 if (shouldFilter && type != threshold.type) { 177 continue; 178 } 179 ret.add(threshold); 180 } 181 return ret; 182 } 183 184 @Override connectToHal()185 protected boolean connectToHal() { 186 return true; 187 } 188 189 @Override dump(PrintWriter pw, String prefix)190 protected void dump(PrintWriter pw, String prefix) { 191 return; 192 } 193 } 194 assertListEqualsIgnoringOrder(List<?> actual, List<?> expected)195 private void assertListEqualsIgnoringOrder(List<?> actual, List<?> expected) { 196 HashSet<?> actualSet = new HashSet<>(actual); 197 HashSet<?> expectedSet = new HashSet<>(expected); 198 assertEquals(expectedSet, actualSet); 199 } 200 201 @Before setUp()202 public void setUp() throws RemoteException { 203 MockitoAnnotations.initMocks(this); 204 mFakeHal = new ThermalHalFake(); 205 mPowerManager = new PowerManager(mContext, mIPowerManagerMock, mIThermalServiceMock, null); 206 when(mContext.getSystemServiceName(PowerManager.class)).thenReturn(Context.POWER_SERVICE); 207 when(mContext.getSystemService(PowerManager.class)).thenReturn(mPowerManager); 208 resetListenerMock(); 209 mService = new ThermalManagerService(mContext, mFakeHal); 210 // Register callbacks before AMS ready and no callback sent 211 assertTrue(mService.mService.registerThermalEventListener(mEventListener1)); 212 assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1)); 213 assertTrue(mService.mService.registerThermalEventListenerWithType(mEventListener2, 214 Temperature.TYPE_SKIN)); 215 assertTrue(mService.mService.registerThermalStatusListener(mStatusListener2)); 216 verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 217 .times(0)).notifyThrottling(any(Temperature.class)); 218 verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 219 .times(1)).onStatusChange(Temperature.THROTTLING_NONE); 220 verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 221 .times(0)).notifyThrottling(any(Temperature.class)); 222 verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 223 .times(1)).onStatusChange(Temperature.THROTTLING_NONE); 224 resetListenerMock(); 225 mService.onBootPhase(SystemService.PHASE_ACTIVITY_MANAGER_READY); 226 ArgumentCaptor<Temperature> captor = ArgumentCaptor.forClass(Temperature.class); 227 verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 228 .times(4)).notifyThrottling(captor.capture()); 229 assertListEqualsIgnoringOrder(mFakeHal.mTemperatureList, captor.getAllValues()); 230 verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 231 .times(0)).onStatusChange(Temperature.THROTTLING_NONE); 232 captor = ArgumentCaptor.forClass(Temperature.class); 233 verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 234 .times(2)).notifyThrottling(captor.capture()); 235 assertListEqualsIgnoringOrder( 236 new ArrayList<>(Arrays.asList(mFakeHal.mSkin1, mFakeHal.mSkin2)), 237 captor.getAllValues()); 238 verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 239 .times(0)).onStatusChange(Temperature.THROTTLING_NONE); 240 } 241 resetListenerMock()242 private void resetListenerMock() { 243 reset(mEventListener1); 244 reset(mStatusListener1); 245 reset(mEventListener2); 246 reset(mStatusListener2); 247 doReturn(mock(IBinder.class)).when(mEventListener1).asBinder(); 248 doReturn(mock(IBinder.class)).when(mStatusListener1).asBinder(); 249 doReturn(mock(IBinder.class)).when(mEventListener2).asBinder(); 250 doReturn(mock(IBinder.class)).when(mStatusListener2).asBinder(); 251 } 252 253 @Test testRegister()254 public void testRegister() throws RemoteException { 255 resetListenerMock(); 256 // Register callbacks and verify they are called 257 assertTrue(mService.mService.registerThermalEventListener(mEventListener1)); 258 assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1)); 259 ArgumentCaptor<Temperature> captor = ArgumentCaptor.forClass(Temperature.class); 260 verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 261 .times(4)).notifyThrottling(captor.capture()); 262 assertListEqualsIgnoringOrder(mFakeHal.mTemperatureList, captor.getAllValues()); 263 verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 264 .times(1)).onStatusChange(Temperature.THROTTLING_NONE); 265 // Register new callbacks and verify old ones are not called (remained same) while new 266 // ones are called 267 assertTrue(mService.mService.registerThermalEventListenerWithType(mEventListener2, 268 Temperature.TYPE_SKIN)); 269 assertTrue(mService.mService.registerThermalStatusListener(mStatusListener2)); 270 verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 271 .times(4)).notifyThrottling(any(Temperature.class)); 272 verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 273 .times(1)).onStatusChange(Temperature.THROTTLING_NONE); 274 captor = ArgumentCaptor.forClass(Temperature.class); 275 verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 276 .times(2)).notifyThrottling(captor.capture()); 277 assertListEqualsIgnoringOrder( 278 new ArrayList<>(Arrays.asList(mFakeHal.mSkin1, mFakeHal.mSkin2)), 279 captor.getAllValues()); 280 verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 281 .times(1)).onStatusChange(Temperature.THROTTLING_NONE); 282 } 283 284 @Test testNotify()285 public void testNotify() throws RemoteException { 286 int status = Temperature.THROTTLING_SEVERE; 287 // Should only notify event not status 288 Temperature newBattery = new Temperature(50, Temperature.TYPE_BATTERY, "batt", status); 289 mFakeHal.mCallback.onValues(newBattery); 290 verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 291 .times(1)).notifyThrottling(newBattery); 292 verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 293 .times(0)).onStatusChange(anyInt()); 294 verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 295 .times(0)).notifyThrottling(newBattery); 296 verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 297 .times(0)).onStatusChange(anyInt()); 298 resetListenerMock(); 299 // Notify both event and status 300 Temperature newSkin = new Temperature(50, Temperature.TYPE_SKIN, "skin1", status); 301 mFakeHal.mCallback.onValues(newSkin); 302 verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 303 .times(1)).notifyThrottling(newSkin); 304 verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 305 .times(1)).onStatusChange(status); 306 verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 307 .times(1)).notifyThrottling(newSkin); 308 verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 309 .times(1)).onStatusChange(status); 310 resetListenerMock(); 311 // Back to None, should only notify event not status 312 status = Temperature.THROTTLING_NONE; 313 newBattery = new Temperature(50, Temperature.TYPE_BATTERY, "batt", status); 314 mFakeHal.mCallback.onValues(newBattery); 315 verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 316 .times(1)).notifyThrottling(newBattery); 317 verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 318 .times(0)).onStatusChange(anyInt()); 319 verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 320 .times(0)).notifyThrottling(newBattery); 321 verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 322 .times(0)).onStatusChange(anyInt()); 323 resetListenerMock(); 324 // Should also notify status 325 newSkin = new Temperature(50, Temperature.TYPE_SKIN, "skin1", status); 326 mFakeHal.mCallback.onValues(newSkin); 327 verify(mEventListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 328 .times(1)).notifyThrottling(newSkin); 329 verify(mStatusListener1, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 330 .times(1)).onStatusChange(status); 331 verify(mEventListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 332 .times(1)).notifyThrottling(newSkin); 333 verify(mStatusListener2, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 334 .times(1)).onStatusChange(status); 335 } 336 337 @Test testGetCurrentTemperatures()338 public void testGetCurrentTemperatures() throws RemoteException { 339 assertListEqualsIgnoringOrder(mFakeHal.getCurrentTemperatures(false, 0), 340 Arrays.asList(mService.mService.getCurrentTemperatures())); 341 assertListEqualsIgnoringOrder( 342 mFakeHal.getCurrentTemperatures(true, Temperature.TYPE_SKIN), 343 Arrays.asList(mService.mService.getCurrentTemperaturesWithType( 344 Temperature.TYPE_SKIN))); 345 } 346 347 @Test testGetCurrentStatus()348 public void testGetCurrentStatus() throws RemoteException { 349 int status = Temperature.THROTTLING_SEVERE; 350 Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status); 351 mFakeHal.mCallback.onValues(newSkin); 352 assertEquals(status, mService.mService.getCurrentThermalStatus()); 353 int battStatus = Temperature.THROTTLING_EMERGENCY; 354 Temperature newBattery = new Temperature(60, Temperature.TYPE_BATTERY, "batt", battStatus); 355 assertEquals(status, mService.mService.getCurrentThermalStatus()); 356 } 357 358 @Test testThermalShutdown()359 public void testThermalShutdown() throws RemoteException { 360 int status = Temperature.THROTTLING_SHUTDOWN; 361 Temperature newSkin = new Temperature(100, Temperature.TYPE_SKIN, "skin1", status); 362 mFakeHal.mCallback.onValues(newSkin); 363 verify(mIPowerManagerMock, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 364 .times(1)).shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false); 365 Temperature newBattery = new Temperature(60, Temperature.TYPE_BATTERY, "batt", status); 366 mFakeHal.mCallback.onValues(newBattery); 367 verify(mIPowerManagerMock, timeout(CALLBACK_TIMEOUT_MILLI_SEC) 368 .times(1)).shutdown(false, PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE, false); 369 } 370 371 @Test testNoHal()372 public void testNoHal() throws RemoteException { 373 mService = new ThermalManagerService(mContext); 374 // Do no call onActivityManagerReady to skip connect HAL 375 assertTrue(mService.mService.registerThermalEventListener(mEventListener1)); 376 assertTrue(mService.mService.registerThermalStatusListener(mStatusListener1)); 377 assertTrue(mService.mService.unregisterThermalEventListener(mEventListener1)); 378 assertTrue(mService.mService.unregisterThermalStatusListener(mStatusListener1)); 379 assertEquals(0, Arrays.asList(mService.mService.getCurrentTemperatures()).size()); 380 assertEquals(0, Arrays.asList(mService.mService.getCurrentTemperaturesWithType( 381 Temperature.TYPE_SKIN)).size()); 382 assertEquals(Temperature.THROTTLING_NONE, mService.mService.getCurrentThermalStatus()); 383 } 384 385 @Test testGetCurrentCoolingDevices()386 public void testGetCurrentCoolingDevices() throws RemoteException { 387 assertListEqualsIgnoringOrder(mFakeHal.getCurrentCoolingDevices(false, 0), 388 Arrays.asList(mService.mService.getCurrentCoolingDevices())); 389 assertListEqualsIgnoringOrder( 390 mFakeHal.getCurrentCoolingDevices(false, CoolingDevice.TYPE_BATTERY), 391 Arrays.asList(mService.mService.getCurrentCoolingDevices())); 392 assertListEqualsIgnoringOrder( 393 mFakeHal.getCurrentCoolingDevices(true, CoolingDevice.TYPE_CPU), 394 Arrays.asList(mService.mService.getCurrentCoolingDevicesWithType( 395 CoolingDevice.TYPE_CPU))); 396 } 397 398 @Test testTemperatureWatcherUpdateSevereThresholds()399 public void testTemperatureWatcherUpdateSevereThresholds() throws RemoteException { 400 ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher; 401 watcher.mSevereThresholds.erase(); 402 watcher.updateSevereThresholds(); 403 assertEquals(1, watcher.mSevereThresholds.size()); 404 assertEquals("skin1", watcher.mSevereThresholds.keyAt(0)); 405 Float threshold = watcher.mSevereThresholds.get("skin1"); 406 assertNotNull(threshold); 407 assertEquals(40.0f, threshold, 0.0f); 408 } 409 410 @Test testTemperatureWatcherGetSlopeOf()411 public void testTemperatureWatcherGetSlopeOf() throws RemoteException { 412 ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher; 413 List<ThermalManagerService.TemperatureWatcher.Sample> samples = new ArrayList<>(); 414 for (int i = 0; i < 30; ++i) { 415 samples.add(watcher.createSampleForTesting(i, (float) (i / 2 * 2))); 416 } 417 assertEquals(1.0f, watcher.getSlopeOf(samples), 0.01f); 418 } 419 420 @Test testTemperatureWatcherNormalizeTemperature()421 public void testTemperatureWatcherNormalizeTemperature() throws RemoteException { 422 ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher; 423 assertEquals(0.5f, watcher.normalizeTemperature(25.0f, 40.0f), 0.0f); 424 425 // Temperatures more than 30 degrees below the SEVERE threshold should be clamped to 0.0f 426 assertEquals(0.0f, watcher.normalizeTemperature(0.0f, 40.0f), 0.0f); 427 428 // Temperatures above the SEVERE threshold should not be clamped 429 assertEquals(2.0f, watcher.normalizeTemperature(70.0f, 40.0f), 0.0f); 430 } 431 432 @Test testTemperatureWatcherGetForecast()433 public void testTemperatureWatcherGetForecast() throws RemoteException { 434 ThermalManagerService.TemperatureWatcher watcher = mService.mTemperatureWatcher; 435 436 ArrayList<ThermalManagerService.TemperatureWatcher.Sample> samples = new ArrayList<>(); 437 438 // Add a single sample 439 samples.add(watcher.createSampleForTesting(0, 25.0f)); 440 watcher.mSamples.put("skin1", samples); 441 442 // Because there are not enough samples to compute the linear regression, 443 // no matter how far ahead we forecast, we should receive the same value 444 assertEquals(0.5f, watcher.getForecast(0), 0.0f); 445 assertEquals(0.5f, watcher.getForecast(5), 0.0f); 446 447 // Add some time-series data 448 for (int i = 1; i < 20; ++i) { 449 samples.add(0, watcher.createSampleForTesting(1000 * i, 25.0f + 0.5f * i)); 450 } 451 452 // Now the forecast should vary depending on how far ahead we are trying to predict 453 assertEquals(0.9f, watcher.getForecast(4), 0.02f); 454 assertEquals(1.0f, watcher.getForecast(10), 0.02f); 455 456 // If there are no thresholds, then we shouldn't receive a headroom value 457 watcher.mSevereThresholds.erase(); 458 assertTrue(Float.isNaN(watcher.getForecast(0))); 459 } 460 } 461