fix(android): retry CAN subscribe dropped by GATT-busy race - #200
Closed
dkneeland wants to merge 1 commit into
Closed
fix(android): retry CAN subscribe dropped by GATT-busy race#200dkneeland wants to merge 1 commit into
dkneeland wants to merge 1 commit into
Conversation
Why: onboarding/Connected stalled at Pairing because the app never actually subscribed to the CAN characteristic. CAN and the Tesla status char enable back-to-back on service discovery, but Android's GATT client allows one op in flight, so the second write was refused. The old code ignored writeDescriptor()'s return value, so the dropped CAN subscribe looked like a success and no CAN frames arrived. Since Connected is gated on the first CAN frame, the app hung whenever there was no bus data (bench or an off car). Fix: check writeDescriptor()'s return value and retry the CAN subscribe (3 attempts, 250ms apart) when the write isn't queued, so it lands once the other subscribe finishes. Log only the failure path and a missing-CCCD error so a regression is visible without per-connect noise. Verified: first attempt is refused, retry queues successfully, firmware reports the CAN subscription, and the dashboard renders live data.
Collaborator
Author
|
Closing in favor of a single consolidated Phase 4 PR: the CAN subscribe retry here is patch-identical to the copy on the Phase 4 branch, which also extends the fix to the Tesla status subscription via a shared DashKitBleManager.subscribeWithRetry() helper. The fix lands (as its own commit) in that PR - link to follow. |
Collaborator
Author
|
Superseded by #201, which carries this fix (as its own commit) and extends it to the Tesla status subscription via a shared subscribeWithRetry() helper. |
dkneeland
added a commit
that referenced
this pull request
Aug 24, 2026
Android's GATT client allows only one operation in flight, and the app subscribes to two characteristics back-to-back when services become ready (CAN + Tesla status). The second CCCD write could be refused while the first was pending, and writeDescriptor()'s return value was ignored, so the dropped subscribe looked like success: no CAN frames arrived (Connected is gated on the first CAN frame, so onboarding stalled at Pairing), or the status tile went stale with no error. Check writeDescriptor()'s return value and retry (3 attempts, 250 ms apart) via a shared DashKitBleManager.subscribeWithRetry(), now used by both the CAN datasource and the Tesla status source. Supersedes #200.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The app can silently fail to subscribe to the CAN characteristic, which leaves a connected phone with no CAN stream and can stall onboarding at "Pairing" whenever a bus has no data (bench test, or a car that's partially sleeping).
Root cause
When services are discovered, the app enables the CAN and Tesla-status characteristics back-to-back. Android's GATT client allows only one operation in flight, so the second
writeDescriptor()call is refused. The old code ignoredwriteDescriptor()'s return value and logged "Subscribed" unconditionally, so the dropped CAN subscribe looked like a success and no CAN notifications ever arrived.This is a latent bug in the existing CAN subscription path — the app-channel/Tesla Phase 4 work didn't touch this code or introduce it.
Fix
In
DashKitDataSource:writeDescriptor()return value instead of discarding it.Verification
On the bench: the first attempt is refused (
queued=false), the retry queues successfully, the firmware reports "Notifications enabled", and the dashboard renders live data (SOC, odometer).