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