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