Thermal API

Released:

Android 11 (API Level 30) - Thermal API

Android 12 (API Level 31) - NDK API

(Preview) Android 15 (DP1) - getThermalHeadroomThresholds()

The potential performance of your app is limited by the thermal state of the device, which can vary based on characteristics such as weather, recent usage, and the device's thermal design. Devices can only maintain a high level of performance for a limited amount of time before being thermally throttled. A key goal of your implementation should be to achieve performance goals without exceeding thermal limitations. Thermal API makes it possible without the need for device specific optimizations. Furthermore, when debugging performance issues, knowing if the thermal state of your device is limiting performance is important. Furthermore, when debugging performance issues, knowing if the thermal state of your device is limiting performance is important.

Game engines usually have runtime performance parameters that can adjust the workload the engine puts on the device. For example, these parameters can set the number of worker threads, worker-thread affinity for big and small cores, GPU fidelity options, and framebuffer resolutions. In Unity Engine, game developers can adjust the workload by changing the Quality Settings using the Adaptive Performance plugin. For Unreal Engine, use the Scalability Settings to adjust quality levels dynamically.

When a device approaches an unsafe thermal state, your game can avoid being throttled by decreasing the workload through these parameters. To avoid throttling, you should monitor the thermal state of the device and proactively adjust the game engine workload. Once the device overheats, the workload must drop below the sustainable performance level in order to dissipate heat. After the thermal headroom decreases to safer levels, the game can increase the quality settings again but make sure to find a sustainable quality level for optimal play time.

You can monitor the thermal state of the device by polling the getThermalHeadroom method. This method predicts how long the device can maintain the current performance level without overheating. If the time is less than the amount needed to run the workload, then your game should decrease the workload to a sustainable level. For example, the game can shift to smaller cores, reduce the frame rate, or lower fidelity.

ADPF Thermal API Pre-Integration
Figure 1. Thermal Headroom without actively monitoring getThermalHeadroom
ADPF Thermal API Post-Integration
Figure 2. Thermal Headroom with active monitoring of `getThermalHeadroom`

Acquire Thermal Manager

In order to use Thermal API, first you'll need to acquire the Thermal Manager

C++

AThermalManager* thermal_manager = AThermal_acquireManager();

Java

PowerManager powerManager = (PowerManager)this.getSystemService(Context.POWER_SERVICE);

Forecast the Thermal Headroom x seconds ahead for more control

You can ask the system to forecast the temperature x seconds ahead with the current workload. This gives you a more fine-grained control and more time to react by reducing the workload to prevent thermal throttling from kicking in.

The result ranges from 0.0f (no throttling, THERMAL_STATUS_NONE) to 1.0f (heavy throttling, THERMAL_STATUS_SEVERE). If you have different graphics quality levels in your games, you can follow our Thermal Headroom Guidelines.

C++

float thermal_headroom = AThermal_getThermalHeadroom(10);
ALOGI("ThermalHeadroom in 10 sec: %f", thermal_headroom);

Java

float thermalHeadroom = powerManager.getThermalHeadroom(10);
Log.d("ADPF", "ThermalHeadroom in 10 sec: " + thermalHeadroom);

Alternatively, rely on thermal status for clarification

Each device model may be designed differently. Some devices may be able to distribute heat better and thus be able to withstand higher thermal headroom before being throttled. If you want to read a simplified grouping of ranges of thermal headroom, you can check on the thermal status to make sense of the thermal headroom value on the current device.

C++

AThermalStatus thermal_status = AThermal_getCurrentThermalStatus(thermal_manager);
ALOGI("ThermalStatus is: %d", thermal_status);

Java

int thermalStatus = powerManager.getCurrentThermalStatus();
Log.d("ADPF", "ThermalStatus is: " + thermalStatus);

Get notified when thermal status changes

You can also avoid polling the thermalHeadroom until the thermalStatus hits a certain level (for example: THERMAL_STATUS_LIGHT). To do so, you can register a callback to let the system notify you whenever the status has changed.

C++

int result = AThermal_registerThermalStatusListener(thermal_manager, callback);
if ( result != 0 ) {
  // failed, check whether you have previously registered callback that
  // hasn’t been unregistered
}

Java

// PowerManager.OnThermalStatusChangedListener is an interface, thus you can
// also define a class that implements the methods
PowerManager.OnThermalStatusChangedListener listener = new
  PowerManager.OnThermalStatusChangedListener() {
    @Override
    public void onThermalStatusChanged(int status) {
        Log.d("ADPF", "ThermalStatus changed: " + status);
        // check the status and flip the flag to start/stop pooling when
        // applicable
    }
};
powerManager.addThermalStatusListener(listener);

