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