Skip to content

Commit e200640

Browse files
committed
improvements
1 parent fd814e6 commit e200640

9 files changed

Lines changed: 208 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,43 @@ for the "definition of done" that will mark v1.
356356
the wire/SDP level (not just APK code), and names DLCI 0x08 "GSND CONTROL" and DLCI 0x0a "GSND
357357
AUDIO" for the first time — new leads for `PROTOCOL.md` §2.3's/§6's open DLCI-0x08-identity
358358
question, proposed for maintainer review, not committed as a promotion. See `CAP-033-FINDINGS.md`.
359+
- **2026-09-01: `CAP-034` (Group W, 4th attempt) captured and analyzed — resolves the
360+
`0x0c0X`/`0x0f2X` GATT handle↔UUID mapping, maintainer sign-off obtained.** Combined `CAP-014`'s
361+
confirmed-unlimited HCI snaplen with Group W's own long-untried cache-busting method
362+
(`pm clear com.android.bluetooth` on a Pixel 9a never before connected to this Buds unit) for the
363+
first time. The resulting discovery burst resolves the full 15-primary-service GATT profile:
364+
`0x0c00``0x0c14` = Google Fast Pair Service (all 5 spec-defined characteristics, plus Message
365+
Stream PSM and one still-unnamed characteristic), `0x0f20``0x0f2a` = Device Information,
366+
`0x0f30``0x0f33` = Battery Service. Corrects an earlier `CAP-017-FINDINGS.md` hypothesis that
367+
"Unknown Service" (`109b862f-…`) contained this cluster — it occupies a separate handle range and
368+
its own purpose remains unidentified. See `CAP-034-FINDINGS.md` and `PROTOCOL.md` §4.3 Option D/§6.
369+
- **2026-09-02: `CAP-035` (Group AB, GMS-independence check) captured and analyzed, maintainer
370+
sign-off obtained.** Tested whether DLCI 0x08 ("GSND CONTROL")/0x0a ("GSND AUDIO")/0x06 ("DEBUG
371+
APP")/0x12 ("BTIS") depend on Google Play Services, on a GrapheneOS phone with GMS present but
372+
`dumpsys`-verified disabled. DLCI 0x08's content reproduces byte-identical across a fresh connect
373+
and a reconnect; DLCI 0x0a opens in lockstep but stays payload-silent both times; DLCI 0x06/0x12
374+
never open at all — clean negatives for both, the first time either has been specifically checked.
375+
Strengthens (does not fully close) `CAP-004-FINDINGS.md` §4a's existing "GMS present but disabled"
376+
finding — a repeat with GMS genuinely uninstalled would close it fully. See `CAP-035-FINDINGS.md`.
377+
- **2026-09-03: documentation audit remediation** (maintainer-directed fixes following a 2026-09-02
378+
documentation audit): registered `CAP-034`/`CAP-035` in `id_registry.csv` (both had full Capture
379+
Index rows and were cited throughout `PROTOCOL.md`/`TESTPLAN_BLUETOOTH_HCI_SNOOP.md` but were never
380+
added to the registry); fixed a live CI "Lint docs" failure (added the deliberately-referenced,
381+
deleted `REVIEW_REPORT.md` to `scripts/lint_docs.py`'s historical-reference allowlist; repaired
382+
`CAP-035-EVENT-NOTES.md`'s footer, which pointed at a truncated folder path); corrected a stale,
383+
self-contradictory "not yet traced" note in `REVERSE_ENGINEERING.md`'s Call graph notes section
384+
(the `fsz`/`fux``MethodClient` chain it described as untraced had in fact been fully traced
385+
earlier in the same document); populated `REVERSE_ENGINEERING.md`'s previously-empty "Native
386+
libraries" table from the already-documented finding; added the missing extraction commands and
387+
raw hex to `CAP-008-FINDINGS.md`'s HFP-handshake and eSCO-setup sections, per `PROJECT_RULES.md`
388+
§1's hex-and-script rule (all of that capture's original conclusions were independently
389+
re-verified and confirmed correct in the process); refreshed two stale `TODO.md` status
390+
descriptions (the UUID register is no longer an empty template; the APK keyword-search pass has
391+
grown well past its originally-cited class-entry count). **`DECISIONS.md` ADR-020** — EQ's
392+
`FrameEncoder`/`FrameDecoder` implementation explicitly unblocked, closing a gap where `ADR-016`
393+
had promoted EQ's protocol knowledge to FACT without ever stating the `ARCHITECTURE.md` §5
394+
implementation gate was cleared (unlike ANC/`ADR-009` and Find My Buds/`ADR-011`); no new protocol
395+
knowledge, maintainer-approved.
359396

360397
### Removed
361398

DECISIONS.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -921,5 +921,44 @@ motivated this).
921921
for DLCI 0x02 generally — that still requires the broader payload-content HYPOTHESIS in `ADR-018` to
922922
reach FACT, which this ADR narrows but does not itself complete.
923923

