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