Remember to remove the listener when done

C++

int result = AThermal_unregisterThermalStatusListener(thermal_manager, callback);
if ( result != 0 ) {
  // failed, check whether the callback has been registered previously
}

Java

powerManager.removeThermalStatusListener(listener);

Cleanup

Once you're done, you'll need to clean up the thermal_manager that you acquired. If you're using Java, the PowerManager reference can be automatically garbage collected for you. But if you're using the Java API through JNI and have retained a reference, remember to clean up the reference!

C++

AThermal_releaseManager(thermal_manager);

For a complete guide on how to implement Thermal API on a native C++ game using both the C++ API (NDK API) and Java API (through JNI), check out the Integrate Thermal API section in the Adaptability codelab section.

Thermal headroom guidelines

You can monitor the thermal state of the device by polling the getThermalHeadroom method. This method predicts how long the device can maintain the current performance level before reaching THERMAL_STATUS_SEVERE. For example, if getThermalHeadroom(30) returns 0.8, it indicates that in 30 seconds, the headroom is expected to reach 0.8, where there is 0.2 distance away from severe throttling, or 1.0. If the time is less than the amount needed to run the workload, then your game should decrease the workload to a sustainable level. For example, the game can reduce the frame rate, lower fidelity, or reduce network connectivity work.

Thermal statuses and meaning

Device limitations of the Thermal API

There are some known limitations or additional requirements of Thermal API, due to implementations of the thermal API on older devices. The limitations and how to work around them are as follows:

  • Do not call the GetThermalHeadroom() API too frequently. Doing so will result in the API returning NaN. You should call it at most once per second.
  • If the initial value of GetThermalHeadroom() is NaN, the API is not available on the device
  • If GetThermalHeadroom() returns a high value (e.g: 0.85 or more) and GetCurrentThermalStatus() still returns THERMAL_STATUS_NONE, the status is likely not updated. Use heuristics to estimate the correct thermal throttling status or just use getThermalHeadroom() without getCurrentThermalStatus().

Heuristics example:

  1. Check that Thermal API is supported. isAPISupported() checks the value of the first call to getThermalHeadroom to ensure that it is not 0 or NaN and skips using the API if the first value is either 0 or NaN.
  2. If getCurrentThermalStatus() returns a value other than THERMAL_STATUS_NONE, the device is being thermally throttled.
  3. If getCurrentThermalStatus() keeps returning THERMAL_STATUS_NONE, it doesn't necessarily mean the device isn't being thermally throttled. It could mean that getCurrentThermalStatus() is not supported on the device. Check the return value of getThermalHeadroom() to ensure the condition of the device.
  4. If getThermalHeadroom() returns a value of > 1.0, the status could actually be THERMAL_STATUS_SEVERE or higher, reduce workload immediately and maintain lower workload until getThermalHeadroom() returns lower value
  5. If getThermalHeadroom() returns a value of 0.95, the status could actually be THERMAL_STATUS_MODERATE or higher, reduce workload immediately and keep the watchout to prevent higher reading
  6. If getThermalHeadroom() returns a value of 0.85, the status could actually be THERMAL_STATUS_LIGHT, keep the watchout and reduce workload if possible

Pseudocode:

  bool isAPISupported() {
    float first_value_of_thermal_headroom = getThermalHeadroom();
    if ( first_value_of_thermal_headroom == 0 ||
      first_value_of_thermal_headroom == NaN ) {
        // Checked the thermal Headroom API's initial return value
        // it is NaN or 0,so, return false (not supported)
        return false;
    }
    return true;
  }
  
  if (!isAPISupported()) {
    // Checked the thermal Headroom API's initial return value, it is NaN or 0
    // Don’t use the API
  } else {
      // Use thermalStatus API to check if it returns valid values.
      if (getCurrentThermalStatus() > THERMAL_STATUS_NONE) {
          // The device IS being thermally throttled
      } else {
      // The device is not being thermally throttled currently. However, it
      // could also be an indicator that the ThermalStatus API may not be
      // supported in the device.
      // Currently this API uses predefined threshold values for thermal status
      // mapping. In the future  you may be able to query this directly.
      float thermal_headroom = getThermalHeadroom();
      if ( thermal_headroom > 1.0) {
            // The device COULD be severely throttled.
      } else  if ( thermal_headroom > 0.95) {
            // The device COULD be moderately throttled.
      } else if ( thermal_headroom > 0.85) {
            // The device COULD be experiencing light throttling.
      }
    }
  }

Diagram:

ADPF Heuristic Example
Figure 3.Example of Heuristic to determine Thermal API support on older devices