Skip to content

Add thermal status instrumentation (#1799)#1801

Open
hitkall wants to merge 4 commits into
open-telemetry:mainfrom
hitkall:thermal-status-instrumentation
Open

Add thermal status instrumentation (#1799)#1801
hitkall wants to merge 4 commits into
open-telemetry:mainfrom
hitkall:thermal-status-instrumentation

Conversation

@hitkall

@hitkall hitkall commented Jun 9, 2026

Copy link
Copy Markdown

What

Adds a new, opt-in instrumentation module (instrumentation/thermal) that listens for device thermal-status changes and emits one log event per change.

Fixes #1799

Approach

  • Event-driven, no polling. ThermalInstrumentation.install() registers a PowerManager.OnThermalStatusChangedListener via PowerManager.addThermalStatusListener(executor, listener) on a single-thread executor. uninstall() calls removeThermalStatusListener and shuts the executor down.
  • API 29+ gated. The thermal APIs (addThermalStatusListener, THERMAL_STATUS_*) are only available on API level 29 (Android Q). Since the project minSdk is 23, install() gates on Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q and no-ops on older devices. The API-29 call sites are annotated @RequiresApi(29) so Animal Sniffer is satisfied.
  • Opt-in. This module is not added to android-agent, so it is not bundled by default — consumers add the dependency and install it explicitly. The README documents this.
  • ThermalDetector maps the integer status codes to readable strings (none, light, moderate, severe, critical, emergency, shutdown, with an unknown fallback) and emits via the logs bridge.

This module closely mirrors the existing instrumentation/screen-orientation module in structure, style, and conventions.

Telemetry

Scope io.opentelemetry.thermal
Event name device.thermal_state
Attribute android.thermal.state (string)

Note on naming: the event name (device.thermal_state) and attribute (android.thermal.state) are proposals. Happy to align them with whatever the maintainers prefer for semantic conventions.

Tests

ThermalDetectorTest (JUnit 4 + MockK + AssertJ + OpenTelemetryRule) covers:

  • a single status change emits the right event name + attribute,
  • the full code→name mapping for every known status,
  • the unknown fallback for an unrecognized code.

Verification

Run locally against JDK 17 + Android SDK 36:

  • ./gradlew spotlessApply
  • ./gradlew :instrumentation:thermal:test (3 tests pass)
  • ./gradlew apiDump (generated api/thermal.api)
  • ./gradlew apiCheck
  • ./gradlew :instrumentation:thermal:assemble
  • (also detekt + lintDebug for the module)

🤖 Generated with Claude Code

@hitkall hitkall requested a review from a team as a code owner June 9, 2026 19:58
@linux-foundation-easycla

linux-foundation-easycla Bot commented Jun 9, 2026

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: hitkall / name: Hitesh Kalluru (0ac4f7e)

@hitkall hitkall force-pushed the thermal-status-instrumentation branch 2 times, most recently from 6fb9f7c to 0ac4f7e Compare June 9, 2026 20:00
@codecov

codecov Bot commented Jun 9, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 95.34884% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 63.13%. Comparing base (fec75d2) to head (3c3a5a0).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
.../instrumentation/thermal/ThermalInstrumentation.kt 92.59% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1801      +/-   ##
==========================================
+ Coverage   62.88%   63.13%   +0.25%     
==========================================
  Files         158      160       +2     
  Lines        3451     3494      +43     
  Branches      355      360       +5     
==========================================
+ Hits         2170     2206      +36     
- Misses       1179     1182       +3     
- Partials      102      106       +4     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Add a new opt-in instrumentation module that listens for device
thermal-status changes via PowerManager.addThermalStatusListener (no
polling) and emits one log event per status change.

The thermal APIs are API 29+, so install() gates on
Build.VERSION.SDK_INT and no-ops on older devices. The module is not
bundled with the android-agent; it must be installed explicitly.

Signed-off-by: Hitesh Kalluru <kalluruhitesh3@gmail.com>
@hitkall hitkall force-pushed the thermal-status-instrumentation branch from 0ac4f7e to 3137bb4 Compare June 9, 2026 20:16

@breedx-splk breedx-splk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @hitkall this is great. I offered a few remarks, but this is definitely on track.

I also think you might have missed adding this module to the settings.gradle.kts in the root of the project. I don't think it'll be included and compiled until you do that.

.loggerBuilder("io.opentelemetry.$name")
.build()

val executor = Executors.newSingleThreadExecutor()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit of a drag to burn an entire thread on something that is probably rarely used. I'm not entirely sure what best practice around this is. We have a few other instrumentations that create their own, so it's not a show-stopper. We might want to consider a future mechanism for sharing these so that it's a little more scalable.

}
}

