Repository navigation
Run GATT callbacks on the main thread and serialize setup - #133
Merged
Merged
Conversation
TeslaGattClient passed no Handler to connectGatt, so every BluetoothGattCallback ran on a Binder thread and raced the main-thread send() path and the controller's timers over the write queue, the framer and VehicleLink state (#110). - Connect with the API 26 connectGatt overload and the main-looper Handler on Android 8.1+. Android 8.0 stores a notification's value on the Binder thread before posting to the Handler, so a queued callback can read the next notification's bytes; there the client takes Binder callbacks, reads the value at once and posts the work to main. - Run setup one GATT operation at a time: MTU exchange, then the CCCD write, then the device-name read, then READY. Characteristic writes wait until setup finishes, and a failed name read still reports READY instead of stalling the link. - Keep write-without-response and note that the Go reference writes with response. - Document the main-thread contract on TeslaTransport.Listener. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
Contributor
ScreenshotsCaptured from the demo build at 5bf55cc. |
Contributor
ScreenshotsCaptured from the demo build at 57070ed. Debug APK: zip from Android CI (GitHub sign-in needed) |
3 of 9 tasks
A value pasted on Windows ends in CRLF, and base64 -d rejects the
carriage return ("invalid input"), which failed the build job before
it produced an APK. Strip all whitespace before decoding; this also
accepts line-wrapped encoder output such as certutil's.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
Contributor
|
The Generated by Claude Code |
Contributor
ScreenshotsCaptured from the demo build at 3b7828b. Debug APK: zip from Android CI (GitHub sign-in needed) |
Contributor
ScreenshotsCaptured from the demo build at 2fee286. Debug APK: zip from Android CI (GitHub sign-in needed) |
6 of 11 tasks
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.




Summary
Part of #110.
TeslaGattClientpassed noHandlertoconnectGatt, so everyBluetoothGattCallbackran on a Binder thread. Those callbacks raced the main-threadsend()path and the controller's timers over the write queue, the framer and theVehicleLinkmaps. Setup also started the CCCD write andrequestMtu()back to back.connectGatt(context, false, callback, TRANSPORT_LE, PHY_LE_1M_MASK, handler)and the main-looperHandler. Listener calls, write-queue state and the framer now all run on the main thread, like the controller'sHandlerrunnables and the simulated car. The KDoc onTeslaTransportandTeslaTransport.Listenerdocuments this.BluetoothGatt.onNotifystores the value in the shared characteristic on the Binder thread before it posts the callback to the Handler (AOSPoreo-release). 8.1 (oreo-mr1-release) movedsetValueinto the posted runnable. With a Handler on 8.0, a queued callback can read the next notification's bytes and corrupt a frame. Nordic's Android-BLE-Library avoids the Handler overload on API 26 for the same reason (their issue Landing page: show only the scanning and car screenshots #54). So on 8.0 only, the client takes callbacks on the Binder thread, reads the value immediately and posts the work to the main thread itself. A guard drops posted work for aBluetoothGattthat was closed or replaced.READY. Each step advances only on its own callback, so a strayonMtuChangedcan't skip a step or fireREADYtwice. Characteristic writes wait until setup finishes. The existing fallbacks and their log lines are unchanged:requestMtu()returning false or a failed MTU callback falls back to default chunking.setCharacteristicNotification/writeDescriptor, skips to the name read.readCharacteristic()returning false goes straight toREADY.Device name read failed with status Nand reportsREADY, like the other name fallbacks.WRITE_TYPE_NO_RESPONSE(validated on a real car). A comment notes that the Go reference writes with response.TeslaBleControlleris unchanged. Two parts of #110 are not in this PR: the test transport that delivers callbacks from a background thread, and serializingreadRemoteRssi()with writes.Type
Testing
The app module can't be built in the agent environment because Google Maven is blocked, so CI is the first full Android build and lint. Checked locally:
TeslaGattClient.ktandTeslaTransport.kt(withBleModels.kt,Vehicle.ktand:core) against Robolectric'sandroid-allAPI 37 framework jar: no errors.connectGattoverload is deprecated behind thegatt_conn_settingsfeature flag. The replacement isconnectGatt(BluetoothGattConnectionSettings, Executor, BluetoothGattCallback).maingets the same warning, so this PR adds no new kind of warning..editorconfigandconfig/detekt/detekt.yml: clean.NewApi(minSdk 26). The 6-argconnectGattandPHY_LE_1M_MASKare API 26 and sit behindSDK_INT >= O_MR1. API 33 calls stay behind the existingTIRAMISUchecks.:app:lintDebug,:app:testDebugUnitTest,:app:assembleDebug(left to CI), and nothing on a device or emulator. The Android 8.0 path is untested.Needs a real-car smoke test before merge: pair, wake, SOC read, and reconnect after the car drops the link. The log should show
TX characteristic: true, RX characteristic: true, thenNotifications enabled, thenDevice name: …, with norequestMtu() returned falseorMTU negotiation failedlines../gradlew :core:test(core untouched)./gradlew :app:testDebugUnitTest(left to CI)./gradlew :app:lintDebug(left to CI)Checklist
expected.txtregenerated, not hand-edited (n/a, no protocol change)SPDX-License-Identifier: AGPL-3.0-only(n/a, no new files)🤖 Generated with Claude Code
https://claude.ai/code/session_0129RzhbEE2cboMcYyZMuxqX
Generated by Claude Code