924+
## ADR-020 — EQ `FrameEncoder`/`FrameDecoder` implementation explicitly unblocked
925+
926+
- **Date**: 2026-09-03
927+
- **Status**: Accepted
928+
- **Context**: `ADR-016` (2026-08-28) promoted EQ's wire envelope shape, its field-to-band mapping,
929+
the ±6.0 band-gain clamp, and the confirmed preset quintets to 🟢 FACT (`PROTOCOL.md` §4.2). Unlike
930+
`ADR-009` (ANC) and `ADR-011` (Find My Buds Left/Right), which each explicitly state that the
931+
`ARCHITECTURE.md` §5 per-command implementation gate is cleared, `ADR-016` never made the
932+
equivalent statement for EQ — a 2026-09-02 documentation audit flagged this as a gap: EQ's protocol
933+
knowledge is fully FACT-level, but its implementation-readiness status was left ambiguous rather
934+
than explicitly settled. The maintainer reviewed this gap directly (session of 2026-09-03) and
935+
explicitly instructed that it be closed via a new ADR, matching `ADR-009`/`ADR-011`'s pattern
936+
rather than an in-place edit to `ADR-016`'s own text (`PROJECT_RULES.md` §3 rule 9's
937+
non-destructive-update convention).
938+
- **Finding being recorded**: none new — this ADR does not add any protocol knowledge. It records
939+
the maintainer's explicit decision that the FACT-level findings `ADR-016` already promoted (5×
940+
`float32` band-gain quintet on DLCI 0x02's `field5{field4{...}}` envelope, field 1↔Low bass /
941+
2↔Bass / 3↔Mid / 4↔Treble / 5↔Upper treble, wire order reversed from on-screen order, ±6.0 clamp,
942+
and the six confirmed preset quintets) are sufficient, on their own, to unblock implementation.
943+
- **What this ADR does NOT clear**: EQ's outer field 16 vs. field 18 distinction (`PROTOCOL.md`
944+
§4.2/§6 — "live value" vs. "persisted value," and whether that maps to "preview" vs.
945+
"slider-release"/"commit") remains 🟡 HYPOTHESIS, unaffected by this ADR. An implementation needs to
946+
pick one field for a given write; per `PROTOCOL.md` §4.2's own code-derived reading
947+
(`REVERSE_ENGINEERING.md`'s `qjw` entry: field 16 = `fyp.f()`, "update user eq," fired on every
948+
slider-drag value change and on preset selection; field 18 = `fyp.d()`, "update last saved user
949+
eq," fired once per gesture and also persisted locally), field 16 is the correct target for a
950+
live/preview-style write — this ADR does not promote that reading to FACT, it only notes it as the
951+
practical default for an initial implementation. The gain unit (plausibly dB, never independently
952+
confirmed) and the ~13-byte correlation-ID/`call_id` region also remain unconfirmed, unaffected.
953+
- **Decision**: EQ's `FrameEncoder`/`FrameDecoder` implementation is unblocked, per `ARCHITECTURE.md`
954+
§5's per-command implementation gate, for the elements `ADR-016` already promoted to FACT (the
955+
envelope wrapper, the 5-band quintet and its field-to-band mapping, the ±6.0 gain clamp, and the
956+
preset quintets).
957+
- **Consequences**: `:data` can implement EQ's `FrameEncoder`/`FrameDecoder` now, against fixed
958+
byte-array fixtures per `AGENTS.md` §11, using field 16 for live/slider-drag writes as the
959+
practical default described above. The field-16-vs-18 semantic question and the gain-unit question
960+
remain open research items (`PROTOCOL.md` §6, `TODO.md`) and should be resolved before EQ ships a
961+
"Save as preset"-style UI affordance that specifically depends on field 18's exact semantics.
962+
924963
---
925964
https://github.com/tedsluis/opencontrolpixelbudspro2/blob/main/DECISIONS.md - https://tedsluis.github.io/opencontrolpixelbudspro2/DECISIONS