override fun onThermalStatusChanged(status: Int) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: prefer to have the interface method above the private methods that it calls into.

Comment on lines +2 to +4
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

</manifest> No newline at end of file

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be removed, unless you have a reason for needing it? I see that the screen orientation also has an empty one, but maybe that can be removed as well (separately from this PR tho).

import org.junit.Test

class ThermalDetectorTest {
private lateinit var sut: ThermalDetector

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what sut is a convention for or shortening of ("[s]omething [u]nder [t]est"?), but it's not something we really use. Maybe just call it detector instead?

Comment thread instrumentation/thermal/README.md Outdated
### Thermal State

* Type: Event
* Name: `device.thermal_state`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reminder to change readme to match the name change suggested above (or whatever it lands on).

@hitkall

hitkall commented Jun 9, 2026

Copy link
Copy Markdown
Author

Thanks for the thorough review, really appreciate it!
Addressing everything:
settings.gradle.kts — I think this is actually handled already. The includeFromDir("instrumentation") function in settings.gradle.kts walks the directory tree looking for build.gradle.kts files up to depth 3, so instrumentation/thermal gets auto-discovered without a manual entry. That said, I want to double-check that api/thermal.api was committed properly — its absence might be what made the module look unregistered in CI. Will verify and push a fix if it's missing.
Executor — totally fair point. A dedicated thread for something that fires maybe a handful of times a day is wasteful. Happy to follow whatever pattern the project lands on for sharing executors across instrumentations — flagging it as a known issue in the code for now sounds reasonable if there's no existing pattern to hook into yet.
device.thermal_status.change — accepted, makes more sense as "what happened". Will update EVENT_NAME, the README, and the test references.
Method ordering — will fix, onThermalStatusChanged above the private extension.
AndroidManifest.xml — will delete it.
sut → detector — will rename throughout the test.
android.thermal.state attribute — I like the direction of framing it around throttling impact rather than raw thermal state. Open to android.thermal.throttling_status or similar if you have a preference — otherwise I'll leave it as-is for now and we can revisit when semconv gets more formal here.
Will push an update addressing all of the above shortly.

@breedx-splk

hitkall and others added 2 commits June 9, 2026 19:38
…/instrumentation/thermal/ThermalDetector.kt

Co-authored-by: jason plumb <75337021+breedx-splk@users.noreply.github.com>
…tation test

Signed-off-by: Hitesh Kalluru <kalluruhitesh3@gmail.com>

@breedx-splk breedx-splk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! Thanks for the help and for the quick responses.

The thermal API reports throttling severity rather than temperature, so
android.thermal.throttling_status more accurately describes the value
(per maintainer review feedback).

Signed-off-by: Hitesh Kalluru <kalluruhitesh3@gmail.com>
@hitkall

hitkall commented Jun 10, 2026

Copy link
Copy Markdown
Author

Thanks for the review @breedx-splk! All feedback is addressed in the latest commits:

  • Renamed the attribute to android.thermal.throttling_status — agreed the API reports throttling severity (0 = none … 6 = shutdown) rather than temperature, so this is more accurate. README updated to match.
  • Renamed the event to device.thermal_status.change.
  • Moved the interface method (onThermalStatusChanged) above the private helpers.
  • Removed the empty AndroidManifest.xml.
  • Renamed sutdetector in the detector test.
  • Added ThermalInstrumentationTest covering the API-29 gate, listener registration/removal, and the uninstall-before-install no-op.

Re: settings.gradle.kts — the module is picked up automatically by includeFromDir("instrumentation"), and CI is compiling/testing :instrumentation:thermal, so no manual include is needed.

The event/attribute names are still open to whatever the semantic-conventions discussion lands on. Let me know if you'd like anything else.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Thermal status instrumentation

2 participants