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