PROTOCOL.md

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,11 @@ never decides which extracted finding is relevant (see `AGENTS.md` §4/§6,
576576
- **Status**: 🟢 FACT for the wire envelope, the field-to-band mapping, and the ±6.0 range; 🟡
577577
HYPOTHESIS (strong) that DLCI 0x02 is specifically `libmaestro`; 🟡 HYPOTHESIS for the
578578
preview/save field semantics; 🔴 unconfirmed for the gain units, the Control byte, and the
579-
~13-byte correlation-ID region (§6).
579+
~13-byte correlation-ID region (§6). **`FrameEncoder`/`FrameDecoder` implementation for EQ is
580+
explicitly unblocked, 2026-09-03 (`DECISIONS.md` ADR-020)** — the FACT-level elements above
581+
(envelope, field-to-band mapping, ±6.0 clamp, preset quintets) are sufficient on their own; the
582+
field-16-vs-18 and gain-unit open items above are unaffected and should be resolved before a
583+
"Save as preset" UI affordance ships, per that ADR's own scope note.
580584
- **Evidence**: `SCREENSHOTS_PIXEL_BUDS_APP.md`, `TESTPLAN_BLUETOOTH_HCI_SNOOP.md` §1,
581585
`captures/CAP-005-2026-08-15_15-02-31_15-03-45-Group_T/CAP-005-FINDINGS.md` (first candidate
582586
format, single-band sample), `captures/CAP-015-2026-08-18_06-11-06_06-17-40-Group_T/CAP-015-FINDINGS.md`
@@ -640,6 +644,14 @@ event-observation coroutines.
640644
assumption of fixed-interval polling).
641645
- Shown ≥8 seconds when using the "show" type; auto-hidden after 20s or via an
642646
explicit "hide" type frame. Optional when a single bud is inserted/removed.
647+
**Re-check flagged 2026-09-03**: two direct re-fetches of the official
648+
`batterynotification` extension page found no sentence anywhere on it stating this
649+
8s/20s timing — the byte-layout table above was re-confirmed exactly, but this specific
650+
timing sub-claim's citation could not be re-verified against the currently-live page text.
651+
Not retracted (the fetch tool's page-to-text conversion is not a guaranteed-complete read,
652+
and this detail may live on a different Fast Pair spec page, e.g. the base Message Stream
653+
spec, not checked this pass) — downgraded from unqualified `[OFFICIAL-SPEC]` to 🟡
654+
HYPOTHESIS pending a maintainer or future session reading the actual page directly.
643655
- **Advantage**: visible on a passive BLE scan — no active connection required,
644656
useful for the battery fallback logic in `ARCHITECTURE.md` §4.
645657
- **Attempted 2026-08-21, `CAP-011` — inconclusive, not `[VERIFIED-LOCAL]`.** A dedicated capture
@@ -1791,6 +1803,8 @@ leaving them buried in prose elsewhere.
17911803
| 2026-08-27 | **PROPOSAL, pending maintainer approval.** `CAP-014` (Group W repeat, snaplen-fixed) analyzed: **§4.3 Option D and the `0x0c0X`/`0x0f2X` open item annotated, no status change** — the handle↔UUID mapping remains 🔴 OPEN after a 3rd Group-W-labeled attempt, but the blocking cause is now precisely identified as GATT-cache reuse on an already-bonded phone (not a snaplen issue this time, which this session's own check confirmed fixed) — see `CAP-014-FINDINGS.md` §4/§8 for the full analysis and the recommended next capture (genuinely combining a fixed snaplen with one of Group W's own untried cache-busting methods, `pm clear com.android.bluetooth` or the Pixel 9a). Byte-length/leading-byte shapes for `0x0c0c`/`0x0c13`/`0x0c14` and content for `0x0f2a` ("Revision 6")/`0x0f32` (`0x64`) reproduce exactly across independent sessions, strengthening confidence without changing any status | Claude (AI), capture-analysis task, not yet reviewed by maintainer |
17921804
| 2026-08-30 | **Four pending FACT promotions from a combined Tier 0 (capture re-decode) / Tier 2 (APK static-analysis) session reviewed and explicitly approved by the maintainer, per-point** (`AGENTS.md` §6), recorded in `DECISIONS.md` ADR-019: **§2.2a** — the "..." inside DLCI 0x02's `field5{field4{...}}` wrapper confirmed 🟢 FACT to be `libmaestro`'s own recovered `WriteSetting` schema (`qhr`), for 2 sampled fields (4, 29), via independent APK static analysis. **§4.5.3** — the top-level "Use touch controls" toggle opcode (`field 4`) and the press-and-hold action-selection opcode (`field 7`/`qju`, plus a corrected, one-level-deeper `qik`→`qho` nesting) both promoted to 🟢 FACT, each now backed by both wire+video correlation and a self-describing log message in the app's own code. The ANC-mode rotation-checklist opcode's **field number** (`field 12`/`qht`) promoted to 🟢 FACT; its equivalence to the app's own "ANC gesture loop" name explicitly **not** promoted — the maintainer reviewed this specific point and kept it at 🟡 HYPOTHESIS. See `REVERSE_ENGINEERING.md`'s `qjc`/`qja`/`qhr`/`qjo`/`qju`/`qjg`/`qht` entries (2026-08-30 updates) and `CAP-020-FINDINGS.md`/`CAP-021-FINDINGS.md`'s 2026-08-30 addenda for the full byte-level and code-level evidence | Claude (AI), maintainer-directed per-point sign-off session |
17931805
| 2026-08-30 | Remediation from a 2026-08-30 project-wide documentation audit (maintainer-directed fixes, no new FACT promotion or ADR): **§2.2a** — added `CAP-033` as a fourth independent, SDP-service-name-level corroboration of DLCI 0x02's "MAESTRO APP" channel-ownership finding. **§2.3** — added a 2026-08-30 update recording `CAP-033`'s SDP-browse naming of DLCI 0x08 ("GSND CONTROL"), DLCI 0x0a ("GSND AUDIO"), DLCI 0x06 ("DEBUG APP"), and DLCI 0x12 ("BTIS") — new leads, 🟡 HYPOTHESIS, explicitly not a resolution of DLCI 0x08's identity. **§6** — added a matching dated update to the DLCI-0x08-ownership open item | Claude (AI), audit-remediation task, maintainer-directed |
1806+
| 2026-09-03 | **§4.2 EQ**`FrameEncoder`/`FrameDecoder` implementation explicitly unblocked (`DECISIONS.md` ADR-020, maintainer-directed, closing a gap a 2026-09-02 documentation audit found: EQ's protocol knowledge was already fully FACT per `ADR-016`, but no ADR had ever explicitly cleared `ARCHITECTURE.md` §5's implementation gate for it, unlike ANC/`ADR-009` and Find My Buds/`ADR-011`). No new protocol knowledge; field-16-vs-18 and gain-unit questions remain open | Claude (AI), maintainer-directed sign-off session |
1807+
| 2026-09-03 | Remediation from a 2026-09-02 documentation audit (mechanical fixes, no new FACT promotion or ADR beyond ADR-020 above): **§4.3 Option A** — the "shown ≥8s, auto-hidden after 20s" Battery Notification visibility-timing claim downgraded from unqualified `[OFFICIAL-SPEC]` to 🟡 HYPOTHESIS after two direct re-fetches of the official `batterynotification` extension page found no matching text; the byte-layout table in the same section was re-confirmed exactly and is unaffected | Claude (AI), audit-remediation task, maintainer-directed |
17941808

