• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.android.internal.util.ConcurrentUtils.DIRECT_EXECUTOR;
20 import static com.android.internal.util.FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__NO_TEMPERATURE_THRESHOLD;
21 import static com.android.internal.util.FrameworkStatsLog.THERMAL_HEADROOM_THRESHOLDS_CALLED__API_STATUS__FEATURE_NOT_SUPPORTED;
22 import static com.android.internal.util.FrameworkStatsLog.THERMAL_HEADROOM_THRESHOLDS_CALLED__API_STATUS__HAL_NOT_READY;
23 import static com.android.internal.util.FrameworkStatsLog.THERMAL_HEADROOM_THRESHOLDS_CALLED__API_STATUS__SUCCESS;
24 import static com.android.internal.util.FrameworkStatsLog.THERMAL_STATUS_CALLED__API_STATUS__HAL_NOT_READY;
25 import static com.android.internal.util.FrameworkStatsLog.THERMAL_STATUS_CALLED__API_STATUS__SUCCESS;
26 
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.app.StatsManager;
30 import android.content.Context;
31 import android.hardware.thermal.IThermal;
32 import android.hardware.thermal.IThermalChangedCallback;
33 import android.hardware.thermal.TemperatureThreshold;
34 import android.hardware.thermal.TemperatureType;
35 import android.hardware.thermal.ThrottlingSeverity;
36 import android.hardware.thermal.V1_0.ThermalStatus;
37 import android.hardware.thermal.V1_0.ThermalStatusCode;
38 import android.hardware.thermal.V1_1.IThermalCallback;
39 import android.os.Binder;
40 import android.os.CoolingDevice;
41 import android.os.Flags;
42 import android.os.Handler;
43 import android.os.HwBinder;
44 import android.os.IBinder;
45 import android.os.IThermalEventListener;
46 import android.os.IThermalHeadroomListener;
47 import android.os.IThermalService;
48 import android.os.IThermalStatusListener;
49 import android.os.PowerManager;
50 import android.os.Process;
51 import android.os.RemoteCallbackList;
52 import android.os.RemoteException;
53 import android.os.ResultReceiver;
54 import android.os.ServiceManager;
55 import android.os.ShellCallback;
56 import android.os.ShellCommand;
57 import android.os.SystemClock;
58 import android.os.Temperature;
59 import android.os.Trace;
60 import android.util.ArrayMap;
61 import android.util.EventLog;
62 import android.util.Slog;
63 import android.util.SparseArray;
64 import android.util.StatsEvent;
65 
66 import com.android.internal.annotations.GuardedBy;
67 import com.android.internal.annotations.VisibleForTesting;
68 import com.android.internal.os.BackgroundThread;
69 import com.android.internal.util.DumpUtils;
70 import com.android.internal.util.FrameworkStatsLog;
71 import com.android.server.EventLogTags;
72 import com.android.server.FgThread;
73 import com.android.server.SystemService;
74 
75 import java.io.FileDescriptor;
76 import java.io.PrintWriter;
77 import java.util.ArrayList;
78 import java.util.Arrays;
79 import java.util.Collection;
80 import java.util.Iterator;
81 import java.util.List;
82 import java.util.Map;
83 import java.util.NoSuchElementException;
84 import java.util.concurrent.atomic.AtomicBoolean;
85 import java.util.stream.Collectors;
86 
87 /**
88  * This is a system service that listens to HAL thermal events and dispatch those to listeners.
89  * <p>The service will also trigger actions based on severity of the throttling status.</p>
90  *
91  * @hide
92  */
93 public class ThermalManagerService extends SystemService {
94     private static final String TAG = ThermalManagerService.class.getSimpleName();
95 
96     private static final boolean DEBUG = false;
97 
98     /** Input range limits for getThermalHeadroom API */
99     public static final int MIN_FORECAST_SEC = 0;
100     public static final int MAX_FORECAST_SEC = 60;
101     public static final int DEFAULT_FORECAST_SECONDS = 10;
102     public static final int HEADROOM_CALLBACK_MIN_INTERVAL_MILLIS = 5000;
103     // headroom to temperature conversion: 3C every 0.1 headroom difference
104     // if no throttling event, the temperature difference should be at least 0.9C (or 0.03 headroom)
105     // to make a callback
106     public static final float HEADROOM_CALLBACK_MIN_DIFFERENCE = 0.03f;
107     // if no throttling event, the threshold headroom difference should be at least 0.01 (or 0.3C)
108     // to make a callback
109     public static final float HEADROOM_THRESHOLD_CALLBACK_MIN_DIFFERENCE = 0.01f;
110 
111     /** Lock to protect listen list. */
112     private final Object mLock = new Object();
113 
114     /**
115      * Registered observers of the thermal events. Cookie is used to store type as Integer, null
116      * means no filter.
117      */
118     @GuardedBy("mLock")
119     private final RemoteCallbackList<IThermalEventListener> mThermalEventListeners =
120             new RemoteCallbackList<>();
121 
122     /** Registered observers of the thermal status. */
123     @GuardedBy("mLock")
124     private final RemoteCallbackList<IThermalStatusListener> mThermalStatusListeners =
125             new RemoteCallbackList<>();
126 
127     /** Registered observers of the thermal headroom. */
128     @GuardedBy("mLock")
129     private final RemoteCallbackList<IThermalHeadroomListener> mThermalHeadroomListeners =
130             new RemoteCallbackList<>();
131     @GuardedBy("mLock")
132     private long mLastHeadroomCallbackTimeMillis;
133     @GuardedBy("mLock")
134     private HeadroomCallbackData mLastHeadroomCallbackData = null;
135 
136     /** Current thermal status */
137     @GuardedBy("mLock")
138     private int mStatus;
139 
140     /** If override status takes effect */
141     @GuardedBy("mLock")
142     private boolean mIsStatusOverride;
143 
144     /** Current thermal map, key as name */
145     @GuardedBy("mLock")
146     private ArrayMap<String, Temperature> mTemperatureMap = new ArrayMap<>();
147 
148     /** HAL wrapper. */
149     private ThermalHalWrapper mHalWrapper;
150 
151     /** Hal ready. */
152     private final AtomicBoolean mHalReady = new AtomicBoolean();
153 
154     /** Watches temperatures to forecast when throttling will occur */
155     @VisibleForTesting
156     final TemperatureWatcher mTemperatureWatcher;
157 
158     @VisibleForTesting
159     final AtomicBoolean mIsHalSkinForecastSupported = new AtomicBoolean(false);
160 
161     private final ThermalHalWrapper.WrapperThermalChangedCallback mWrapperCallback =
162             new ThermalHalWrapper.WrapperThermalChangedCallback() {
163                 @Override
164                 public void onTemperatureChanged(Temperature temperature) {
165                     final long token = Binder.clearCallingIdentity();
166                     try {
167                         ThermalManagerService.this.onTemperatureChanged(temperature, true);
168                     } finally {
169                         Binder.restoreCallingIdentity(token);
170                     }
171                 }
172 
173                 @Override
174                 public void onThresholdChanged(TemperatureThreshold threshold) {
175                     final long token = Binder.clearCallingIdentity();
176                     try {
177                         final HeadroomCallbackData data;
178                         synchronized (mTemperatureWatcher.mSamples) {
179                             if (DEBUG) {
180                                 Slog.d(TAG, "Updating skin threshold: " + threshold);
181                             }
182                             mTemperatureWatcher.updateTemperatureThresholdLocked(threshold, true);
183                             data = mTemperatureWatcher.getHeadroomCallbackDataLocked();
184                         }
185                         synchronized (mLock) {
186                             checkAndNotifyHeadroomListenersLocked(data);
187                         }
188                     } finally {
189                         Binder.restoreCallingIdentity(token);
190                     }
191                 }
192             };
193 
194     private final Context mContext;
195 
ThermalManagerService(Context context)196     public ThermalManagerService(Context context) {
197         this(context, null);
198     }
199 
200     @VisibleForTesting
ThermalManagerService(Context context, @Nullable ThermalHalWrapper halWrapper)201     ThermalManagerService(Context context, @Nullable ThermalHalWrapper halWrapper) {
202         super(context);
203         mContext = context;
204         mHalWrapper = halWrapper;
205         if (halWrapper != null) {
206             halWrapper.setCallback(mWrapperCallback);
207         }
208         mStatus = Temperature.THROTTLING_NONE;
209         mTemperatureWatcher = new TemperatureWatcher();
210     }
211 
212     @Override
onStart()213     public void onStart() {
214         publishBinderService(Context.THERMAL_SERVICE, mService);
215     }
216 
217     @Override
onBootPhase(int phase)218     public void onBootPhase(int phase) {
219         if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
220             onActivityManagerReady();
221         }
222         if (phase == SystemService.PHASE_BOOT_COMPLETED) {
223             registerStatsCallbacks();
224         }
225     }
226 
onActivityManagerReady()227     private void onActivityManagerReady() {
228         synchronized (mLock) {
229             // Connect to HAL and post to listeners.
230             boolean halConnected = (mHalWrapper != null);
231             if (!halConnected) {
232                 mHalWrapper = new ThermalHalAidlWrapper(mWrapperCallback);
233                 halConnected = mHalWrapper.connectToHal();
234             }
235             if (!halConnected) {
236                 mHalWrapper = new ThermalHal20Wrapper(mWrapperCallback);
237                 halConnected = mHalWrapper.connectToHal();
238             }
239             if (!halConnected) {
240                 mHalWrapper = new ThermalHal11Wrapper(mWrapperCallback);
241                 halConnected = mHalWrapper.connectToHal();
242             }
243             if (!halConnected) {
244                 mHalWrapper = new ThermalHal10Wrapper(mWrapperCallback);
245                 halConnected = mHalWrapper.connectToHal();
246             }
247             if (!halConnected) {
248                 Slog.w(TAG, "No Thermal HAL service on this device");
249                 return;
250             }
251             List<Temperature> temperatures = mHalWrapper.getCurrentTemperatures(false,
252                     0);
253             final int count = temperatures.size();
254             if (count == 0) {
255                 Slog.w(TAG, "Thermal HAL reported invalid data, abort connection");
256             }
257             for (int i = 0; i < count; i++) {
258                 onTemperatureChanged(temperatures.get(i), false);
259             }
260             onTemperatureMapChangedLocked();
261             mTemperatureWatcher.getAndUpdateThresholds();
262             // we only check forecast if a single SKIN sensor threshold is reported
263             synchronized (mTemperatureWatcher.mSamples) {
264                 if (mTemperatureWatcher.mSevereThresholds.size() == 1) {
265                     try {
266                         mIsHalSkinForecastSupported.set(
267                                 Flags.allowThermalHalSkinForecast()
268                                         && !Float.isNaN(mHalWrapper.forecastSkinTemperature(10)));
269                     } catch (UnsupportedOperationException e) {
270                         Slog.i(TAG, "Thermal HAL does not support forecastSkinTemperature");
271                     }
272                 }
273             }
274             mHalReady.set(true);
275         }
276     }
277 
278     @GuardedBy("mLock")
postStatusListenerLocked(IThermalStatusListener listener)279     private void postStatusListenerLocked(IThermalStatusListener listener) {
280         final boolean thermalCallbackQueued = FgThread.getHandler().post(() -> {
281             try {
282                 listener.onStatusChange(mStatus);
283             } catch (RemoteException | RuntimeException e) {
284                 Slog.e(TAG, "Thermal status callback failed to call", e);
285             }
286         });
287         if (!thermalCallbackQueued) {
288             Slog.e(TAG, "Thermal status callback failed to queue");
289         }
290     }
291 
292     @GuardedBy("mLock")
notifyStatusListenersLocked()293     private void notifyStatusListenersLocked() {
294         final int length = mThermalStatusListeners.beginBroadcast();
295         try {
296             for (int i = 0; i < length; i++) {
297                 final IThermalStatusListener listener =
298                         mThermalStatusListeners.getBroadcastItem(i);
299                 postStatusListenerLocked(listener);
300             }
301         } finally {
302             mThermalStatusListeners.finishBroadcast();
303         }
304     }
305 
306     @GuardedBy("mLock")
postHeadroomListenerLocked(IThermalHeadroomListener listener, HeadroomCallbackData data)307     private void postHeadroomListenerLocked(IThermalHeadroomListener listener,
308             HeadroomCallbackData data) {
309         if (!mHalReady.get()) {
310             return;
311         }
312         final boolean thermalCallbackQueued = FgThread.getHandler().post(() -> {
313             try {
314                 if (Float.isNaN(data.mHeadroom)) {
315                     return;
316                 }
317                 listener.onHeadroomChange(data.mHeadroom, data.mForecastHeadroom,
318                         data.mForecastSeconds, data.mHeadroomThresholds);
319             } catch (RemoteException | RuntimeException e) {
320                 Slog.e(TAG, "Thermal headroom callback failed to call", e);
321             }
322         });
323         if (!thermalCallbackQueued) {
324             Slog.e(TAG, "Thermal headroom callback failed to queue");
325         }
326     }
327 
328     @GuardedBy("mLock")
checkAndNotifyHeadroomListenersLocked(HeadroomCallbackData data)329     private void checkAndNotifyHeadroomListenersLocked(HeadroomCallbackData data) {
330         if (!data.isSignificantDifferentFrom(mLastHeadroomCallbackData)
331                 && System.currentTimeMillis()
332                 < mLastHeadroomCallbackTimeMillis + HEADROOM_CALLBACK_MIN_INTERVAL_MILLIS) {
333             // skip notifying the client with similar data within a short period
334             return;
335         }
336         mLastHeadroomCallbackTimeMillis = System.currentTimeMillis();
337         mLastHeadroomCallbackData = data;
338         final int length = mThermalHeadroomListeners.beginBroadcast();
339         try {
340             for (int i = 0; i < length; i++) {
341                 final IThermalHeadroomListener listener =
342                         mThermalHeadroomListeners.getBroadcastItem(i);
343                 postHeadroomListenerLocked(listener, data);
344             }
345         } finally {
346             mThermalHeadroomListeners.finishBroadcast();
347         }
348     }
349 
350     @GuardedBy("mLock")
onTemperatureMapChangedLocked()351     private void onTemperatureMapChangedLocked() {
352         int newStatus = Temperature.THROTTLING_NONE;
353         final int count = mTemperatureMap.size();
354         for (int i = 0; i < count; i++) {
355             Temperature t = mTemperatureMap.valueAt(i);
356             if (t.getType() == Temperature.TYPE_SKIN && t.getStatus() >= newStatus) {
357                 newStatus = t.getStatus();
358             }
359         }
360         // Do not update if override from shell
361         if (!mIsStatusOverride) {
362             setStatusLocked(newStatus);
363         }
364     }
365 
366     @GuardedBy("mLock")
setStatusLocked(int newStatus)367     private void setStatusLocked(int newStatus) {
368         if (newStatus != mStatus) {
369             Trace.traceCounter(Trace.TRACE_TAG_POWER, "ThermalManagerService.status", newStatus);
370             mStatus = newStatus;
371             notifyStatusListenersLocked();
372         }
373     }
374 
375     @GuardedBy("mLock")
postEventListenerCurrentTemperaturesLocked(IThermalEventListener listener, @Nullable Integer type)376     private void postEventListenerCurrentTemperaturesLocked(IThermalEventListener listener,
377             @Nullable Integer type) {
378         final int count = mTemperatureMap.size();
379         for (int i = 0; i < count; i++) {
380             postEventListenerLocked(mTemperatureMap.valueAt(i), listener,
381                     type);
382         }
383     }
384 
385     @GuardedBy("mLock")
postEventListenerLocked(Temperature temperature, IThermalEventListener listener, @Nullable Integer type)386     private void postEventListenerLocked(Temperature temperature,
387             IThermalEventListener listener,
388             @Nullable Integer type) {
389         // Skip if listener registered with a different type
390         if (type != null && type != temperature.getType()) {
391             return;
392         }
393         final boolean thermalCallbackQueued = FgThread.getHandler().post(() -> {
394             try {
395                 listener.notifyThrottling(temperature);
396             } catch (RemoteException | RuntimeException e) {
397                 Slog.e(TAG, "Thermal event callback failed to call", e);
398             }
399         });
400         if (!thermalCallbackQueued) {
401             Slog.e(TAG, "Thermal event callback failed to queue");
402         }
403     }
404 
405     @GuardedBy("mLock")
notifyEventListenersLocked(Temperature temperature)406     private void notifyEventListenersLocked(Temperature temperature) {
407         final int length = mThermalEventListeners.beginBroadcast();
408         try {
409             for (int i = 0; i < length; i++) {
410                 final IThermalEventListener listener =
411                         mThermalEventListeners.getBroadcastItem(i);
412                 final Integer type =
413                         (Integer) mThermalEventListeners.getBroadcastCookie(i);
414                 postEventListenerLocked(temperature, listener, type);
415             }
416         } finally {
417             mThermalEventListeners.finishBroadcast();
418         }
419         EventLog.writeEvent(EventLogTags.THERMAL_CHANGED, temperature.getName(),
420                 temperature.getType(), temperature.getValue(), temperature.getStatus(), mStatus);
421     }
422 
shutdownIfNeeded(Temperature temperature)423     private void shutdownIfNeeded(Temperature temperature) {
424         if (temperature.getStatus() != Temperature.THROTTLING_SHUTDOWN) {
425             return;
426         }
427         final PowerManager powerManager = getContext().getSystemService(PowerManager.class);
428         switch (temperature.getType()) {
429             case Temperature.TYPE_CPU:
430                 // Fall through
431             case Temperature.TYPE_GPU:
432                 // Fall through
433             case Temperature.TYPE_NPU:
434                 // Fall through
435             case Temperature.TYPE_SKIN:
436                 powerManager.shutdown(false, PowerManager.SHUTDOWN_THERMAL_STATE, false);
437                 break;
438             case Temperature.TYPE_BATTERY:
439                 powerManager.shutdown(false, PowerManager.SHUTDOWN_BATTERY_THERMAL_STATE, false);
440                 break;
441         }
442     }
443 
onTemperatureChanged(Temperature temperature, boolean sendCallback)444     private void onTemperatureChanged(Temperature temperature, boolean sendCallback) {
445         shutdownIfNeeded(temperature);
446         synchronized (mLock) {
447             Temperature old = mTemperatureMap.put(temperature.getName(), temperature);
448             if (old == null || old.getStatus() != temperature.getStatus()) {
449                 notifyEventListenersLocked(temperature);
450             }
451             if (sendCallback) {
452                 onTemperatureMapChangedLocked();
453             }
454         }
455         if (sendCallback && Flags.allowThermalThresholdsCallback()
456                 && temperature.getType() == Temperature.TYPE_SKIN) {
457             final HeadroomCallbackData data;
458             synchronized (mTemperatureWatcher.mSamples) {
459                 if (DEBUG) {
460                     Slog.d(TAG, "Updating new temperature: " + temperature);
461                 }
462                 mTemperatureWatcher.updateTemperatureSampleLocked(System.currentTimeMillis(),
463                         temperature);
464                 mTemperatureWatcher.mCachedHeadrooms.clear();
465                 data = mTemperatureWatcher.getHeadroomCallbackDataLocked();
466             }
467             synchronized (mLock) {
468                 checkAndNotifyHeadroomListenersLocked(data);
469             }
470         }
471     }
472 
registerStatsCallbacks()473     private void registerStatsCallbacks() {
474         final StatsManager statsManager = mContext.getSystemService(StatsManager.class);
475         if (statsManager != null) {
476             statsManager.setPullAtomCallback(
477                     FrameworkStatsLog.THERMAL_HEADROOM_THRESHOLDS,
478                     null, // use default PullAtomMetadata values
479                     DIRECT_EXECUTOR,
480                     this::onPullAtom);
481         }
482     }
483 
onPullAtom(int atomTag, @NonNull List<StatsEvent> data)484     private int onPullAtom(int atomTag, @NonNull List<StatsEvent> data) {
485         if (atomTag == FrameworkStatsLog.THERMAL_HEADROOM_THRESHOLDS) {
486             final float[] thresholds;
487             synchronized (mTemperatureWatcher.mSamples) {
488                 thresholds = Arrays.copyOf(mTemperatureWatcher.mHeadroomThresholds,
489                         mTemperatureWatcher.mHeadroomThresholds.length);
490             }
491             data.add(
492                     FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.THERMAL_HEADROOM_THRESHOLDS,
493                             thresholds));
494         }
495         return android.app.StatsManager.PULL_SUCCESS;
496     }
497 
498     @VisibleForTesting
499     final IThermalService.Stub mService = new IThermalService.Stub() {
500         @Override
501         public boolean registerThermalEventListener(IThermalEventListener listener) {
502             getContext().enforceCallingOrSelfPermission(
503                     android.Manifest.permission.DEVICE_POWER, null);
504             synchronized (mLock) {
505                 final long token = Binder.clearCallingIdentity();
506                 try {
507                     if (!mThermalEventListeners.register(listener, null)) {
508                         return false;
509                     }
510                     // Notify its callback after new client registered.
511                     postEventListenerCurrentTemperaturesLocked(listener, null);
512                     return true;
513                 } finally {
514                     Binder.restoreCallingIdentity(token);
515                 }
516             }
517         }
518 
519         @Override
520         public boolean registerThermalEventListenerWithType(IThermalEventListener listener,
521                 int type) {
522             getContext().enforceCallingOrSelfPermission(
523                     android.Manifest.permission.DEVICE_POWER, null);
524             synchronized (mLock) {
525                 final long token = Binder.clearCallingIdentity();
526                 try {
527                     if (!mThermalEventListeners.register(listener, type)) {
528                         return false;
529                     }
530                     // Notify its callback after new client registered.
531                     postEventListenerCurrentTemperaturesLocked(listener, type);
532                     return true;
533                 } finally {
534                     Binder.restoreCallingIdentity(token);
535                 }
536             }
537         }
538 
539         @Override
540         public boolean unregisterThermalEventListener(IThermalEventListener listener) {
541             getContext().enforceCallingOrSelfPermission(
542                     android.Manifest.permission.DEVICE_POWER, null);
543             synchronized (mLock) {
544                 final long token = Binder.clearCallingIdentity();
545                 try {
546                     return mThermalEventListeners.unregister(listener);
547                 } finally {
548                     Binder.restoreCallingIdentity(token);
549                 }
550             }
551         }
552 
553         @Override
554         public Temperature[] getCurrentTemperatures() {
555             getContext().enforceCallingOrSelfPermission(
556                     android.Manifest.permission.DEVICE_POWER, null);
557             final long token = Binder.clearCallingIdentity();
558             try {
559                 if (!mHalReady.get()) {
560                     return new Temperature[0];
561                 }
562                 final List<Temperature> curr = mHalWrapper.getCurrentTemperatures(
563                         false, 0 /* not used */);
564                 return curr.toArray(new Temperature[curr.size()]);
565             } finally {
566                 Binder.restoreCallingIdentity(token);
567             }
568         }
569 
570         @Override
571         public Temperature[] getCurrentTemperaturesWithType(int type) {
572             getContext().enforceCallingOrSelfPermission(
573                     android.Manifest.permission.DEVICE_POWER, null);
574             final long token = Binder.clearCallingIdentity();
575             try {
576                 if (!mHalReady.get()) {
577                     return new Temperature[0];
578                 }
579                 final List<Temperature> curr = mHalWrapper.getCurrentTemperatures(true, type);
580                 return curr.toArray(new Temperature[curr.size()]);
581             } finally {
582                 Binder.restoreCallingIdentity(token);
583             }
584         }
585 
586         @Override
587         public boolean registerThermalStatusListener(IThermalStatusListener listener) {
588             synchronized (mLock) {
589                 // Notify its callback after new client registered.
590                 final long token = Binder.clearCallingIdentity();
591                 try {
592                     if (!mThermalStatusListeners.register(listener)) {
593                         return false;
594                     }
595                     // Notify its callback after new client registered.
596                     postStatusListenerLocked(listener);
597                     return true;
598                 } finally {
599                     Binder.restoreCallingIdentity(token);
600                 }
601             }
602         }
603 
604         @Override
605         public boolean unregisterThermalStatusListener(IThermalStatusListener listener) {
606             synchronized (mLock) {
607                 final long token = Binder.clearCallingIdentity();
608                 try {
609                     return mThermalStatusListeners.unregister(listener);
610                 } finally {
611                     Binder.restoreCallingIdentity(token);
612                 }
613             }
614         }
615 
616         @Override
617         public int getCurrentThermalStatus() {
618             synchronized (mLock) {
619                 final long token = Binder.clearCallingIdentity();
620                 try {
621                     FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_STATUS_CALLED,
622                             Binder.getCallingUid(),
623                             mHalReady.get()
624                                     ? THERMAL_STATUS_CALLED__API_STATUS__SUCCESS
625                                     : THERMAL_STATUS_CALLED__API_STATUS__HAL_NOT_READY,
626                             thermalSeverityToStatsdStatus(mStatus));
627                     return mStatus;
628                 } finally {
629                     Binder.restoreCallingIdentity(token);
630                 }
631             }
632         }
633 
634         @Override
635         public CoolingDevice[] getCurrentCoolingDevices() {
636             getContext().enforceCallingOrSelfPermission(
637                     android.Manifest.permission.DEVICE_POWER, null);
638             final long token = Binder.clearCallingIdentity();
639             try {
640                 if (!mHalReady.get()) {
641                     return new CoolingDevice[0];
642                 }
643                 final List<CoolingDevice> devList = mHalWrapper.getCurrentCoolingDevices(
644                         false, 0);
645                 return devList.toArray(new CoolingDevice[devList.size()]);
646             } finally {
647                 Binder.restoreCallingIdentity(token);
648             }
649         }
650 
651         @Override
652         public CoolingDevice[] getCurrentCoolingDevicesWithType(int type) {
653             getContext().enforceCallingOrSelfPermission(
654                     android.Manifest.permission.DEVICE_POWER, null);
655             final long token = Binder.clearCallingIdentity();
656             try {
657                 if (!mHalReady.get()) {
658                     return new CoolingDevice[0];
659                 }
660                 final List<CoolingDevice> devList = mHalWrapper.getCurrentCoolingDevices(
661                         true, type);
662                 return devList.toArray(new CoolingDevice[devList.size()]);
663             } finally {
664                 Binder.restoreCallingIdentity(token);
665             }
666         }
667 
668         @Override
669         public boolean registerThermalHeadroomListener(IThermalHeadroomListener listener) {
670             if (!mHalReady.get()) {
671                 return false;
672             }
673             synchronized (mLock) {
674                 // Notify its callback after new client registered.
675                 final long token = Binder.clearCallingIdentity();
676                 try {
677                     if (!mThermalHeadroomListeners.register(listener)) {
678                         return false;
679                     }
680                 } finally {
681                     Binder.restoreCallingIdentity(token);
682                 }
683             }
684             final HeadroomCallbackData data;
685             synchronized (mTemperatureWatcher.mSamples) {
686                 data = mTemperatureWatcher.getHeadroomCallbackDataLocked();
687             }
688             // Notify its callback after new client registered.
689             synchronized (mLock) {
690                 postHeadroomListenerLocked(listener, data);
691             }
692             return true;
693         }
694 
695         @Override
696         public boolean unregisterThermalHeadroomListener(IThermalHeadroomListener listener) {
697             synchronized (mLock) {
698                 final long token = Binder.clearCallingIdentity();
699                 try {
700                     return mThermalHeadroomListeners.unregister(listener);
701                 } finally {
702                     Binder.restoreCallingIdentity(token);
703                 }
704             }
705         }
706 
707         @Override
708         public float getThermalHeadroom(int forecastSeconds) {
709             if (!mHalReady.get()) {
710                 FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED, getCallingUid(),
711                         FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__HAL_NOT_READY,
712                         Float.NaN, forecastSeconds);
713                 return Float.NaN;
714             }
715 
716             if (forecastSeconds < MIN_FORECAST_SEC || forecastSeconds > MAX_FORECAST_SEC) {
717                 if (DEBUG) {
718                     Slog.d(TAG, "Invalid forecastSeconds: " + forecastSeconds);
719                 }
720                 FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED, getCallingUid(),
721                         FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__INVALID_ARGUMENT,
722                         Float.NaN, forecastSeconds);
723                 return Float.NaN;
724             }
725 
726             return mTemperatureWatcher.getForecast(forecastSeconds);
727         }
728 
729         @Override
730         public float[] getThermalHeadroomThresholds() {
731             if (!mHalReady.get()) {
732                 FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_THRESHOLDS_CALLED,
733                         Binder.getCallingUid(),
734                         THERMAL_HEADROOM_THRESHOLDS_CALLED__API_STATUS__HAL_NOT_READY);
735                 throw new IllegalStateException("Thermal HAL connection is not initialized");
736             }
737             if (!Flags.allowThermalHeadroomThresholds()) {
738                 FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_THRESHOLDS_CALLED,
739                         Binder.getCallingUid(),
740                         THERMAL_HEADROOM_THRESHOLDS_CALLED__API_STATUS__FEATURE_NOT_SUPPORTED);
741                 throw new UnsupportedOperationException("Thermal headroom thresholds not enabled");
742             }
743             FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_THRESHOLDS_CALLED,
744                     Binder.getCallingUid(),
745                     THERMAL_HEADROOM_THRESHOLDS_CALLED__API_STATUS__SUCCESS);
746             return mTemperatureWatcher.getHeadroomThresholds();
747         }
748 
749         @Override
750         protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
751             dumpInternal(fd, pw, args);
752         }
753 
754         private boolean isCallerShell() {
755             final int callingUid = Binder.getCallingUid();
756             return callingUid == Process.SHELL_UID || callingUid == Process.ROOT_UID;
757         }
758 
759         @Override
760         public void onShellCommand(FileDescriptor in, FileDescriptor out,
761                 FileDescriptor err, String[] args, ShellCallback callback,
762                 ResultReceiver resultReceiver) {
763             if (!isCallerShell()) {
764                 Slog.w(TAG, "Only shell is allowed to call thermalservice shell commands");
765                 return;
766             }
767             (new ThermalShellCommand()).exec(
768                     this, in, out, err, args, callback, resultReceiver);
769         }
770 
771     };
772 
thermalSeverityToStatsdStatus(int severity)773     private static int thermalSeverityToStatsdStatus(int severity) {
774         switch (severity) {
775             case PowerManager.THERMAL_STATUS_NONE:
776                 return FrameworkStatsLog.THERMAL_STATUS_CALLED__STATUS__NONE;
777             case PowerManager.THERMAL_STATUS_LIGHT:
778                 return FrameworkStatsLog.THERMAL_STATUS_CALLED__STATUS__LIGHT;
779             case PowerManager.THERMAL_STATUS_MODERATE:
780                 return FrameworkStatsLog.THERMAL_STATUS_CALLED__STATUS__MODERATE;
781             case PowerManager.THERMAL_STATUS_SEVERE:
782                 return FrameworkStatsLog.THERMAL_STATUS_CALLED__STATUS__SEVERE;
783             case PowerManager.THERMAL_STATUS_CRITICAL:
784                 return FrameworkStatsLog.THERMAL_STATUS_CALLED__STATUS__CRITICAL;
785             case PowerManager.THERMAL_STATUS_EMERGENCY:
786                 return FrameworkStatsLog.THERMAL_STATUS_CALLED__STATUS__EMERGENCY;
787             case PowerManager.THERMAL_STATUS_SHUTDOWN:
788                 return FrameworkStatsLog.THERMAL_STATUS_CALLED__STATUS__SHUTDOWN;
789             default:
790                 return FrameworkStatsLog.THERMAL_STATUS_CALLED__STATUS__NONE;
791         }
792     }
793 
dumpItemsLocked(PrintWriter pw, String prefix, Collection<?> items)794     private static void dumpItemsLocked(PrintWriter pw, String prefix,
795             Collection<?> items) {
796         for (Iterator iterator = items.iterator(); iterator.hasNext();) {
797             pw.println(prefix + iterator.next().toString());
798         }
799     }
800 
dumpTemperatureThresholds(PrintWriter pw, String prefix, List<TemperatureThreshold> thresholds)801     private static void dumpTemperatureThresholds(PrintWriter pw, String prefix,
802             List<TemperatureThreshold> thresholds) {
803         for (TemperatureThreshold threshold : thresholds) {
804             pw.println(prefix + "TemperatureThreshold{mType=" + threshold.type
805                     + ", mName=" + threshold.name
806                     + ", mHotThrottlingThresholds=" + Arrays.toString(
807                     threshold.hotThrottlingThresholds)
808                     + ", mColdThrottlingThresholds=" + Arrays.toString(
809                     threshold.coldThrottlingThresholds)
810                     + "}");
811         }
812     }
813 
814     @VisibleForTesting
dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args)815     void dumpInternal(FileDescriptor fd, PrintWriter pw, String[] args) {
816         if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) {
817             return;
818         }
819         final long token = Binder.clearCallingIdentity();
820         try {
821             synchronized (mLock) {
822                 pw.println("IsStatusOverride: " + mIsStatusOverride);
823                 pw.println("ThermalEventListeners:");
824                 mThermalEventListeners.dump(pw, "\t");
825                 pw.println("ThermalStatusListeners:");
826                 mThermalStatusListeners.dump(pw, "\t");
827                 pw.println("Thermal Status: " + mStatus);
828                 pw.println("Cached temperatures:");
829                 dumpItemsLocked(pw, "\t", mTemperatureMap.values());
830                 pw.println("HAL Ready: " + mHalReady.get());
831                 if (mHalReady.get()) {
832                     pw.println("HAL connection:");
833                     mHalWrapper.dump(pw, "\t");
834                     pw.println("Current temperatures from HAL:");
835                     dumpItemsLocked(pw, "\t",
836                             mHalWrapper.getCurrentTemperatures(false, 0));
837                     pw.println("Current cooling devices from HAL:");
838                     dumpItemsLocked(pw, "\t",
839                             mHalWrapper.getCurrentCoolingDevices(false, 0));
840                     pw.println("Temperature static thresholds from HAL:");
841                     dumpTemperatureThresholds(pw, "\t",
842                             mHalWrapper.getTemperatureThresholds(false, 0));
843                 }
844             }
845             if (Flags.allowThermalHeadroomThresholds()) {
846                 synchronized (mTemperatureWatcher.mSamples) {
847                     pw.println("Temperature headroom thresholds:");
848                     pw.println(Arrays.toString(mTemperatureWatcher.mHeadroomThresholds));
849                 }
850             }
851         } finally {
852             Binder.restoreCallingIdentity(token);
853         }
854     }
855 
856     class ThermalShellCommand extends ShellCommand {
857         @Override
onCommand(String cmd)858         public int onCommand(String cmd) {
859             switch (cmd != null ? cmd : "") {
860                 case "inject-temperature":
861                     return runInjectTemperature();
862                 case "override-status":
863                     return runOverrideStatus();
864                 case "reset":
865                     return runReset();
866                 case "headroom":
867                     return runHeadroom();
868                 default:
869                     return handleDefaultCommands(cmd);
870             }
871         }
872 
runReset()873         private int runReset() {
874             final long token = Binder.clearCallingIdentity();
875             try {
876                 synchronized (mLock) {
877                     mIsStatusOverride = false;
878                     onTemperatureMapChangedLocked();
879                     return 0;
880                 }
881             } finally {
882                 Binder.restoreCallingIdentity(token);
883             }
884         }
885 
886 
runInjectTemperature()887         private int runInjectTemperature() {
888             final long token = Binder.clearCallingIdentity();
889             try {
890                 final PrintWriter pw = getOutPrintWriter();
891                 int type;
892                 String typeName = getNextArgRequired();
893                 switch (typeName.toUpperCase()) {
894                     case "UNKNOWN":
895                         type = Temperature.TYPE_UNKNOWN;
896                         break;
897                     case "CPU":
898                         type = Temperature.TYPE_CPU;
899                         break;
900                     case "GPU":
901                         type = Temperature.TYPE_GPU;
902                         break;
903                     case "BATTERY":
904                         type = Temperature.TYPE_BATTERY;
905                         break;
906                     case "SKIN":
907                         type = Temperature.TYPE_SKIN;
908                         break;
909                     case "USB_PORT":
910                         type = Temperature.TYPE_USB_PORT;
911                         break;
912                     case "POWER_AMPLIFIER":
913                         type = Temperature.TYPE_POWER_AMPLIFIER;
914                         break;
915                     case "BCL_VOLTAGE":
916                         type = Temperature.TYPE_BCL_VOLTAGE;
917                         break;
918                     case "BCL_CURRENT":
919                         type = Temperature.TYPE_BCL_CURRENT;
920                         break;
921                     case "BCL_PERCENTAGE":
922                         type = Temperature.TYPE_BCL_PERCENTAGE;
923                         break;
924                     case "NPU":
925                         type = Temperature.TYPE_NPU;
926                         break;
927                     case "TPU":
928                         type = Temperature.TYPE_TPU;
929                         break;
930                     case "DISPLAY":
931                         type = Temperature.TYPE_DISPLAY;
932                         break;
933                     case "MODEM":
934                         type = Temperature.TYPE_MODEM;
935                         break;
936                     case "SOC":
937                         type = Temperature.TYPE_SOC;
938                         break;
939                     case "WIFI":
940                         type = Temperature.TYPE_WIFI;
941                         break;
942                     case "CAMERA":
943                         type = Temperature.TYPE_CAMERA;
944                         break;
945                     case "FLASHLIGHT":
946                         type = Temperature.TYPE_FLASHLIGHT;
947                         break;
948                     case "SPEAKER":
949                         type = Temperature.TYPE_SPEAKER;
950                         break;
951                     case "AMBIENT":
952                         type = Temperature.TYPE_AMBIENT;
953                         break;
954                     case "POGO":
955                         type = Temperature.TYPE_POGO;
956                         break;
957                     default:
958                         pw.println("Invalid temperature type: " + typeName);
959                         return -1;
960                 }
961                 int throttle;
962                 String throttleName = getNextArgRequired();
963                 switch (throttleName.toUpperCase()) {
964                     case "NONE":
965                         throttle = Temperature.THROTTLING_NONE;
966                         break;
967                     case "LIGHT":
968                         throttle = Temperature.THROTTLING_LIGHT;
969                         break;
970                     case "MODERATE":
971                         throttle = Temperature.THROTTLING_MODERATE;
972                         break;
973                     case "SEVERE":
974                         throttle = Temperature.THROTTLING_SEVERE;
975                         break;
976                     case "CRITICAL":
977                         throttle = Temperature.THROTTLING_CRITICAL;
978                         break;
979                     case "EMERGENCY":
980                         throttle = Temperature.THROTTLING_EMERGENCY;
981                         break;
982                     case "SHUTDOWN":
983                         throttle = Temperature.THROTTLING_SHUTDOWN;
984                         break;
985                     default:
986                         pw.println("Invalid throttle status: " + throttleName);
987                         return -1;
988                 }
989                 String name = getNextArgRequired();
990                 float value = 28.0f;
991                 try {
992                     String valueStr = getNextArg();
993                     if (valueStr != null) value = Float.parseFloat(valueStr);
994                 } catch (RuntimeException ex) {
995                     pw.println("Error: " + ex.toString());
996                     return -1;
997                 }
998                 onTemperatureChanged(new Temperature(value, type, name, throttle), true);
999                 return 0;
1000             } finally {
1001                 Binder.restoreCallingIdentity(token);
1002             }
1003         }
1004 
runOverrideStatus()1005         private int runOverrideStatus() {
1006             final long token = Binder.clearCallingIdentity();
1007             try {
1008                 final PrintWriter pw = getOutPrintWriter();
1009                 int status;
1010                 try {
1011                     status = Integer.parseInt(getNextArgRequired());
1012                 } catch (RuntimeException ex) {
1013                     pw.println("Error: " + ex.toString());
1014                     return -1;
1015                 }
1016                 if (!Temperature.isValidStatus(status)) {
1017                     pw.println("Invalid status: " + status);
1018                     return -1;
1019                 }
1020                 synchronized (mLock) {
1021                     mIsStatusOverride = true;
1022                     setStatusLocked(status);
1023                 }
1024                 return 0;
1025             } finally {
1026                 Binder.restoreCallingIdentity(token);
1027             }
1028         }
1029 
runHeadroom()1030         private int runHeadroom() {
1031             final long token = Binder.clearCallingIdentity();
1032             try {
1033                 final PrintWriter pw = getOutPrintWriter();
1034                 int forecastSecs;
1035                 try {
1036                     forecastSecs = Integer.parseInt(getNextArgRequired());
1037                 } catch (RuntimeException ex) {
1038                     pw.println("Error: " + ex);
1039                     return -1;
1040                 }
1041                 if (!mHalReady.get()) {
1042                     pw.println("Error: thermal HAL is not ready");
1043                     return -1;
1044                 }
1045 
1046                 if (forecastSecs < MIN_FORECAST_SEC || forecastSecs > MAX_FORECAST_SEC) {
1047                     pw.println(
1048                             "Error: forecast second input should be in range [" + MIN_FORECAST_SEC
1049                                     + "," + MAX_FORECAST_SEC + "]");
1050                     return -1;
1051                 }
1052                 float headroom = mTemperatureWatcher.getForecast(forecastSecs);
1053                 pw.println("Headroom in " + forecastSecs + " seconds: " + headroom);
1054                 return 0;
1055             } finally {
1056                 Binder.restoreCallingIdentity(token);
1057             }
1058         }
1059 
1060         @Override
onHelp()1061         public void onHelp() {
1062             final PrintWriter pw = getOutPrintWriter();
1063             pw.println("Thermal service (thermalservice) commands:");
1064             pw.println("  help");
1065             pw.println("    Print this help text.");
1066             pw.println("");
1067             pw.println("  inject-temperature TYPE STATUS NAME [VALUE]");
1068             pw.println("    injects a new temperature sample for the specified device.");
1069             pw.println("    type and status strings follow the names in android.os.Temperature.");
1070             pw.println("  override-status STATUS");
1071             pw.println("    sets and locks the thermal status of the device to STATUS.");
1072             pw.println("    status code is defined in android.os.Temperature.");
1073             pw.println("  reset");
1074             pw.println("    unlocks the thermal status of the device.");
1075             pw.println("  headroom FORECAST_SECONDS");
1076             pw.println("    gets the thermal headroom forecast in specified seconds, from ["
1077                     + MIN_FORECAST_SEC + "," + MAX_FORECAST_SEC + "].");
1078             pw.println();
1079         }
1080     }
1081 
1082     abstract static class ThermalHalWrapper {
1083         protected static final String TAG = ThermalHalWrapper.class.getSimpleName();
1084 
1085         /** Lock to protect HAL handle. */
1086         protected final Object mHalLock = new Object();
1087 
1088         interface WrapperThermalChangedCallback {
onTemperatureChanged(Temperature temperature)1089             void onTemperatureChanged(Temperature temperature);
onThresholdChanged(TemperatureThreshold threshold)1090             void onThresholdChanged(TemperatureThreshold threshold);
1091         }
1092 
1093         /** Temperature callback. */
1094         protected WrapperThermalChangedCallback mCallback;
1095 
1096         /** Cookie for matching the right end point. */
1097         protected static final int THERMAL_HAL_DEATH_COOKIE = 5612;
1098 
1099         @VisibleForTesting
setCallback(WrapperThermalChangedCallback cb)1100         protected void setCallback(WrapperThermalChangedCallback cb) {
1101             mCallback = cb;
1102         }
1103 
getCurrentTemperatures(boolean shouldFilter, int type)1104         protected abstract List<Temperature> getCurrentTemperatures(boolean shouldFilter,
1105                 int type);
1106 
getCurrentCoolingDevices(boolean shouldFilter, int type)1107         protected abstract List<CoolingDevice> getCurrentCoolingDevices(boolean shouldFilter,
1108                 int type);
1109 
1110         @NonNull
getTemperatureThresholds(boolean shouldFilter, int type)1111         protected abstract List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
1112                 int type);
1113 
forecastSkinTemperature(int forecastSeconds)1114         protected abstract float forecastSkinTemperature(int forecastSeconds);
1115 
connectToHal()1116         protected abstract boolean connectToHal();
1117 
dump(PrintWriter pw, String prefix)1118         protected abstract void dump(PrintWriter pw, String prefix);
1119 
resendCurrentTemperatures()1120         protected void resendCurrentTemperatures() {
1121             synchronized (mHalLock) {
1122                 List<Temperature> temperatures = getCurrentTemperatures(false, 0);
1123                 final int count = temperatures.size();
1124                 for (int i = 0; i < count; i++) {
1125                     mCallback.onTemperatureChanged(temperatures.get(i));
1126                 }
1127             }
1128         }
1129 
1130         final class DeathRecipient implements HwBinder.DeathRecipient {
1131             @Override
serviceDied(long cookie)1132             public void serviceDied(long cookie) {
1133                 if (cookie == THERMAL_HAL_DEATH_COOKIE) {
1134                     Slog.e(TAG, "Thermal HAL service died cookie: " + cookie);
1135                     synchronized (mHalLock) {
1136                         connectToHal();
1137                         // Post to listeners after reconnect to HAL.
1138                         resendCurrentTemperatures();
1139                     }
1140                 }
1141             }
1142         }
1143     }
1144 
1145     @VisibleForTesting
1146     static class ThermalHalAidlWrapper extends ThermalHalWrapper implements IBinder.DeathRecipient {
1147         /* Proxy object for the Thermal HAL AIDL service. */
1148 
1149         @GuardedBy("mHalLock")
1150         private IThermal mInstance = null;
1151 
getHalInstance()1152         private IThermal getHalInstance() {
1153             synchronized (mHalLock) {
1154                 return mInstance;
1155             }
1156         }
1157 
1158         /** Callback for Thermal HAL AIDL. */
1159         private final IThermalChangedCallback mThermalCallbackAidl =
1160                 new IThermalChangedCallback.Stub() {
1161                     @Override
1162                     public void notifyThrottling(
1163                             android.hardware.thermal.Temperature temperature) {
1164                         Temperature svcTemperature = new Temperature(temperature.value,
1165                                 temperature.type, temperature.name, temperature.throttlingStatus);
1166                         final long token = Binder.clearCallingIdentity();
1167                         try {
1168                             mCallback.onTemperatureChanged(svcTemperature);
1169                         } finally {
1170                             Binder.restoreCallingIdentity(token);
1171                         }
1172                     }
1173 
1174                     @Override
1175                     public void notifyThresholdChanged(TemperatureThreshold threshold) {
1176                         if (Flags.allowThermalThresholdsCallback()) {
1177                             if (threshold.type == TemperatureType.SKIN) {
1178                                 mCallback.onThresholdChanged(threshold);
1179                             }
1180                         }
1181                     }
1182 
1183                     @Override
1184                     public int getInterfaceVersion() throws RemoteException {
1185                         return this.VERSION;
1186                     }
1187 
1188                     @Override
1189                     public String getInterfaceHash() throws RemoteException {
1190                         return this.HASH;
1191                     }
1192                 };
1193 
ThermalHalAidlWrapper(WrapperThermalChangedCallback callback)1194         ThermalHalAidlWrapper(WrapperThermalChangedCallback callback) {
1195             mCallback = callback;
1196         }
1197 
1198         @Override
getCurrentTemperatures(boolean shouldFilter, int type)1199         protected List<Temperature> getCurrentTemperatures(boolean shouldFilter,
1200                 int type) {
1201             final IThermal instance = getHalInstance();
1202             final List<Temperature> ret = new ArrayList<>();
1203             if (instance == null) {
1204                 return ret;
1205             }
1206             try {
1207                 final android.hardware.thermal.Temperature[] halRet =
1208                         shouldFilter ? instance.getTemperaturesWithType(type)
1209                                 : instance.getTemperatures();
1210                 if (halRet == null) {
1211                     return ret;
1212                 }
1213                 for (android.hardware.thermal.Temperature t : halRet) {
1214                     if (!Temperature.isValidStatus(t.throttlingStatus)) {
1215                         Slog.e(TAG, "Invalid temperature status " + t.throttlingStatus
1216                                 + " received from AIDL HAL");
1217                         t.throttlingStatus = Temperature.THROTTLING_NONE;
1218                     }
1219                     if (shouldFilter && t.type != type) {
1220                         continue;
1221                     }
1222                     ret.add(new Temperature(t.value, t.type, t.name, t.throttlingStatus));
1223                 }
1224             } catch (IllegalArgumentException | IllegalStateException e) {
1225                 Slog.e(TAG, "Couldn't getCurrentCoolingDevices due to invalid status", e);
1226             } catch (RemoteException e) {
1227                 Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting", e);
1228                 synchronized (mHalLock) {
1229                     connectToHalIfNeededLocked(instance);
1230                 }
1231             }
1232             return ret;
1233         }
1234 
1235         @Override
getCurrentCoolingDevices(boolean shouldFilter, int type)1236         protected List<CoolingDevice> getCurrentCoolingDevices(boolean shouldFilter,
1237                 int type) {
1238             final IThermal instance = getHalInstance();
1239             final List<CoolingDevice> ret = new ArrayList<>();
1240             if (instance == null) {
1241                 return ret;
1242             }
1243             try {
1244                 final android.hardware.thermal.CoolingDevice[] halRet = shouldFilter
1245                         ? instance.getCoolingDevicesWithType(type)
1246                         : instance.getCoolingDevices();
1247                 if (halRet == null) {
1248                     return ret;
1249                 }
1250                 for (android.hardware.thermal.CoolingDevice t : halRet) {
1251                     if (!CoolingDevice.isValidType(t.type)) {
1252                         Slog.e(TAG, "Invalid cooling device type " + t.type + " from AIDL HAL");
1253                         continue;
1254                     }
1255                     if (shouldFilter && t.type != type) {
1256                         continue;
1257                     }
1258                     ret.add(new CoolingDevice(t.value, t.type, t.name));
1259                 }
1260             } catch (IllegalArgumentException | IllegalStateException e) {
1261                 Slog.e(TAG, "Couldn't getCurrentCoolingDevices due to invalid status", e);
1262             } catch (RemoteException e) {
1263                 Slog.e(TAG, "Couldn't getCurrentCoolingDevices, reconnecting", e);
1264                 synchronized (mHalLock) {
1265                     connectToHalIfNeededLocked(instance);
1266                 }
1267             }
1268             return ret;
1269         }
1270 
1271         @Override
1272         @NonNull
getTemperatureThresholds( boolean shouldFilter, int type)1273         protected List<TemperatureThreshold> getTemperatureThresholds(
1274                 boolean shouldFilter, int type) {
1275             final IThermal instance = getHalInstance();
1276             final List<TemperatureThreshold> ret = new ArrayList<>();
1277             if (instance == null) {
1278                 return ret;
1279             }
1280             try {
1281                 final TemperatureThreshold[] halRet =
1282                         shouldFilter ? instance.getTemperatureThresholdsWithType(type)
1283                                 : instance.getTemperatureThresholds();
1284                 if (halRet == null) {
1285                     return ret;
1286                 }
1287                 if (shouldFilter) {
1288                     return Arrays.stream(halRet).filter(t -> t.type == type).collect(
1289                             Collectors.toList());
1290                 }
1291                 return Arrays.asList(halRet);
1292             } catch (IllegalArgumentException | IllegalStateException e) {
1293                 Slog.e(TAG, "Couldn't getTemperatureThresholds due to invalid status", e);
1294             } catch (RemoteException e) {
1295                 Slog.e(TAG, "Couldn't getTemperatureThresholds, reconnecting...", e);
1296                 synchronized (mHalLock) {
1297                     connectToHalIfNeededLocked(instance);
1298                 }
1299             }
1300             return ret;
1301         }
1302 
1303         @Override
forecastSkinTemperature(int forecastSeconds)1304         protected float forecastSkinTemperature(int forecastSeconds) {
1305             final IThermal instance = getHalInstance();
1306             if (instance == null) {
1307                 return Float.NaN;
1308             }
1309             try {
1310                 return instance.forecastSkinTemperature(forecastSeconds);
1311             } catch (RemoteException e) {
1312                 Slog.e(TAG, "Couldn't forecastSkinTemperature, reconnecting...", e);
1313                 synchronized (mHalLock) {
1314                     connectToHalIfNeededLocked(instance);
1315                 }
1316             }
1317             return Float.NaN;
1318         }
1319 
1320         @Override
connectToHal()1321         protected boolean connectToHal() {
1322             synchronized (mHalLock) {
1323                 return connectToHalIfNeededLocked(mInstance);
1324             }
1325         }
1326 
1327         @GuardedBy("mHalLock")
connectToHalIfNeededLocked(IThermal instance)1328         protected boolean connectToHalIfNeededLocked(IThermal instance) {
1329             if (instance != mInstance) {
1330                 // instance has been updated since last used
1331                 return true;
1332             }
1333             IBinder binder = Binder.allowBlocking(ServiceManager.waitForDeclaredService(
1334                     IThermal.DESCRIPTOR + "/default"));
1335             initProxyAndRegisterCallbackLocked(binder);
1336             return mInstance != null;
1337         }
1338 
1339         @VisibleForTesting
initProxyAndRegisterCallback(IBinder binder)1340         void initProxyAndRegisterCallback(IBinder binder) {
1341             synchronized (mHalLock) {
1342                 initProxyAndRegisterCallbackLocked(binder);
1343             }
1344         }
1345 
1346         @GuardedBy("mHalLock")
initProxyAndRegisterCallbackLocked(IBinder binder)1347         protected void initProxyAndRegisterCallbackLocked(IBinder binder) {
1348             if (binder != null) {
1349                 mInstance = IThermal.Stub.asInterface(binder);
1350                 try {
1351                     binder.linkToDeath(this, 0);
1352                 } catch (RemoteException e) {
1353                     Slog.e(TAG, "Unable to connect IThermal AIDL instance", e);
1354                     connectToHal();
1355                 }
1356                 if (mInstance != null) {
1357                     try {
1358                         Slog.i(TAG, "Thermal HAL AIDL service connected with version "
1359                                 + mInstance.getInterfaceVersion());
1360                     } catch (RemoteException e) {
1361                         Slog.e(TAG, "Unable to read interface version from Thermal HAL", e);
1362                         connectToHal();
1363                         return;
1364                     }
1365                     try {
1366                         mInstance.registerThermalChangedCallback(mThermalCallbackAidl);
1367                     } catch (IllegalArgumentException | IllegalStateException e) {
1368                         Slog.e(TAG, "Couldn't registerThermalChangedCallback due to invalid status",
1369                                 e);
1370                     } catch (RemoteException e) {
1371                         Slog.e(TAG, "Unable to connect IThermal AIDL instance", e);
1372                         connectToHal();
1373                     }
1374                 }
1375             }
1376         }
1377 
1378         @Override
dump(PrintWriter pw, String prefix)1379         protected void dump(PrintWriter pw, String prefix) {
1380             synchronized (mHalLock) {
1381                 pw.print(prefix);
1382                 pw.println(
1383                         "ThermalHAL AIDL " + IThermal.VERSION + "  connected: " + (mInstance != null
1384                                 ? "yes" : "no"));
1385             }
1386         }
1387 
1388         @Override
binderDied()1389         public synchronized void binderDied() {
1390             Slog.w(TAG, "Thermal AIDL HAL died, reconnecting...");
1391             connectToHal();
1392         }
1393     }
1394 
1395     static class ThermalHal10Wrapper extends ThermalHalWrapper {
1396         /** Proxy object for the Thermal HAL 1.0 service. */
1397         @GuardedBy("mHalLock")
1398         private android.hardware.thermal.V1_0.IThermal mThermalHal10 = null;
1399 
ThermalHal10Wrapper(WrapperThermalChangedCallback callback)1400         ThermalHal10Wrapper(WrapperThermalChangedCallback callback) {
1401             mCallback = callback;
1402         }
1403 
1404         @Override
getCurrentTemperatures(boolean shouldFilter, int type)1405         protected List<Temperature> getCurrentTemperatures(boolean shouldFilter,
1406                 int type) {
1407             synchronized (mHalLock) {
1408                 List<Temperature> ret = new ArrayList<>();
1409                 if (mThermalHal10 == null) {
1410                     return ret;
1411                 }
1412                 try {
1413                     mThermalHal10.getTemperatures(
1414                             (ThermalStatus status,
1415                                     ArrayList<android.hardware.thermal.V1_0.Temperature>
1416                                             temperatures) -> {
1417                                 if (ThermalStatusCode.SUCCESS == status.code) {
1418                                     for (android.hardware.thermal.V1_0.Temperature
1419                                             temperature : temperatures) {
1420                                         if (shouldFilter && type != temperature.type) {
1421                                             continue;
1422                                         }
1423                                         // Thermal HAL 1.0 doesn't report current throttling status
1424                                         ret.add(new Temperature(
1425                                                 temperature.currentValue, temperature.type,
1426                                                 temperature.name,
1427                                                 Temperature.THROTTLING_NONE));
1428                                     }
1429                                 } else {
1430                                     Slog.e(TAG,
1431                                             "Couldn't get temperatures because of HAL error: "
1432                                                     + status.debugMessage);
1433                                 }
1434 
1435                             });
1436                 } catch (RemoteException e) {
1437                     Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e);
1438                     connectToHal();
1439                 }
1440                 return ret;
1441             }
1442         }
1443 
1444         @Override
getCurrentCoolingDevices(boolean shouldFilter, int type)1445         protected List<CoolingDevice> getCurrentCoolingDevices(boolean shouldFilter,
1446                 int type) {
1447             synchronized (mHalLock) {
1448                 List<CoolingDevice> ret = new ArrayList<>();
1449                 if (mThermalHal10 == null) {
1450                     return ret;
1451                 }
1452                 try {
1453                     mThermalHal10.getCoolingDevices((status, coolingDevices) -> {
1454                         if (ThermalStatusCode.SUCCESS == status.code) {
1455                             for (android.hardware.thermal.V1_0.CoolingDevice
1456                                     coolingDevice : coolingDevices) {
1457                                 if (shouldFilter && type != coolingDevice.type) {
1458                                     continue;
1459                                 }
1460                                 ret.add(new CoolingDevice(
1461                                         (long) coolingDevice.currentValue,
1462                                         coolingDevice.type,
1463                                         coolingDevice.name));
1464                             }
1465                         } else {
1466                             Slog.e(TAG,
1467                                     "Couldn't get cooling device because of HAL error: "
1468                                             + status.debugMessage);
1469                         }
1470 
1471                     });
1472                 } catch (RemoteException e) {
1473                     Slog.e(TAG, "Couldn't getCurrentCoolingDevices, reconnecting...", e);
1474                     connectToHal();
1475                 }
1476                 return ret;
1477             }
1478         }
1479 
1480         @Override
getTemperatureThresholds(boolean shouldFilter, int type)1481         protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
1482                 int type) {
1483             return new ArrayList<>();
1484         }
1485 
1486         @Override
connectToHal()1487         protected boolean connectToHal() {
1488             synchronized (mHalLock) {
1489                 try {
1490                     mThermalHal10 = android.hardware.thermal.V1_0.IThermal.getService(true);
1491                     mThermalHal10.linkToDeath(new DeathRecipient(),
1492                             THERMAL_HAL_DEATH_COOKIE);
1493                     Slog.i(TAG,
1494                             "Thermal HAL 1.0 service connected, no thermal call back will be "
1495                                     + "called due to legacy API.");
1496                 } catch (NoSuchElementException | RemoteException e) {
1497                     Slog.e(TAG,
1498                             "Thermal HAL 1.0 service not connected.");
1499                     mThermalHal10 = null;
1500                 }
1501                 return (mThermalHal10 != null);
1502             }
1503         }
1504 
1505         @Override
forecastSkinTemperature(int forecastSeconds)1506         protected float forecastSkinTemperature(int forecastSeconds) {
1507             throw new UnsupportedOperationException("Not supported in Thermal HAL 1.0");
1508         }
1509 
1510         @Override
dump(PrintWriter pw, String prefix)1511         protected void dump(PrintWriter pw, String prefix) {
1512             synchronized (mHalLock) {
1513                 pw.print(prefix);
1514                 pw.println("ThermalHAL 1.0 connected: " + (mThermalHal10 != null ? "yes"
1515                         : "no"));
1516             }
1517         }
1518     }
1519 
1520     static class ThermalHal11Wrapper extends ThermalHalWrapper {
1521         /** Proxy object for the Thermal HAL 1.1 service. */
1522         @GuardedBy("mHalLock")
1523         private android.hardware.thermal.V1_1.IThermal mThermalHal11 = null;
1524 
1525         /** HWbinder callback for Thermal HAL 1.1. */
1526         private final IThermalCallback.Stub mThermalCallback11 =
1527                 new IThermalCallback.Stub() {
1528                     @Override
1529                     public void notifyThrottling(boolean isThrottling,
1530                             android.hardware.thermal.V1_0.Temperature temperature) {
1531                         Temperature thermalSvcTemp = new Temperature(
1532                                 temperature.currentValue, temperature.type, temperature.name,
1533                                 isThrottling ? Temperature.THROTTLING_SEVERE
1534                                         : Temperature.THROTTLING_NONE);
1535                         final long token = Binder.clearCallingIdentity();
1536                         try {
1537                             mCallback.onTemperatureChanged(thermalSvcTemp);
1538                         } finally {
1539                             Binder.restoreCallingIdentity(token);
1540                         }
1541                     }
1542                 };
1543 
ThermalHal11Wrapper(WrapperThermalChangedCallback callback)1544         ThermalHal11Wrapper(WrapperThermalChangedCallback callback) {
1545             mCallback = callback;
1546         }
1547 
1548         @Override
getCurrentTemperatures(boolean shouldFilter, int type)1549         protected List<Temperature> getCurrentTemperatures(boolean shouldFilter,
1550                 int type) {
1551             synchronized (mHalLock) {
1552                 List<Temperature> ret = new ArrayList<>();
1553                 if (mThermalHal11 == null) {
1554                     return ret;
1555                 }
1556                 try {
1557                     mThermalHal11.getTemperatures(
1558                             (ThermalStatus status,
1559                                     ArrayList<android.hardware.thermal.V1_0.Temperature>
1560                                             temperatures) -> {
1561                                 if (ThermalStatusCode.SUCCESS == status.code) {
1562                                     for (android.hardware.thermal.V1_0.Temperature
1563                                             temperature : temperatures) {
1564                                         if (shouldFilter && type != temperature.type) {
1565                                             continue;
1566                                         }
1567                                         // Thermal HAL 1.1 doesn't report current throttling status
1568                                         ret.add(new Temperature(
1569                                                 temperature.currentValue, temperature.type,
1570                                                 temperature.name,
1571                                                 Temperature.THROTTLING_NONE));
1572                                     }
1573                                 } else {
1574                                     Slog.e(TAG,
1575                                             "Couldn't get temperatures because of HAL error: "
1576                                                     + status.debugMessage);
1577                                 }
1578 
1579                             });
1580                 } catch (RemoteException e) {
1581                     Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e);
1582                     connectToHal();
1583                 }
1584                 return ret;
1585             }
1586         }
1587 
1588         @Override
getCurrentCoolingDevices(boolean shouldFilter, int type)1589         protected List<CoolingDevice> getCurrentCoolingDevices(boolean shouldFilter,
1590                 int type) {
1591             synchronized (mHalLock) {
1592                 List<CoolingDevice> ret = new ArrayList<>();
1593                 if (mThermalHal11 == null) {
1594                     return ret;
1595                 }
1596                 try {
1597                     mThermalHal11.getCoolingDevices((status, coolingDevices) -> {
1598                         if (ThermalStatusCode.SUCCESS == status.code) {
1599                             for (android.hardware.thermal.V1_0.CoolingDevice
1600                                     coolingDevice : coolingDevices) {
1601                                 if (shouldFilter && type != coolingDevice.type) {
1602                                     continue;
1603                                 }
1604                                 ret.add(new CoolingDevice(
1605                                         (long) coolingDevice.currentValue,
1606                                         coolingDevice.type,
1607                                         coolingDevice.name));
1608                             }
1609                         } else {
1610                             Slog.e(TAG,
1611                                     "Couldn't get cooling device because of HAL error: "
1612                                             + status.debugMessage);
1613                         }
1614 
1615                     });
1616                 } catch (RemoteException e) {
1617                     Slog.e(TAG, "Couldn't getCurrentCoolingDevices, reconnecting...", e);
1618                     connectToHal();
1619                 }
1620                 return ret;
1621             }
1622         }
1623 
1624         @Override
getTemperatureThresholds(boolean shouldFilter, int type)1625         protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
1626                 int type) {
1627             return new ArrayList<>();
1628         }
1629 
1630         @Override
connectToHal()1631         protected boolean connectToHal() {
1632             synchronized (mHalLock) {
1633                 try {
1634                     mThermalHal11 = android.hardware.thermal.V1_1.IThermal.getService(true);
1635                     mThermalHal11.linkToDeath(new DeathRecipient(),
1636                             THERMAL_HAL_DEATH_COOKIE);
1637                     mThermalHal11.registerThermalCallback(mThermalCallback11);
1638                     Slog.i(TAG, "Thermal HAL 1.1 service connected, limited thermal functions "
1639                             + "due to legacy API.");
1640                 } catch (NoSuchElementException | RemoteException e) {
1641                     Slog.e(TAG, "Thermal HAL 1.1 service not connected.");
1642                     mThermalHal11 = null;
1643                 }
1644                 return (mThermalHal11 != null);
1645             }
1646         }
1647 
1648         @Override
forecastSkinTemperature(int forecastSeconds)1649         protected float forecastSkinTemperature(int forecastSeconds) {
1650             throw new UnsupportedOperationException("Not supported in Thermal HAL 1.1");
1651         }
1652 
1653         @Override
dump(PrintWriter pw, String prefix)1654         protected void dump(PrintWriter pw, String prefix) {
1655             synchronized (mHalLock) {
1656                 pw.print(prefix);
1657                 pw.println("ThermalHAL 1.1 connected: " + (mThermalHal11 != null ? "yes"
1658                         : "no"));
1659             }
1660         }
1661     }
1662 
1663     static class ThermalHal20Wrapper extends ThermalHalWrapper {
1664         /** Proxy object for the Thermal HAL 2.0 service. */
1665         @GuardedBy("mHalLock")
1666         private android.hardware.thermal.V2_0.IThermal mThermalHal20 = null;
1667 
1668         /** HWbinder callback for Thermal HAL 2.0. */
1669         private final android.hardware.thermal.V2_0.IThermalChangedCallback.Stub
1670                 mThermalCallback20 =
1671                 new android.hardware.thermal.V2_0.IThermalChangedCallback.Stub() {
1672                     @Override
1673                     public void notifyThrottling(
1674                             android.hardware.thermal.V2_0.Temperature temperature) {
1675                         Temperature thermalSvcTemp = new Temperature(
1676                                 temperature.value, temperature.type, temperature.name,
1677                                 temperature.throttlingStatus);
1678                         final long token = Binder.clearCallingIdentity();
1679                         try {
1680                             mCallback.onTemperatureChanged(thermalSvcTemp);
1681                         } finally {
1682                             Binder.restoreCallingIdentity(token);
1683                         }
1684                     }
1685                 };
1686 
ThermalHal20Wrapper(WrapperThermalChangedCallback callback)1687         ThermalHal20Wrapper(WrapperThermalChangedCallback callback) {
1688             mCallback = callback;
1689         }
1690 
1691         @Override
getCurrentTemperatures(boolean shouldFilter, int type)1692         protected List<Temperature> getCurrentTemperatures(boolean shouldFilter,
1693                 int type) {
1694             synchronized (mHalLock) {
1695                 List<Temperature> ret = new ArrayList<>();
1696                 if (mThermalHal20 == null) {
1697                     return ret;
1698                 }
1699                 try {
1700                     mThermalHal20.getCurrentTemperatures(shouldFilter, type,
1701                             (status, temperatures) -> {
1702                                 if (ThermalStatusCode.SUCCESS == status.code) {
1703                                     for (android.hardware.thermal.V2_0.Temperature
1704                                             temperature : temperatures) {
1705                                         if (!Temperature.isValidStatus(
1706                                                 temperature.throttlingStatus)) {
1707                                             Slog.e(TAG, "Invalid status data from HAL");
1708                                             temperature.throttlingStatus =
1709                                                     Temperature.THROTTLING_NONE;
1710                                         }
1711                                         ret.add(new Temperature(
1712                                                 temperature.value, temperature.type,
1713                                                 temperature.name,
1714                                                 temperature.throttlingStatus));
1715                                     }
1716                                 } else {
1717                                     Slog.e(TAG,
1718                                             "Couldn't get temperatures because of HAL error: "
1719                                                     + status.debugMessage);
1720                                 }
1721 
1722                             });
1723                 } catch (RemoteException e) {
1724                     Slog.e(TAG, "Couldn't getCurrentTemperatures, reconnecting...", e);
1725                     connectToHal();
1726                 }
1727                 return ret;
1728             }
1729         }
1730 
1731         @Override
getCurrentCoolingDevices(boolean shouldFilter, int type)1732         protected List<CoolingDevice> getCurrentCoolingDevices(boolean shouldFilter,
1733                 int type) {
1734             synchronized (mHalLock) {
1735                 List<CoolingDevice> ret = new ArrayList<>();
1736                 if (mThermalHal20 == null) {
1737                     return ret;
1738                 }
1739                 try {
1740                     mThermalHal20.getCurrentCoolingDevices(shouldFilter, type,
1741                             (status, coolingDevices) -> {
1742                                 if (ThermalStatusCode.SUCCESS == status.code) {
1743                                     for (android.hardware.thermal.V2_0.CoolingDevice
1744                                             coolingDevice : coolingDevices) {
1745                                         ret.add(new CoolingDevice(
1746                                                 coolingDevice.value, coolingDevice.type,
1747                                                 coolingDevice.name));
1748                                     }
1749                                 } else {
1750                                     Slog.e(TAG,
1751                                             "Couldn't get cooling device because of HAL error: "
1752                                                     + status.debugMessage);
1753                                 }
1754 
1755                             });
1756                 } catch (RemoteException e) {
1757                     Slog.e(TAG, "Couldn't getCurrentCoolingDevices, reconnecting...", e);
1758                     connectToHal();
1759                 }
1760                 return ret;
1761             }
1762         }
1763 
1764         @Override
getTemperatureThresholds(boolean shouldFilter, int type)1765         protected List<TemperatureThreshold> getTemperatureThresholds(boolean shouldFilter,
1766                 int type) {
1767             synchronized (mHalLock) {
1768                 List<TemperatureThreshold> ret = new ArrayList<>();
1769                 if (mThermalHal20 == null) {
1770                     return ret;
1771                 }
1772                 try {
1773                     mThermalHal20.getTemperatureThresholds(shouldFilter, type,
1774                             (status, thresholds) -> {
1775                                 if (ThermalStatusCode.SUCCESS == status.code) {
1776                                     ret.addAll(thresholds.stream().map(
1777                                             this::convertToAidlTemperatureThreshold).collect(
1778                                             Collectors.toList()));
1779                                 } else {
1780                                     Slog.e(TAG,
1781                                             "Couldn't get temperature thresholds because of HAL "
1782                                                     + "error: " + status.debugMessage);
1783                                 }
1784                             });
1785                 } catch (RemoteException e) {
1786                     Slog.e(TAG, "Couldn't getTemperatureThresholds, reconnecting...", e);
1787                 }
1788                 return ret;
1789             }
1790         }
1791 
convertToAidlTemperatureThreshold( android.hardware.thermal.V2_0.TemperatureThreshold threshold)1792         private TemperatureThreshold convertToAidlTemperatureThreshold(
1793                 android.hardware.thermal.V2_0.TemperatureThreshold threshold) {
1794             final TemperatureThreshold ret = new TemperatureThreshold();
1795             ret.name = threshold.name;
1796             ret.type = threshold.type;
1797             ret.coldThrottlingThresholds = threshold.coldThrottlingThresholds;
1798             ret.hotThrottlingThresholds = threshold.hotThrottlingThresholds;
1799             return ret;
1800         }
1801 
1802         @Override
connectToHal()1803         protected boolean connectToHal() {
1804             synchronized (mHalLock) {
1805                 try {
1806                     mThermalHal20 = android.hardware.thermal.V2_0.IThermal.getService(true);
1807                     mThermalHal20.linkToDeath(new DeathRecipient(), THERMAL_HAL_DEATH_COOKIE);
1808                     mThermalHal20.registerThermalChangedCallback(mThermalCallback20, false,
1809                             0 /* not used */);
1810                     Slog.i(TAG, "Thermal HAL 2.0 service connected.");
1811                 } catch (NoSuchElementException | RemoteException e) {
1812                     Slog.e(TAG, "Thermal HAL 2.0 service not connected.");
1813                     mThermalHal20 = null;
1814                 }
1815                 return (mThermalHal20 != null);
1816             }
1817         }
1818 
1819         @Override
forecastSkinTemperature(int forecastSeconds)1820         protected float forecastSkinTemperature(int forecastSeconds) {
1821             throw new UnsupportedOperationException("Not supported in Thermal HAL 2.0");
1822         }
1823 
1824         @Override
dump(PrintWriter pw, String prefix)1825         protected void dump(PrintWriter pw, String prefix) {
1826             synchronized (mHalLock) {
1827                 pw.print(prefix);
1828                 pw.println("ThermalHAL 2.0 connected: " + (mThermalHal20 != null ? "yes"
1829                         : "no"));
1830             }
1831         }
1832     }
1833 
1834     private static final class HeadroomCallbackData {
1835         float mHeadroom;
1836         float mForecastHeadroom;
1837         int mForecastSeconds;
1838         float[] mHeadroomThresholds;
1839 
HeadroomCallbackData(float headroom, float forecastHeadroom, int forecastSeconds, @NonNull float[] headroomThresholds)1840         HeadroomCallbackData(float headroom, float forecastHeadroom, int forecastSeconds,
1841                 @NonNull float[] headroomThresholds) {
1842             mHeadroom = headroom;
1843             mForecastHeadroom = forecastHeadroom;
1844             mForecastSeconds = forecastSeconds;
1845             mHeadroomThresholds = headroomThresholds;
1846         }
1847 
isSignificantDifferentFrom(HeadroomCallbackData other)1848         private boolean isSignificantDifferentFrom(HeadroomCallbackData other) {
1849             if (other == null) return true;
1850             // currently this is always the same as DEFAULT_FORECAST_SECONDS, when it's retried
1851             // from thermal HAL, we may want to adjust this.
1852             if (this.mForecastSeconds != other.mForecastSeconds) return true;
1853             if (Math.abs(this.mHeadroom - other.mHeadroom)
1854                     >= HEADROOM_CALLBACK_MIN_DIFFERENCE) return true;
1855             if (Math.abs(this.mForecastHeadroom - other.mForecastHeadroom)
1856                     >= HEADROOM_CALLBACK_MIN_DIFFERENCE) return true;
1857             for (int i = 0; i < this.mHeadroomThresholds.length; i++) {
1858                 if (Float.isNaN(this.mHeadroomThresholds[i]) != Float.isNaN(
1859                         other.mHeadroomThresholds[i])) {
1860                     return true;
1861                 }
1862                 if (Math.abs(this.mHeadroomThresholds[i] - other.mHeadroomThresholds[i])
1863                         >= HEADROOM_THRESHOLD_CALLBACK_MIN_DIFFERENCE) {
1864                     return true;
1865                 }
1866             }
1867             return false;
1868         }
1869 
1870         @Override
toString()1871         public String toString() {
1872             return "HeadroomCallbackData[mHeadroom=" + mHeadroom + ", mForecastHeadroom="
1873                     + mForecastHeadroom + ", mForecastSeconds=" + mForecastSeconds
1874                     + ", mHeadroomThresholds=" + Arrays.toString(mHeadroomThresholds) + "]";
1875         }
1876     }
1877 
1878     @VisibleForTesting
1879     class TemperatureWatcher {
1880         private static final int RING_BUFFER_SIZE = 30;
1881         private static final int INACTIVITY_THRESHOLD_MILLIS = 10000;
1882         @VisibleForTesting
1883         long mInactivityThresholdMillis = INACTIVITY_THRESHOLD_MILLIS;
1884 
1885         @GuardedBy("mSamples")
1886         private final Handler mHandler = BackgroundThread.getHandler();
1887 
1888         /**
1889          * Map of skin temperature sensor name to a corresponding list of samples
1890          * Updates to the samples should also clear the headroom cache.
1891          */
1892         @GuardedBy("mSamples")
1893         @VisibleForTesting
1894         final ArrayMap<String, ArrayList<Sample>> mSamples = new ArrayMap<>();
1895         @GuardedBy("mSamples")
1896         private final SparseArray<Float> mCachedHeadrooms = new SparseArray<>(2);
1897 
1898         /** Map of skin temperature sensor name to the corresponding SEVERE temperature threshold */
1899         @GuardedBy("mSamples")
1900         @VisibleForTesting
1901         ArrayMap<String, Float> mSevereThresholds = new ArrayMap<>();
1902 
1903         @GuardedBy("mSamples")
1904         float[] mHeadroomThresholds = new float[ThrottlingSeverity.SHUTDOWN + 1];
1905         @GuardedBy("mSamples")
1906         private long mLastForecastCallTimeMillis = 0;
1907 
1908         private final Runnable mGetAndUpdateTemperatureSamplesRunnable =
1909                 this::getAndUpdateTemperatureSamples;
1910 
getAndUpdateThresholds()1911         void getAndUpdateThresholds() {
1912             List<TemperatureThreshold> thresholds =
1913                     mHalWrapper.getTemperatureThresholds(true, Temperature.TYPE_SKIN);
1914             synchronized (mSamples) {
1915                 if (Flags.allowThermalHeadroomThresholds()) {
1916                     Arrays.fill(mHeadroomThresholds, Float.NaN);
1917                 }
1918                 for (final TemperatureThreshold threshold : thresholds) {
1919                     updateTemperatureThresholdLocked(threshold, false);
1920                 }
1921             }
1922         }
1923 
1924         // For an older device with multiple SKIN sensors, we will set a severity's headroom
1925         // threshold based on the minimum value of all as a workaround, unless override.
1926         @GuardedBy("mSamples")
updateTemperatureThresholdLocked(TemperatureThreshold threshold, boolean override)1927         void updateTemperatureThresholdLocked(TemperatureThreshold threshold, boolean override) {
1928             if (threshold.hotThrottlingThresholds.length <= ThrottlingSeverity.SEVERE) {
1929                 return;
1930             }
1931             float severeThreshold =
1932                     threshold.hotThrottlingThresholds[ThrottlingSeverity.SEVERE];
1933             if (Float.isNaN(severeThreshold)) {
1934                 return;
1935             }
1936             mSevereThresholds.put(threshold.name, severeThreshold);
1937             if (!Flags.allowThermalHeadroomThresholds()) {
1938                 return;
1939             }
1940             if (override) {
1941                 if (DEBUG) {
1942                     Slog.d(TAG, "Headroom cache cleared on threshold update " + threshold);
1943                 }
1944                 mCachedHeadrooms.clear();
1945                 Arrays.fill(mHeadroomThresholds, Float.NaN);
1946             }
1947             for (int severity = ThrottlingSeverity.LIGHT;
1948                     severity <= ThrottlingSeverity.SHUTDOWN; severity++) {
1949                 if (threshold.hotThrottlingThresholds.length > severity) {
1950                     float t = threshold.hotThrottlingThresholds[severity];
1951                     if (Float.isNaN(t)) {
1952                         continue;
1953                     }
1954                     if (severity == ThrottlingSeverity.SEVERE) {
1955                         mHeadroomThresholds[severity] = 1.0f;
1956                         continue;
1957                     }
1958                     float headroom = normalizeTemperature(t, severeThreshold);
1959                     if (Float.isNaN(mHeadroomThresholds[severity])) {
1960                         mHeadroomThresholds[severity] = headroom;
1961                     } else {
1962                         float lastHeadroom = mHeadroomThresholds[severity];
1963                         mHeadroomThresholds[severity] = Math.min(lastHeadroom, headroom);
1964                     }
1965                 }
1966             }
1967         }
1968 
getAndUpdateTemperatureSamples()1969         private void getAndUpdateTemperatureSamples() {
1970             synchronized (mSamples) {
1971                 if (SystemClock.elapsedRealtime() - mLastForecastCallTimeMillis
1972                         < mInactivityThresholdMillis) {
1973                     // Trigger this again after a second as long as forecast has been called more
1974                     // recently than the inactivity timeout
1975                     mHandler.postDelayed(mGetAndUpdateTemperatureSamplesRunnable, 1000);
1976                 } else {
1977                     // Otherwise, we've been idle for at least 10 seconds, so we should
1978                     // shut down
1979                     mSamples.clear();
1980                     mCachedHeadrooms.clear();
1981                     return;
1982                 }
1983 
1984                 long now = SystemClock.elapsedRealtime();
1985                 final List<Temperature> temperatures = mHalWrapper.getCurrentTemperatures(true,
1986                         Temperature.TYPE_SKIN);
1987                 if (DEBUG) {
1988                     Slog.d(TAG, "Thermal HAL getCurrentTemperatures result: " + temperatures);
1989                 }
1990                 for (Temperature temperature : temperatures) {
1991                     updateTemperatureSampleLocked(now, temperature);
1992                 }
1993                 mCachedHeadrooms.clear();
1994             }
1995         }
1996 
1997         @GuardedBy("mSamples")
updateTemperatureSampleLocked(long timeNow, Temperature temperature)1998         private void updateTemperatureSampleLocked(long timeNow, Temperature temperature) {
1999             // Filter out invalid temperatures. If this results in no values being stored at
2000             // all, the mSamples.empty() check in getForecast() will catch it.
2001             if (Float.isNaN(temperature.getValue())) {
2002                 return;
2003             }
2004             ArrayList<Sample> samples = mSamples.computeIfAbsent(temperature.getName(),
2005                     k -> new ArrayList<>(RING_BUFFER_SIZE));
2006             if (samples.size() == RING_BUFFER_SIZE) {
2007                 samples.removeFirst();
2008             }
2009             samples.add(new Sample(timeNow, temperature.getValue()));
2010         }
2011 
2012         /**
2013          * Calculates the trend using a linear regression. As the samples are degrees Celsius with
2014          * associated timestamps in milliseconds, the slope is in degrees Celsius per millisecond.
2015          */
2016         @VisibleForTesting
getSlopeOf(List<Sample> samples)2017         float getSlopeOf(List<Sample> samples) {
2018             long sumTimes = 0L;
2019             float sumTemperatures = 0.0f;
2020             for (int s = 0; s < samples.size(); ++s) {
2021                 Sample sample = samples.get(s);
2022                 sumTimes += sample.time;
2023                 sumTemperatures += sample.temperature;
2024             }
2025             long meanTime = sumTimes / samples.size();
2026             float meanTemperature = sumTemperatures / samples.size();
2027 
2028             long sampleVariance = 0L;
2029             float sampleCovariance = 0.0f;
2030             for (int s = 0; s < samples.size(); ++s) {
2031                 Sample sample = samples.get(s);
2032                 long timeDelta = sample.time - meanTime;
2033                 float temperatureDelta = sample.temperature - meanTemperature;
2034                 sampleVariance += timeDelta * timeDelta;
2035                 sampleCovariance += timeDelta * temperatureDelta;
2036             }
2037 
2038             return sampleCovariance / sampleVariance;
2039         }
2040 
2041         /**
2042          * Used to determine the temperature corresponding to 0.0. Given that 1.0 is pinned at the
2043          * temperature corresponding to the SEVERE threshold, we set 0.0 to be that temperature
2044          * minus DEGREES_BETWEEN_ZERO_AND_ONE.
2045          */
2046         private static final float DEGREES_BETWEEN_ZERO_AND_ONE = 30.0f;
2047 
2048         @VisibleForTesting
normalizeTemperature(float temperature, float severeThreshold)2049         static float normalizeTemperature(float temperature, float severeThreshold) {
2050             float zeroNormalized = severeThreshold - DEGREES_BETWEEN_ZERO_AND_ONE;
2051             if (temperature <= zeroNormalized) {
2052                 return 0.0f;
2053             }
2054             float delta = temperature - zeroNormalized;
2055             return delta / DEGREES_BETWEEN_ZERO_AND_ONE;
2056         }
2057 
2058         private static final int MINIMUM_SAMPLE_COUNT = 3;
2059 
getForecast(int forecastSeconds)2060         float getForecast(int forecastSeconds) {
2061             synchronized (mSamples) {
2062                 // If we don't have any thresholds, we can't normalize the temperatures,
2063                 // so return early
2064                 if (mSevereThresholds.isEmpty()) {
2065                     Slog.e(TAG, "No temperature thresholds found");
2066                     FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED,
2067                             Binder.getCallingUid(),
2068                             THERMAL_HEADROOM_CALLED__API_STATUS__NO_TEMPERATURE_THRESHOLD,
2069                             Float.NaN, forecastSeconds);
2070                     return Float.NaN;
2071                 }
2072             }
2073             if (mIsHalSkinForecastSupported.get()) {
2074                 float threshold = -1f;
2075                 synchronized (mSamples) {
2076                     // we only do forecast if a single SKIN sensor threshold is reported
2077                     if (mSevereThresholds.size() == 1) {
2078                         threshold = mSevereThresholds.valueAt(0);
2079                     }
2080                 }
2081                 if (threshold > 0) {
2082                     try {
2083                         final float forecastTemperature =
2084                                 mHalWrapper.forecastSkinTemperature(forecastSeconds);
2085                         return normalizeTemperature(forecastTemperature, threshold);
2086                     } catch (UnsupportedOperationException e) {
2087                         Slog.wtf(TAG, "forecastSkinTemperature returns unsupported");
2088                     } catch (Exception e) {
2089                         Slog.e(TAG, "forecastSkinTemperature fails");
2090                     }
2091                     return Float.NaN;
2092                 }
2093             }
2094             synchronized (mSamples) {
2095                 mLastForecastCallTimeMillis = SystemClock.elapsedRealtime();
2096                 if (!mHandler.hasCallbacks(mGetAndUpdateTemperatureSamplesRunnable)) {
2097                     if (DEBUG) {
2098                         Slog.d(TAG, "No temperature update callback, scheduling one");
2099                     }
2100                     getAndUpdateTemperatureSamples();
2101                 } else {
2102                     if (DEBUG) {
2103                         Slog.d(TAG, "Temperature update callback already exists");
2104                     }
2105                 }
2106                 // If somehow things take much longer than expected or there are no temperatures
2107                 // to sample, return early
2108                 if (mSamples.isEmpty()) {
2109                     Slog.e(TAG, "No temperature samples found");
2110                     FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED,
2111                             Binder.getCallingUid(),
2112                             FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__NO_TEMPERATURE,
2113                             Float.NaN, forecastSeconds);
2114                     return Float.NaN;
2115                 }
2116 
2117                 if (mCachedHeadrooms.contains(forecastSeconds)) {
2118                     float headroom = mCachedHeadrooms.get(forecastSeconds);
2119                     // TODO(b/360486877): add new API status enum or a new atom field to
2120                     //                    differentiate success from reading cache or not
2121                     FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED,
2122                             Binder.getCallingUid(),
2123                             FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__SUCCESS,
2124                             headroom, forecastSeconds);
2125                     if (DEBUG) {
2126                         Slog.d(TAG,
2127                                 "Headroom forecast in " + forecastSeconds + "s served from cache: "
2128                                         + headroom);
2129                     }
2130                     return headroom;
2131                 }
2132 
2133                 float maxNormalized = Float.NaN;
2134                 int noThresholdSampleCount = 0;
2135                 for (Map.Entry<String, ArrayList<Sample>> entry : mSamples.entrySet()) {
2136                     String name = entry.getKey();
2137                     ArrayList<Sample> samples = entry.getValue();
2138 
2139                     Float threshold = mSevereThresholds.get(name);
2140                     if (threshold == null) {
2141                         noThresholdSampleCount++;
2142                         Slog.e(TAG, "No threshold found for " + name);
2143                         continue;
2144                     }
2145 
2146                     float currentTemperature = samples.getLast().temperature;
2147 
2148                     if (samples.size() < MINIMUM_SAMPLE_COUNT) {
2149                         if (mSamples.size() == 1 && mCachedHeadrooms.contains(0)) {
2150                             // if only one sensor name exists, then try reading the cache
2151                             // TODO(b/360486877): add new API status enum or a new atom field to
2152                             //                    differentiate success from reading cache or not
2153                             float headroom = mCachedHeadrooms.get(0);
2154                             FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED,
2155                                     Binder.getCallingUid(),
2156                                     FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__SUCCESS,
2157                                     headroom, 0);
2158                             if (DEBUG) {
2159                                 Slog.d(TAG,
2160                                         "Headroom forecast in 0s served from cache: " + headroom);
2161                             }
2162                             return headroom;
2163                         }
2164                         // Don't try to forecast, just use the latest one we have
2165                         float normalized = normalizeTemperature(currentTemperature, threshold);
2166                         if (Float.isNaN(maxNormalized) || normalized > maxNormalized) {
2167                             maxNormalized = normalized;
2168                         }
2169                         continue;
2170                     }
2171                     float slope = 0.0f;
2172                     if (forecastSeconds > 0) {
2173                         slope = getSlopeOf(samples);
2174                     }
2175                     float normalized = normalizeTemperature(
2176                             currentTemperature + slope * forecastSeconds * 1000, threshold);
2177                     if (Float.isNaN(maxNormalized) || normalized > maxNormalized) {
2178                         maxNormalized = normalized;
2179                     }
2180                 }
2181                 if (noThresholdSampleCount == mSamples.size()) {
2182                     FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED,
2183                             Binder.getCallingUid(),
2184                             THERMAL_HEADROOM_CALLED__API_STATUS__NO_TEMPERATURE_THRESHOLD,
2185                             Float.NaN, forecastSeconds);
2186                 } else {
2187                     FrameworkStatsLog.write(FrameworkStatsLog.THERMAL_HEADROOM_CALLED,
2188                             Binder.getCallingUid(),
2189                             FrameworkStatsLog.THERMAL_HEADROOM_CALLED__API_STATUS__SUCCESS,
2190                             maxNormalized, forecastSeconds);
2191                 }
2192                 mCachedHeadrooms.put(forecastSeconds, maxNormalized);
2193                 return maxNormalized;
2194             }
2195         }
2196 
getHeadroomThresholds()2197         float[] getHeadroomThresholds() {
2198             synchronized (mSamples) {
2199                 return Arrays.copyOf(mHeadroomThresholds, mHeadroomThresholds.length);
2200             }
2201         }
2202 
2203         @GuardedBy("mSamples")
getHeadroomCallbackDataLocked()2204         HeadroomCallbackData getHeadroomCallbackDataLocked() {
2205             final HeadroomCallbackData data = new HeadroomCallbackData(
2206                     getForecast(0),
2207                     getForecast(DEFAULT_FORECAST_SECONDS),
2208                     DEFAULT_FORECAST_SECONDS,
2209                     Arrays.copyOf(mHeadroomThresholds, mHeadroomThresholds.length));
2210             if (DEBUG) {
2211                 Slog.d(TAG, "New headroom callback data: " + data);
2212             }
2213             return data;
2214         }
2215 
2216         @VisibleForTesting
2217         // Since Sample is inside an inner class, we can't make it static
2218         // This allows test code to create Sample objects via ThermalManagerService
createSampleForTesting(long time, float temperature)2219         Sample createSampleForTesting(long time, float temperature) {
2220             return new Sample(time, temperature);
2221         }
2222 
2223         @VisibleForTesting
2224         static class Sample {
2225             public long time;
2226             public float temperature;
2227 
Sample(long time, float temperature)2228             Sample(long time, float temperature) {
2229                 this.time = time;
2230                 this.temperature = temperature;
2231             }
2232 
2233             @Override
toString()2234             public String toString() {
2235                 return "Sample[temperature=" + temperature + ", time=" + time + "]";
2236             }
2237         }
2238     }
2239 }
2240