Skip to content

Serialize G2 GATT ops for reliable packet delivery - #5

Open
aheschl1 wants to merge 1 commit into
andrew/notificationsfrom
andrew/reliable-bluetooth-g2
Open

Serialize G2 GATT ops for reliable packet delivery#5
aheschl1 wants to merge 1 commit into
andrew/notificationsfrom
andrew/reliable-bluetooth-g2

Conversation

@aheschl1

@aheschl1 aheschl1 commented Aug 9, 2026

Copy link
Copy Markdown
Owner

Improve G2 bluetooth reliability

Android GATT client com.android.bluetooth allows only one packet in flight per connection. On boot-up, or heavy utilization times, many important packets don't get sent.

The solution is to centralise and enqueue packets, and tick when the GATT stack.

Symptom

On boot about 50% of the times, it was noticed that calendar events didn't appear. Packets never got sent. This was due to a stampede at connect time.

Logs

Logs collected on G2 smart glasses

Comparison

writes lost loss rate worst window
before (HEAD) 289 25 8.65% connect+auth: 40%
after (this PR) 300 0 0.00%

flow both runs: connect, auth, dashboard, calendar sync, Mentra Maps.

Notes:

  • The fail counter is now stricter: it counts writes refused by the stack or never
    retired, versus refused-only before.

Raw log lines - grep -E "BGCAP:|G2/GATT:"

Before (HEAD)

 LOG  CORE: BGCAP: g2 writeCharacteristic ok=0 fail=1 in 603411349ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=3 fail=4 in 1103ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=15 fail=7 in 1266ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=5 fail=0 in 1872ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=5 fail=0 in 2496ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=1 fail=0 in 4995ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=5 fail=0 in 1211ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=3 fail=1 in 1394ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=6 fail=0 in 2407ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=1 fail=0 in 4995ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=3 fail=0 in 1063ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=7 fail=1 in 1279ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=25 fail=3 in 1086ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=34 fail=3 in 1583ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=1 fail=0 in 1090ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=32 fail=0 in 1001ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=20 fail=0 in 1301ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=33 fail=0 in 1432ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=5 fail=1 in 2005ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=18 fail=4 in 1004ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=24 fail=0 in 1311ms (fail = stack busy/dropped)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=18 fail=0 in 3217ms (fail = stack busy/dropped)

Totals: ok=264 fail=25 (8.65% lost). First three lines are the connect+auth window: 12 of 30
writes lost (40%) while CCC descriptor writes and the auth sequence raced for the GATT slot.

After (this PR)

 LOG  CORE: BGCAP: g2 writeCharacteristic ok=1 fail=0 in 602846115ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=23 fail=0 in 1166ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=4 fail=0 in 1044ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=2 fail=0 in 1005ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=2 fail=0 in 2240ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=3 fail=0 in 1143ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=4 fail=0 in 3858ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=2 fail=0 in 1351ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=4 fail=0 in 3650ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=1 fail=0 in 3867ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=1 fail=0 in 1130ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=2 fail=0 in 4800ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=38 fail=0 in 1101ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=37 fail=0 in 1104ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=19 fail=0 in 1172ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=34 fail=0 in 1326ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=35 fail=0 in 1331ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=10 fail=0 in 4168ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=1 fail=0 in 1635ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=36 fail=0 in 1018ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=20 fail=0 in 1055ms (fail = refused or never retired)
 LOG  CORE: BGCAP: g2 writeCharacteristic ok=21 fail=0 in 1308ms (fail = refused or never retired)

Totals: ok=300 fail=0 (0.00% lost). Connect+auth window is a single clean ok=23 fail=0, and
sustained bursts run at 35-38 writes/s with zero loss — throughput up, loss gone.

the large elapsed on each run's first line is a logging artifact

Serialize every GATT op — characteristic writes on both legs and the
descriptor writes that enable notifications — on one op thread, issuing
each only after the stack reports the previous one retired. Replaces the
fixed 8 ms pacing, which never covered single-packet command bursts and
still lost packets on multi-packet ones (8.65% measured; 40% during
connect+auth). Zero lost writes measured after.
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.

1 participant