17951809
---
17961810
https://github.com/tedsluis/opencontrolpixelbudspro2/blob/main/PROTOCOL.md - https://tedsluis.github.io/opencontrolpixelbudspro2/PROTOCOL

REVERSE_ENGINEERING.md

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1982,7 +1982,13 @@ APK, in addition to the officially documented ones.
19821982

19831983
| File | Architecture | Suspected function | Analyzed? |
19841984
|---|---|---|---|
1985-
| | | | No / Ghidra in progress / Done |
1985+
| `libandroidx.graphics.path.so` | `arm64-v8a` (present only in `split_config.arm64_v8a.apk`, absent from `base.apk`) | AndroidX Graphics `Path` native backend (`System.loadLibrary("androidx.graphics.path")`) — unrelated to Bluetooth/`libmaestro` | Done — ruled out as protocol-relevant |
1986+
| `libpw_tokenizer_jni.so` | `arm64-v8a` (same split as above) | Pigweed `pw_tokenizer` JNI bridge (`System.loadLibrary("pw_tokenizer_jni")`) — log-string detokenization support, not the protocol transport itself | Done — ruled out as protocol-relevant |
1987+
1988+
No file named `libmaestro.so`/`libgfps.so` exists anywhere across `base.apk` or either split, and no
1989+
`System.loadLibrary`/`System.load` call in the decompiled sources names one either (full-tree grep,
1990+
see the "APK metadata" table above and `AGENTS.md` §0's 2026-08-30 correction) — the Maestro control
1991+
logic is pure Kotlin/Java, not a native binary, for this analyzed version.
19861992

19871993
> **Updated 2026-08-30 (`DECISIONS.md` ADR-017, superseding ADR-003):** native
19881994
> `.so` disassembly is now in scope for AI *mechanical* assistance, on the
@@ -2016,11 +2022,22 @@ fxm.i() [MaestroSoftwareInfoAndHidUuidCheck]
20162022
-> fxm.c(goq, goq) x4 [maestro_pw.Maestro/GetSoftwareInfo pw_rpc unary,
20172023
one per {MAESTRO_A,MAESTRO_B} x {LEFT_BT_CORE,RIGHT_BT_CORE}]
20182024
-> BluetoothDevice.fetchUuidsWithSdp() [only if HID UUID 0x1124 not yet present]
2025+
2026+
fye.a(qhs) / fsz's WriteSetting send path [confirmed end-to-end, see the nqx/npy/nqo/npw/nqm
2027+
entry's "Full confirmed send chain" above]
2028+
-> nqo.e(qjc) [pw_rpc.MethodClient.invoke]
2029+
-> npy.a(...) [pw_rpc.Client — builds/serializes the RpcPacket, payload=serialized qjc]
2030+
-> npw.a(bytes) [pw_rpc.Channel]
2031+
-> npv.a(bytes) [one of frg.java's 6 anonymous ChannelOutput implementations]
2032+
-> fut.f(bytes, goq) [HDLC-encode: flag + LEB128 address + control + payload + CRC-32 + flag]
2033+
-> ffd.j() = BluetoothSocket.getOutputStream()
20192034
```
20202035

2021-
Not yet traced: how `ClassicBTReceiver`'s connection-state events lead into `gbm`'s socket selection,
2022-
and how `fsz`'s `WriteSetting` / `fux`'s per-service pw_rpc calls get their `MethodClient` — both are
2023-
plausible next steps for a follow-up §4 pass, not claimed here.
2036+
Not yet traced: how `ClassicBTReceiver`'s connection-state events lead into `gbm`'s socket
2037+
selection — a plausible next step for a follow-up §4 pass, not claimed here. (The other half of
2038+
this section's original open item — how `fsz`'s `WriteSetting`/`fux`'s per-service pw_rpc calls
2039+
obtain their `MethodClient` — was resolved by a later pass the same day; see the second call-graph
2040+
block above and the `nqx`/`npy`/`nqo`/`npw`/`nqm` entry for the full trace.)
20242041

20252042
## Correlation status with PROTOCOL.md
20262043

0 commit comments

Comments
 (0)