Skip to content

Fix Infotainment reads blocked by a handshake that never finished - #170

Merged
dzid26 merged 1 commit into
mainfrom
work/infotainment-session-gate
Oct 9, 2026
Merged

dzid26 merged 1 commit into
mainfrom
work/infotainment-session-gate

Conversation

@dzid26

@dzid26 dzid26 commented Oct 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

Real-car log (owner test, 2026-10-09): with #161 the app logged VCSEC status all morning (wake, unlock, doors, a 20-minute drive) but sent no charge or drive read. InfotainmentPollPolicy asked for reads; the controller blocked them.

Cause, confirmed in TeslaBleController.VehicleLink: the status handler passed sessionReady = !chargeAfterSession && ... to the policy. chargeAfterSession was set by requestChargeState() when it started the Infotainment handshake and cleared only on session establishment or close(). Any other end of the handshake left it set forever: sessionRetry giving up after SESSION_MAX_ATTEMPTS, the retry loop returning early when the car went back to sleep or the phase was not READY, startSession() returning early (no key/VIN), or the link dropping (the FAILED/DISCONNECTED path never cleared it). The policy then got sessionReady=false for the rest of the link's life, so no read and no new handshake followed.

One detail differs from the report: neededSessions() already left Infotainment out while the stored status said asleep, so the READY branch did not itself request it for a sleeping car. But that status is the last one seen before the link dropped, so after a reconnect it can be stale and say awake. The fix removes the dependency on it.

Changes:

  • New pure InfotainmentSessionGate (core): tracks "a due read is waiting for the handshake". Ended by session established, retries giving up, car asleep, link dropped, or handshake not startable.
  • The Infotainment session is requested only on demand: from requestChargeState() (driven by the policy) when the car is awake. Never on connect, never while VCSEC says asleep. On READY only the VCSEC session is requested.
  • No second request while one is in flight; unanswered Infotainment entries in pendingSessions are dropped when a wait ends so none looks like a live handshake.
  • Policy sessionReady is now session exists || VIN valid ("a read can be attempted now"); no double gating on the flag.
  • ADR-0009 gains a paragraph on this. New docs/research/real-car-log-2026-10-09.md holds the measurements (no VIN, car ID, BLE name or raw logs), linked from infotainment-polling-and-sleep.md under "To measure".

Type

  • Bug fix
  • Feature
  • Protocol / crypto change
  • Docs / CI / tooling
  • Refactor (no behavior change)

Testing

  • ./gradlew :core:test :app:assembleDebug :app:testDebugUnitTest :app:lintDebug ktlintCheck detekt all pass locally (VehicleLink stays under the LargeClass cap).
  • InfotainmentSessionGateTest (core): stuck wait cleared on give-up, asleep, link drop and unstartable; no handshake while asleep; no duplicate request while in flight; read released once on establishment.
  • Controller wiring (VehicleLink, Android handler/BLE) has no unit harness; it is read-reviewed only. The decision logic lives in the tested gate; the controller just calls it at the points listed above.
  • Real car: not done. Check to run: with a weak home link the car wakes; the app then establishes the Infotainment session and reads within about 10 s of the wake; and no session request DOMAIN_INFOTAINMENT line appears in the debug log while the car is asleep.

Checklist

  • Protocol changes ship test vectors; expected.txt regenerated, not hand-edited (no protocol change)
  • No VINs, keys, or decrypted payloads in logs
  • Docs/master-plan updated where relevant (ADR-0009, research notes)
  • New source files carry SPDX-License-Identifier: AGPL-3.0-only

🤖 Generated with Claude Code

https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX


Generated by Claude Code

The controller passed sessionReady = !chargeAfterSession to the poll policy.
The flag was cleared only when the Infotainment session was established or the
link was closed, so a handshake that ended any other way (retries gave up, car
went to sleep, link dropped) blocked every later read and handshake.

Track the waiting read in InfotainmentSessionGate, which is ended by
establishment, give-up, asleep or link drop. Request the Infotainment session
only on demand, from a due read on an awake car, and never on connect or while
asleep. Do not resend a request while one is in flight. Record the real-car
measurements in docs/research.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
@github-actions

github-actions Bot commented Oct 9, 2026

Copy link
Copy Markdown
Contributor

Screenshots

Light

Scanning for nearby cars (light)Car detail with the battery reading (light)Settings (light)

Dark

Scanning for nearby cars (dark)Car detail with the battery reading (dark)Settings (dark)

Captured from the demo build at 06d6a59.

Debug APK: zip from Android CI (GitHub sign-in needed)

@dzid26
dzid26 merged commit 4a7ffa3 into main Oct 9, 2026
5 checks passed
@dzid26
dzid26 deleted the work/infotainment-session-gate branch October 9, 2026 13:37
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.

2 participants