Open tasks, grouped by phase. Check items off and move completed major items
to CHANGELOG.md (see PROJECT_RULES.md §6, rule 13, on technical debt
tracking).
A cross-phase execution order, distinct from the phase grouping below (which organizes tasks by
kind, not by when to do them). This section is a sequencing layer only — each item's full
description still lives in its own phase/section below (or in PROTOCOL.md/ARCHITECTURE.md for
protocol/architecture open questions, per this file's own "Open questions" section at the bottom);
nothing here is a second copy of that detail, only a pointer plus the reasoning for the ordering.
- Decisions & sign-offs (no new data needed — cheapest, unlocks the most):
- Maintainer sign-off on pending 🟢 FACT promotions — see the new Phase 3 item below for the
current list. Per
AGENTS.md§6 this step can only be done by the maintainer, not an agent. DI approach (Hilt vs. manual) — Phase 4.Resolved 2026-09-13: Hilt (DECISIONS.mdADR-028).Minimum Android API level — Phase 5.Resolved 2026-09-13: API 34 (DECISIONS.mdADR-029).Find My Buds Case/"both simultaneously" — whether to accept a Google Find Hub/account-mediated fallback for this one sub-feature or ship v1 without local Case-ring support (Resolved 2026-09-13: ship v1 with Left/Right ring only (PROTOCOL.md§6, Behavior) — a genuine Zero-GMS scope trade-off, not a research gap; no capture or static analysis can resolve this, only a maintainer product decision can.DECISIONS.mdADR-027,PROJECT.mdnon-goals).
- Maintainer sign-off on pending 🟢 FACT promotions — see the new Phase 3 item below for the
current list. Per
- Start Phase 4 app development, ANC-first: ANC, Find My Buds Left/Right
(
DECISIONS.mdADR-011), and EQ (DECISIONS.mdADR-020) are all fully 🟢 FACT and implementation-unblocked — ANC remains the recommended starting point (DECISIONS.mdADR-009): building it end-to-end (transport → framing → UI) is the cheapest way to prove the whole architecture works. Battery via HFP (PROTOCOL.md§4.3 Option C, also already 🟢 FACT) is the natural second target — together they cover most ofPROJECT.md's "Definition of done (v1)". - Phase 2 (APK reverse engineering) — updated 2026-08-30, no longer 0% done. APK pulled,
JADX/apktool-decompiled, and multiple
§4keyword-search/follow-up passes done (REVERSE_ENGINEERING.md's growing class-entry list, 30+ entries as of the last pass), andDECISIONS.mdADR-018 accepted (DLCI 0x02 channel-ownership → 🟢 FACT), and.proto/pw_rpc schema recovery — updated 2026-09-03, done via a different route than planned here:pbtkconfirmed structurally incapable of this APK's codegen (both the whole-APK and a 2026-09-03 targeted-class re-run wrote 0.protofiles; root cause is now source-cited, not just the tool's own caveat — see the Phase 2 checklist item below). The schemas were instead recovered by hand (scripts/decode_rawmessageinfo.py,DECISIONS.mdADR-019) and cross-correlated against wire captures forqhrfields 4, 7, 12, and 29. Updated 2026-09-08 — the "remaining confirmed-but-unchecked field numbers" item this bullet used to point to is now fully closed: fields 17/19/22/27/28 closed 2026-09-03, and fields 11/15 (the two the maintainer's0002sign-off was scoped to) closed 2026-09-08 — see this file's "Targeted research follow-ups" section below,PROTOCOL.md§4.5.2/§4.5.6/§4.5.7/§4.5.5a/§4.5.8/§4.5.1,DECISIONS.mdADR-019 and its two Updates. Current highest-leverage single next step is now theMaestroDeviceSettingsProviderService6-case-ID→qhr-field forward trace (same "Targeted research follow-ups" section, added 2026-09-08 by prompt0002) — the cheapest of the three open APK-RE leads fromai-sessions/0001_CROSSCHECK_RESULT_2026_09_07.md's Phase 1/Phase 2 (the other two,MaestroEndpointService's smali fallback read andgjv.p()'s caller trace, both need either an untried smali read or a fresh capture first).CAP-033(Group AA,SDP-001/SDP-002) is done (2026-08-30) — see below. - Remaining planned captures (updated 2026-09-13 — see below for the next-test-session queue;
CAP-008,CAP-009,CAP-013,CAP-014,CAP-027,CAP-033–CAP-042are done, seeCAPTURE_BLUETOOTH_HCI_SNOOP.md§9):- Next test session (added 2026-09-13,
ai-sessions/0017_MAINTENANCE_RESULT_2026_09_13.md) — 5 short, self-contained captures, each with its own placeholder capture folder and event-notes skeleton already prepared undercaptures/(see eachCAP-0NN's own row inCAPTURE_BLUETOOTH_HCI_SNOOP.md§9), each ~3–10 minutes, no destructive/one-time action among them (all safely repeatable if a retry is needed):CAP-053(Group AO) — EQ outer field 16-vs-18: isolate Save-tap / navigate-away / genuine slider-release from each other.CAP-054(Group AP) — Battery Notification, connection-free, bracketing a single-bud insertion/removal (the Fast Pair spec's own "optional" trigger, untested so far).CAP-055(Group AQ) — Nod/Shake head gestures with an actual active call/notification, camera also framing the gesture itself (needs a second phone to place the call).CAP-056(Group AR) — ANC-rotation-checklist Left/Right split, genuine re-run — read the skeleton's anti-repeat safeguard first,CAP-045skipped the actual checklist screen.CAP-057(Group AS) — liveGetSoftwareInfo/GetHardwareInfocorrelation against the connect-time burst, using the firmware/serial-number screen.
- Also still queued from before, lower priority than the 5 above:
CAP-043(Group Q repeat, Battery Notification BLE scan) is done (2026-09-13) — a second confirmed non-match, see Phase 1 above andCAP-054in the next-test-session queue for its own follow-up. CAP-044(Group AA repeat, 2nd attempt,SDP-001/SDP-002) is done (2026-09-13) — still 🟡 HYPOTHESIS, a different isolation gap thanCAP-033's; a 3rd attempt is a Tier-2 decision item inai-sessions/0017_MAINTENANCE_RESULT_2026_09_13.mdPhase 6, not yet designed as a skeleton pending that go/no-go.CAP-018and the still-uncaptured main-run-through remainder (CAP-026,CAP-029–CAP-030) —CAP-028(head gestures) is done (2026-09-12, inconclusive) and re-verified (2026-09-13); its follow-up is now tracked as plannedCAP-055(Group AQ), not this bullet. Closed this update: Group W's own untried GATT cache-busting methods —CAP-034(2026-09-01) combinedpm clear com.android.bluetoothwith a Pixel 9a never before connected to this Buds unit and fully resolved the0x0c0X/0x0f2Xhandle↔UUID mapping (seePROTOCOL.md§6, §4.3 Option D) — this bullet's own "untried" framing is now stale and removed.
- Next test session (added 2026-09-13,
- Targeted research follow-ups, lowest priority, tracked at their source per this file's
"Open questions" section: the
CAP-021DLCI 0x0a burst trigger and the DLCI 0x02 AES-128 hypothesis (PROTOCOL.md§6) — the latter is only really testable once Phase 2 above provides a pw_rpc/protobuf schema to check against. Added 2026-08-28 (2026-08-28 project-wide audit, Phase 5), three specific new-capture ideas, none yet designed inCAPTURE_BLUETOOTH_HCI_SNOOP.md:HOLD-005's Left/Right ANC-rotation-checklist split (PROTOCOL.md§6) — a purpose-built capture isolating one earbud's rotation list at a time (the envelope carries no Left/Right-distinguishing field for this specific write, unlikeHOLD-001–HOLD-004). Now designed, 2026-09-09: skeleton created asCAP-045(Group AJ, new — seeCAPTURE_BLUETOOTH_HCI_SNOOP.md§4.1 andai-sessions/0005_MAINTENANCE_RESULT_2026_09_09.md).CAP-045run 2026-09-12 but did not exercise Group AJ's own procedure (physical press-and-hold ANC cycling was captured instead — the rotation-checklist screen was never opened); the question remains fully open. A genuine re-run, with a mandatory on-camera anti-repeat safeguard, is designed as Group AR (plannedCAP-056,ai-sessions/0017_MAINTENANCE_RESULT_2026_09_13.mdPhase 4).- Volume balance (
field 17) scale/direction (CAP-022-FINDINGS.md§5,PROTOCOL.md§4.5.7/§6) — a capture with isolated extreme-position samples (not a continuous drag) plus tighter video correlation. Now designed, 2026-09-09: skeleton created asCAP-046(Group AK, new). - The
CAP-021DLCI 0x0a burst trigger, more precisely: a purpose-built hypothesis test (PROJECT_RULES.md§4's fixed template — hypothesis, setup, expected outcome, actual outcome, conclusion) bracketing candidate triggers one at a time (app backgrounded/foregrounded, a scheduled sync window, a charge-state change) — the burst recurred in exactly 1 of 16 sessions checked so far, so passively waiting for it to reappear is not expected to work. Now designed, 2026-09-09: skeleton created asCAP-047(Group AL, new; no existing Test-ID, flagged as aTESTPLAN_BLUETOOTH_HCI_SNOOP.mdfollow-up). - Added 2026-08-30 (audit finding), closed 2026-09-08 (prompt
0002, implementingai-sessions/0001_CROSSCHECK_RESULT_2026_09_07.mdPhase 2/Phase 4): applyDECISIONS.mdADR-019's same static-analysis method (matching a confirmed wire field number against the recoveredqhrschema) to the remaining confirmed-but-unchecked DLCI 0x02 field numbers —field11, 15, 17, 19, 22, 27, 28 (PROTOCOL.md§6's "what do DLCI 0x02's confirmed inner field numbers actually represent" item). Fields 17/19/22/27/28 were closed 2026-09-03 (DECISIONS.mdADR-019 Update), though this bullet was never updated at the time to reflect that. Fields 11 (Multipoint) and 15 (Volume EQ) — the two the maintainer's sign-off for prompt0002was scoped to — are now closed too, via a forward trace from a named UI fragment/preference key to the write call site (PROTOCOL.md§4.5.2/§4.5.6,REVERSE_ENGINEERING.md'sqhrentry,DECISIONS.mdADR-025 Update). All 7 field numbers this item originally listed are now checked against the recoveredqhrschema — item closed. - Added 2026-09-03 (audit finding), closed 2026-09-08
(
ai-sessions/0003_MAINTENANCE_RESULT_2026_09_08.mdPhase 3 item 4): tracedfyd.d/fyd.e's own call sites in the EQ UI fragment — field 16 fires from the slider-drag/preset path; field 18 is reachable only via a dedicated, self-describing "On click save EQ button" handler, with no slider-release code path found anywhere. This closes the static-analysis question but contradictsCAP-015's own wire-timing "fires on slider-release" hypothesis rather than confirming it — a genuine, unreconciled tension, proposed for maintainer review. Re-verified 2026-09-13 (ai-sessions/0017_MAINTENANCE_RESULT_2026_09_13.mdPhase 1): the "reachable only via Save button" reading was incomplete — a second call path exists (hod.java, a navigate-away-with-unsaved-changes trigger), adding a third candidate. A capture isolating all three (Save tap / navigate-away / genuine slider-release only) is proposed asCAPTURE_BLUETOOTH_HCI_SNOOP.mdGroup AO (plannedCAP-053). SeeREVERSE_ENGINEERING.md'sqjwentry andPROTOCOL.md§4.2/§6. - Added 2026-09-03 (audit finding): re-verify
PROTOCOL.md§4.3 Option A's "shown ≥8s, auto-hidden after 20s" Battery Notification visibility-timing claim directly against the official Fast Pair spec pages (a 2026-09-03 re-check found no such text on thebatterynotificationextension page specifically — downgraded to 🟡 HYPOTHESIS pending this check; the detail may live on a different spec page not checked yet, e.g. the base Message Stream spec). - Added 2026-09-08 (
ai-sessions/0001_CROSSCHECK_RESULT_2026_09_07.mdPhase 1/Phase 4, prompt0002), closed 2026-09-08 (ai-sessions/0003_MAINTENANCE_RESULT_2026_09_08.mdPhase 3 item 1): tracedMaestroDeviceSettingsProviderService's 6 case IDs to their exact accessor call —2102→qhrfield 2,2103→field 27,2104→field 11 (Multipoint — the head-gestures lead below did not pan out; field 29 is not among these 6 mappings),2113→field 5,2115→noqhrfield at all (a separate "Feature A" toggle),2116→field 32 (new). SeeREVERSE_ENGINEERING.md'sMaestroDeviceSettingsProviderServiceentry for the full trace, including two byproductqhrregister corrections (fields 6 and 32) and an unreconciledCATEGORY_MULTIPOINT-vs-fpm.ENABLED_HEAD_GESTURESnaming tension on case 2104 — proposed forPROTOCOL.mdpromotion, pending maintainer review. - Added 2026-09-08 (
ai-sessions/0001_CROSSCHECK_RESULT_2026_09_07.mdPhase 1, prompt0002), largely closed 2026-09-08 (ai-sessions/0003_MAINTENANCE_RESULT_2026_09_08.mdPhase 3 item 2):apktoolsmali-fallback read ofMaestroEndpointService.onCreate()done — the registered services come from a Dagger multibinding (Map<String, Optional<ofd>>) assembled elsewhere (names not recovered), andofd's method is a per-call, UID-based authorization check (not a service dispatcher as its shape first suggested) — two policies found, an internal-UID-only check and an allowlisted-Google-signed-caller check. SeeREVERSE_ENGINEERING.md'sMaestroEndpointServiceentry. Still open, if pursued further: the literal registered service names (would need locating the Dagger multibinding's own assembly site — judged out of proportion to chase further this session) and whether GMS specifically is ever in the allowlist for this service's own methods. - Formalized 2026-09-08 (
ai-sessions/0002_MAINTENANCE_RESULT_2026_09_08.md's own gap scan flagged this as never added to this list; re-attempted and still not closed byai-sessions/0003_MAINTENANCE_RESULT_2026_09_08.mdPhase 3 item 3): tracegjv.p()'s own caller — the remaining open link needed to determine whetherfxm.i()'sGetSoftwareInfofetch genuinely fires inside theCAP-036/CAP-041connect-time settling window (REVERSE_ENGINEERING.md'sfrb/fuh/glk/gjventry). Two static-analysis passes have now failed to locate this caller (generic-token searches on.p()/.u =are unproductive against this app's R8 obfuscation) — the byte-level capture-correlation alternative (PROTOCOL.md§6's matching item) is now the recommended path, not a further static-analysis attempt, unless a future session identifies a more targeted search strategy. - Added 2026-09-11 (
ai-sessions/0008_CROSSCHECK_RESULT_2026_09_11.md), full non-sampled validation of Gemini'sai-sessions/0007_CROSSCHECK_RESULT_2026_09_11.md. Independently re-derived every citation in0007; roughly half of its line-number citations from §2.2 onward point to the wrong location, two (the claimed field-19 write site and the claimed field-17 write site) point to code with no connection to the claim at all.0007's §2.6 "NEW INDEPENDENT FINDING" (qhrfield 19 as a "Volume Balance extreme/gate boolean") is not new — it restatesREVERSE_ENGINEERING.md/DECISIONS.mdADR-019's 2026-09-03 Update — and omits field 19's actual, already-approved primary identity ("Mono audio"), and its reported field-17 value (10) skips the zigzag-decode correction ADR-019 already documents (correct value: 5).0007's Executive Summary's "100%/absolute certainty" language is not supported. Do not act on0007's recommendations directly — see0008's §3 for the maintainer decisions this raised (field 19's documentation should not be changed to "limit gate";0007's "approve all Phase 4 Promotions" recommendation needs to be evaluated per-item, not as a bundle). - Added 2026-09-13 (
ai-sessions/0013_FEATURE_RESULT_2026_09_13.md), housekeeping pointer for0012.ai-sessions/0012_CROSSCHECK_RESULT_2026_09_12.mdran a full, non-sampled independent re-derivation of all 130 findings in Gemini CLI's0011review. Headline result:0011's core protocol-decode content (opcodes, field mappings, byte-level payload claims) held up with zero errors; 5 citation errors and 1 overclaim were found, all in secondary/background material (co-occurring unrelated devices, one-off vendor commands, procedural timestamps), never in a primary protocol claim. Two pre-existing capture-count discrepancies0012itself surfaced (CAP-036's "34-frame" burst count,CAP-037's "34 reconnects" count) are now resolved — see0012§4 for both.
- Set up the Fedora development workstation (
WORKSTATION_PREPARATIONS.md) - Claude Code and Google Antigravity installed and configured
- GitHub repository created + first commit
- License chosen — AGPL-3.0 (see
DECISIONS.mdADR-002,LICENSE) - Core project documentation drafted:
AGENTS.md,PROJECT.md,PROJECT_RULES.md,ARCHITECTURE.md,PROTOCOL.md,PROTOCOL_NOTES.md(retired 2026-08-15, seeCHANGELOG.md),REVERSE_ENGINEERING.md,DECISIONS.md,CHANGELOG.md,README.md,CAPTURE_BLUETOOTH_HCI_SNOOP.md,TESTPLAN_BLUETOOTH_HCI_SNOOP.md,SCREENSHOTS_PIXEL_BUDS_APP.md,SCREENSHOTS_PIXEL_BUDS_WEB_APP.md - Review the repository for inconsistencies, vagueness, ambiguity,
contradictions, errors, or undocumented choices — both within each file
and between files — and address what's found. Status so far:
-
ARCHITECTURE.md— reviewed and revised (transport layer naming aligned with RFCOMM-primary reality, DI/scanning-policy open questions added) -
AGENTS.md— reviewed and revised (stale license section, BLE-only framing assumption, and duplicate heading artifacts fixed) -
PROJECT_RULES.md,PROJECT.md,DECISIONS.md,PROTOCOL.md,REVERSE_ENGINEERING.md,README.md,CHANGELOG.md,CAPTURE_BLUETOOTH_HCI_SNOOP.md,TESTPLAN_BLUETOOTH_HCI_SNOOP.md,WORKSTATION_PREPARATIONS.md— given a dedicated cross-consistency pass via the 2026-08-20 comprehensive documentation audit (seeCHANGELOG.md) and a further external audit on 2026-08-22/23 (seeAUDIT_REPORT_2026-08-22.mdandCHANGELOG.md's matching entry). This checklist item was left unchecked after that work already completed it — closed here to fix the staleness itself.
-
- Pipeline validation — the HCI snoop → bugreport →
btsnooz.pyextraction → Wireshark chain (RFCOMM/SPP + BLE dissectors) confirmed working viaCAP-001(Group Z), 2026-08-09. Logged in the Capture Index (CAPTURE_BLUETOOTH_HCI_SNOOP.md§9). - Pairing/bonding baseline — forget-and-re-pair captured via
CAP-002(Group A), 2026-08-09; a second, independent baseline viaCAP-003(Group R) and a third viaCAP-004(Group S). Logged in the Capture Index. - Log every capture session in the Capture Index
(
CAPTURE_BLUETOOTH_HCI_SNOOP.md§9) with a uniqueCAP-NNNID and metadata (firmware version, Android version, app version, capture method — perPROJECT_RULES.mdrule 11 and rule 14) — ongoing practice, not a one-time task; kept unchecked deliberately. - Optionally, as a deliberate one-time capture (not before), trigger the
factory-reset re-pair for comparison (
CAPTURE_BLUETOOTH_HCI_SNOOP.md§4.1 Group P #16 — destructive, also resets the Find My Device link, so this is a bonus capture, not a prerequisite). See alsoWORKSTATION_PREPARATIONS.md's Disaster Recovery section — this is the same procedure, deliberately triggered as an experiment rather than as an emergency recovery step.
Top priority (updated 2026-08-18) — these block implementation-readiness
for the app's core v1 features and outrank everything else below, including
the still-open edge-case protocol questions (DLCI 0x08's identity, Groups
0x04/0x05/0x09's semantics, the CTKD generalization, HFP battchg vs.
AT+BIEV discrepancy, etc. — those stay valuable research but are explicitly
lower priority than finishing ANC/Battery/EQ):
-
CAP-005/CAP-015(Group T) — EQ command isolation. Done 2026-08-18 viaCAP-015, a second, independent Group T session: field-to-band mapping promoted to 🟢 FACT (all 5 sliders individually isolated, 3 passes each), plus the ±6.0 band-gain clamp and a confirmed preset-quintet reference table (CAP-015-FINDINGS.md,PROTOCOL.md§4.2). -
CAP-006(Group B repeat) — ANC reliability confirmation. Done 2026-08-15 — isolated single-tap repeat of all four ANC modes; exactly 40x12"Set ANC state" frames in the whole log, one per tap, zero misses (CAP-006-FINDINGS.md§3).CAP-001's 2/6 gap does not reproduce under isolated conditions.DECISIONS.mdADR-009 updated,FrameEncoderimplementation block for the ANC command lifted. -
CAP-010/CAP-017/CAP-014(Group W) — stronger GATT cache-busting for live service discovery. Discovery goal achieved 2026-08-16 viaCAP-017, a fresh-GATT-client-app path not originally in this row's scope — 137 live discovery frames, full 15-service GATT profile recovered.CAP-014(2026-08-27) fixed that session's snaplen truncation but still did not close the mapping — the0x0f2a/0x0c0Xhandle→UUID mapping remains open (CAP-014-FINDINGS.md§4/§8): 3 attempts now, andpm clear com.android.bluetooth/the Pixel 9a — Group W's own actual candidate methods — remain untried in all of them. That combination (proven snaplen fix + an actually-untried cache-busting method) is the clear next step. -
CAP-016(Group U re-run) — case/bud-removal hardware events. Synced intoPROTOCOL.md2026-08-18 — promotes 3 🟢 FACTs (§5/§7): Buds-initiated reconnect on bud removal, ACL disconnect the instant both buds are re-docked, and case-lid open/close producing zero wire signal (now 2-capture-confirmed). New open items (RFCOMM channel-bounce trigger, ANC settable-toggles byte, a0x0044BLE notification burst, anAndroidHeadTrackerHID Feature report) tracked inPROTOCOL.md§6 andCAP-016-FINDINGS.md. -
CAP-008(Group V) — first real phone call. Done 2026-08-26. Both open questions resolved: the full HFP AT-command SLC handshake reoccurs on a fresh classic-link connection, and two clean SCO/eSCO pairs appear, one per call. DLCI 0x0a stayed silent through both calls, ruling it out as the call's audio path (CAP-021's later, unrelated 1123-frame burst on that same DLCI remains a separate, still-open question —PROTOCOL.md§6). SeeCAP-008-FINDINGS.md. -
CAP-009(Group X) — battery-level discrepancy bracket. Done 2026-08-23.AT+CIND/battchgconfirmed a stale single snapshot;AT+BIEVconfirmed per-earbud (Right, this session), not a fixed aggregate, and non-fixed-cadence —BATT-006closed, maintainer-approved (DECISIONS.mdADR-015). SeeCAP-009-FINDINGS.md.
Next, still important but behind the above:
- Capture the "Play sound on Left/Right earbud" (Find My Buds) action —
done,
CAP-025(2026-08-21). Left/Right confirmed 🟡 HYPOTHESIS (strong), video-correlated, proposed forPROTOCOL.md§4.4 promotion to 🟢 FACT pending maintainer sign-off. New finding: Case/"both" route through a separate Find Hub/Find-My-Device-Network mechanism with no local wire command — possibly a Zero-GMS hard limit, flagged to the maintainer inPROTOCOL.md§6 (Behavior) andCAP-025-FINDINGS.md§7/§8. - Passively capture a BLE scan to confirm the Battery Notification
advertisement — attempted,
CAP-011(2026-08-21), inconclusive. Fast Pair Service (0xFE2C) traffic confirmed present, but the procedure deviated (an active RFCOMM connection was present throughout, not the intended connection-free scan) and the sampled payloads don't structurally match the documented byte layout — seePROTOCOL.md§4.3 Option A andCAP-011-FINDINGS.md. Still open: a genuinely clean, connection-free repeat is needed — skeleton created 2026-09-09 asCAP-043(Group Q repeat, seeCAPTURE_BLUETOOTH_HCI_SNOOP.md§9 andai-sessions/0005_MAINTENANCE_RESULT_2026_09_09.md).CAP-043done 2026-09-13: genuinely clean isolation confirmed, a second confirmed non-match against Option A's documented layout — closes the active-connection-confound question, but only the idle/case-closed trigger condition was tested. A single-bud-insertion/removal bracket (the Fast Pair spec's own "optional" trigger) is proposed asCAPTURE_BLUETOOTH_HCI_SNOOP.mdGroup AP (plannedCAP-054,ai-sessions/0017_MAINTENANCE_RESULT_2026_09_13.mdPhase 2). -
CAP-013/CAP-031/CAP-032(Group A repeat) — whether "Forget" fully clears prior BLE association. Done 2026-08-27, on the fourth attempt (CAP-032) — the first three (CAP-001's original session,CAP-013,CAP-031) all either predate the question or failed to capture the pre-clearing-action window;CAP-032, extracted via the raw path instead of the lossybtsnoozfallback, finally captured it and found a clean counter-example (no prior BLE link/valid key for that session) —CAP-001's own session-specific puzzle (why that session had residual state) remains independently open, seePROTOCOL.md§6 (Behavior). SeeCAP-032-FINDINGS.md. - Updated 2026-08-28 — remaining planned captures not yet individually tracked here (each
already has its own row in
CAPTURE_BLUETOOTH_HCI_SNOOP.md§9's Capture Index; listed here only so this file's priority ordering covers them too, not as a duplicate description):CAP-018(Group Y,0x0044BLE-notification-burst isolation), and the still-uncaptured main run-through remainder —CAP-026(Group L, passive observation).CAP-027(Group N, touch gestures) is done, 2026-08-30 — seeCAP-027-FINDINGS.md.CAP-028(Group O, head gestures) run 2026-09-12 — inconclusive: zero wire-visible traffic during the claimed gesture window, but no active call/notification existed for a gesture to act on, so this cannot distinguish "functionally inert, as expected" from "gesture not triggered." Re-verified across the full log 2026-09-13, same clean negative (ai-sessions/0017_MAINTENANCE_RESULT_2026_09_13.mdPhase 3) — a correctly-scoped repeat with an active call/notification is designed as Group AQ (plannedCAP-055). Still to do:CAP-029(Group P, Conversation Detection voice trigger + the optional, destructive factory-reset comparison + the still-open shorter-press pairing-mode question), andCAP-030(Group Q items #19–20, Loud Noise Protection/Adaptive Audio, needs firmware ≥4.467 — worth double-checking this against the project'srelease_5.203baseline first, since the two version identifiers have never been explicitly reconciled,PROTOCOL.md§0.1). Lower priority than a cleanCAP-011repeat and a properly-done Group W attempt above. - Six captures planned 2026-09-05, follow-ups to
CAP-036'sOBS-004session — done and analyzed 2026-09-06, updated here 2026-09-08 (was stale: still listed as "planned" thoughPROTOCOL.md§8's 2026-09-06 changelog row already syncs their findings).CAP-037(Group AD, purpose-built repeat of the "Get ANC state" reconnect-reliability + dock-state-transition question) ran far longer than planned — 34 reconnects, 26/26 zero-missDECISIONS.mdADR-022 replications and 26/26 ADR-024 dock-state matches, the largest single-session replication of either on file.CAP-038(Group AE, realistic buds-out-of-case-and-worn reconnect) found aSettable-toggles=0x00reading immediately after physical case-removal, in unreconciled tension with ADR-024 (PROTOCOL.md§6, still open).CAP-039(Group AF, Set-vs-Get comparison) gave 10/10 same-session confirmations of ADR-024's trigger-independence.CAP-040(Group AG, DLCI 0x08 unmapped codes) found the app's own in-app Connect/Disconnect buttons produce zero wire signal, leaving its 7 target codes at N=1 each — inconclusive, still open.CAP-041(Group AH, connect-time burst vs. non-default settings) found the burst's length/shape signature invariant across 3 non-default states — a scoped negative at the length level; a full content diff remains open (Phase 4 item 1 below).CAP-042(Group AI, long idle bracket) found the periodic cross-channel push far sparser thanCAP-036's short sample suggested, with HFP dropping out of the sync entirely. See each capture's own findings file (in itscaptures/CAP-0NN-...folder) andPROTOCOL.md§6's matching open items for full detail.
-
Groundwork/tooling — done 2026-08-30. Governance, storage, and procedure now in place so the actual analysis work below can start; none of it constitutes analysis having happened yet:
DECISIONS.mdADR-017 (supersedes ADR-003) permits AI mechanical assistance — search,pbtkextraction, native.sodisassembly explanation — within a maintainer-decides-relevance boundary;WORKSTATION_PREPARATIONS.mddocumentspbtkinstallation/real scope/dependencies; a versioned APK storage structure exists (reverse-engineering/apk/v<versionName>-<versionCode>/, indexed in the git-trackedreverse-engineering/APK_VERSIONS.md, itself gitignored for the APK/decompiled output per.gitignore);APK_REVERSE_ENGINEERING_PROCEDURE.mddocuments the full pull → decompile → extract → search → analyze procedure, including the diff-against-previous-version pass and the out-of-scope exclusion list (AccountLinking/ OwnershipTransfer/AccessoryNonOwner/Firebase-Analytics-Crashlytics);REVERSE_ENGINEERING.md's template now requires a file+line citation per finding and a hypothesis-to-capture-test link. -
APK pulled — done 2026-08-30.
v1.0.955078536-10253511(base +arm64_v8a/xxhdpisplits), pulled from the maintainer's own Pixel 7a, hashed, and recorded inreverse-engineering/APK_VERSIONS.mdperAPK_REVERSE_ENGINEERING_PROCEDURE.md§2. -
JADX decompilation — done 2026-08-30.
jadx-output/(12,545 Java/Kotlin files); 22 non-fatal per-class errors, typical for an obfuscated multi-dex app of this size. -
apktool decompilation — done 2026-08-30.
apktool-output/(base) andapktool-output-arm64_v8a/(native libs live only in that split, not in base.apk). -
Keyword search (§4 pass) — done 2026-08-30, one pass; more passes still valuable. Found: no
libmaestro.so/libgfps.soanywhere (onlylibandroidx.graphics.path.so/libpw_tokenizer_jni.so— the app's Maestro logic is pure Kotlin, not a native binary, contra this project's original assumption); the app's own RFCOMM-socket-selection logic (gbm.java/fzd.java) and its two candidate SDP UUIDs ("pigweed"/"default"); literalmaestro_pw.*pw_rpc service/method names (Maestro.WriteSetting/GetSoftwareInfo,HeadGesture,EartipFitTest,Dosimeter,JitterBuffer,Multipoint,DynamicServerConfigService) and a survivingdev.pigweed.pw_rpc.MethodClientreference confirming the app's own transport vocabulary. Full write-up:REVERSE_ENGINEERING.md's "Identified relevant classes" section (10 entries). Not yet done: a second pass tracing howClassicBTReceiver's connection-state events lead intogbm's socket selection, and howfsz'sWriteSetting/fux's per-service calls obtain theirMethodClient— flagged as untraced inREVERSE_ENGINEERING.md's Call graph notes. -
Extract real
.proto/pw_rpc schemas — done 2026-08-30/2026-09-03, via manual decode, notpbtk.pbtk-jar-extractagainstbase.apkwrote 0.protofiles, and a 2026-09-03 follow-up confirmed this isn't a scope/targeting problem:pbtk-jar-extracthas no class-filter flag, and a manually-built 32-class targeted JAR (qjc/qja/qhr/nqx/fux/fsz/etc., plus the one legacyCodedInputStream/CodedOutputStream-signature class pair still present elsewhere in the APK) still produced 0 files — confirmed againstpbtk's ownjar_extract.pysource: its extraction requires a per-classmergeFrom(CodedInputStream)switch-structure in the generated class's own bytecode, which this APK'sGeneratedMessageLite.newMessageInfo(default, infoString, objects)reflection-based codegen never emits, for any class.pbtk's GUI shares the same extractor module, so it is not expected to differ. Solved instead viascripts/decode_rawmessageinfo.py(a dependency-freeRawMessageInfocompact-schema-string decoder, ported field-for-field from the publicprotobufruntime source):qjc/qja(5-alternative oneof),qhr(38 fields, all field-type/reference info recovered), andnqx(pw_rpc.RpcPacket, 7 fields) all decoded and cross-correlated against real wire bytes (CAP-020frames 1741/1935). SeeDECISIONS.mdADR-019 (maintainer sign-off obtained) for the accepted findings. Not fully closed: onlyqhrfields 4, 7, 12 (field-number only), and 29 have been wire-cross-checked so far — fields 11, 15, 17, 19, 22, 27, 28 are confirmed on the wire but not yet run through this same method (tracked in this file's "Targeted research follow-ups" section). -
DLCI 0x02 channel-ownership question — resolved 2026-08-30 (narrow promotion).
DECISIONS.mdADR-018 (Option 2, maintainer-approved): DLCI 0x02 confirmed 🟢 FACT as the companion app's own internal RFCOMM channel (SDP UUID25e97ff7-...= RFCOMM channel 1 = DLCI 0x02, cross-checked againstCAP-001/CAP-002/CAP-032), via the app's owngbm.java/fzd.javaselection logic — seePROTOCOL.md§2.2a. Not fully resolved: whether the Sent-direction payload content specifically carrieslibmaestro's settings commands, in general — still 🟡 HYPOTHESIS (strong) perDECISIONS.mdADR-019's own scope note, though now substantially strengthened for the 4qhrfields ADR-019 sampled. Settling it further means running more fields through the manual-decode item above, notpbtk. -
CAP-033(Group AA) — done 2026-08-30. Tested whether the second, never-observed-on-the-wire "default internal rfcomm socket" SDP UUID (gbm/fzd) ever appears when SDP is queried by the OS's own pairing flow before the companion app opens (SDP-001);SDP-002not attempted (no firmware update pending). Result: the "default" UUID still does not appear; the full named service list (including "MAESTRO APP") is returned even with the app force-stopped — but a confirmed Forget-before-Force-stop procedure deviation and a never-executed step 3 (opening the app for a baseline comparison) capSDP-001at 🟡 HYPOTHESIS, not a clean result either way. A proper isolation-clean repeat is still needed (seeCAP-033-FINDINGS.md§8). New lead, unplanned: the session's SDP browse also named DLCI 0x08 "GSND CONTROL" and DLCI 0x0a "GSND AUDIO" for the first time — seePROTOCOL.md§2.3/§6. -
GMS/Play Services reverse-engineering — decided out of scope, 2026-09-07 (
DECISIONS.mdADR-025). A project-wide audit (AUDIT_REPORT_2026-09-07.md§1.0) found no trace of DLCI 0x04's Fast Pair Message Stream or DLCI 0x08's private envelope anywhere in the companion app's own decompiled code — both appear to be implemented inside Google Play Services itself. The resulting scope question (should this project decompile GMS to close that gap?) is now decided: no. DLCI 0x04/0x08FrameEncoder/FrameDecoderwork proceeds clean-room, from wire-capture evidence alone (plus, for DLCI 0x04, the public Fast Pair spec) — the same method already used for ANC/Find My Buds/EQ, none of which ever needed a companion-app code cross-reference. No further APK-search effort should be spent trying to locate DLCI 0x04/0x08 transport code in this companion app;REVERSE_ENGINEERING.md's empty Message Group/Code register for these two channels reflects this, not an unfinished search.
- Fill in the UUID register (
REVERSE_ENGINEERING.md§UUID register) — cross-referencing pass done 2026-09-09 (ai-sessions/0004_MAINTENANCE_RESULT_2026_09_09.mdTask 2), clean negative.CAP-034's 8 wire-confirmed GATT UUIDs (Fast Pair Service0xFE2C/FE2C1233–FE2C1239, Device Information0x180A, Battery Service0x180F/0x2A19, Firmware Revision0x2A26, Accessory Non-Owner Service15190001-..., "Unknown Service"109b862f-...) were searched for across the entire decompiled tree (jadx-output/andapktool-output/smali*/, both full and short UUID forms) — zero genuine matches found, corroboratingDECISIONS.mdADR-025's existing finding that this companion app's own code contains no Fast-Pair-GATT handling at all. Recorded as a checked negative in the register itself rather than left silently untried. Kept unchecked as "exhaustive" is still not claimed — a future APK version or a different keyword angle could still surface something — but this specific, concretely-scoped next step is done. - Message Group/Code register — confirmed empty by design, not stalled (updated 2026-09-09).
The Fast Pair Message Stream framing hypothesis is confirmed (
PROTOCOL.md§2.1/§4.1), butDECISIONS.mdADR-025 andREVERSE_ENGINEERING.md's own register note already establish this table is expected to stay empty for DLCI 0x04/0x08 specifically — their transport lives inside Google Play Services, not the companion app's own decompiled code, so no vendor-specific Group/Code value will ever be found there to fill this table with. Checked here, not unfinished work. - Resolve the framing question — updated 2026-09-09, resolved per channel, not monolithic.
Per
PROTOCOL.md§2.3/ARCHITECTURE.md§5's own per-channel implementation gate: DLCI 0x02 (Pigweedpw_hdlc) and DLCI 0x04 (official Fast Pair Message Stream) are both 🟢 FACT and implementation-unblocked —FrameEncoder/FrameDecoderwork for either is not blocked on this item. Only DLCI 0x08's own identity remains 🔴 open (structurally decodable,[Group][Code] [Length][Value], but which protocol it belongs to is unresolved) — kept unchecked for that one remaining channel only, not for the framing question as a whole. - Document the full connection lifecycle with real capture evidence — updated 2026-09-09,
step 3 now analyzed and maintainer-reviewed.
PROTOCOL.md§5.1 already promotes the classic BR/EDR link-establishment mechanics (steps 1–2) to 🟢 FACT across seven independent captures. Step 3 (the RFCOMM channel-opening sequence) is now analyzed: a newPROTOCOL.md§5.2 records 🟡 HYPOTHESIS (strong) that DLCI 0x02 (libmaestro) reliably opens last of the five data-carrying RFCOMM channels on a fresh reconnect (6/6 independent instances acrossCAP-036/CAP-037/CAP-041, zero counter-examples in that condition, one honestly-scoped exception during a mid-session channel-bounce) — the maintainer reviewed this directly and explicitly chose to keep it at HYPOTHESIS rather than promote, pending more evidence or an explanation for the exception (seeai-sessions/0004_MAINTENANCE_RESULT_2026_09_09.mdTask 3 for the full analysis). Still open: steps 4/6 (the Message Stream/libmaestrohandshake's own internal content ordering beyond channel-open timing, and user-triggered-command timing) — not attempted this pass. - Bring the first command to full 🟢 FACT status — updated 2026-09-09, ANC and battery Option C
already done; only battery Option A remains, and that item is capture-blocked. ANC
(
PROTOCOL.md§4.1) reached full FACT status 2026-08-12 (see the checked item immediately below). Battery via HFP (PROTOCOL.md§4.3 Option C) is also already 🟢 FACT (DECISIONS.mdADR-015/ADR-023). Only battery Option A (the Fast Pair BLE Battery Notification) remains open, and it is specifically blocked on the still-outstanding clean, connection-free BLE-scan repeat this file's Phase 1 section already tracks (CAP-011was inconclusive; skeleton now created asCAP-043, see Phase 1 above) — not a research gap an AI session can close without that capture. - Bring ANC mode switching to full 🟢 FACT status (
PROTOCOL.md§4.1) — done 2026-08-12 via deskresearch correlation against the official Fast Pair "Hearable Controls" spec +CAP-001's existing capture.DECISIONS.mdADR-009 (added 2026-08-15) blockedFrameEncoderfor this command pendingCAP-006, since 2 ofCAP-001's 6 ANC taps produced no command frame.CAP-006(2026-08-15) resolved this — 4/4 isolated taps produced a matching frame, zero misses — and ADR-009 was updated to lift the block.FrameEncoder/FrameDecoderfor the ANC command is now implementation-ready perAGENTS.md§6. - Log every hypothesis test in the relevant capture's
CAP-NNN-FINDINGS.mdbefore promoting a finding from HYPOTHESIS to FACT (PROJECT_RULES.md§4) - Added 2026-08-23 — maintainer sign-off session on pending FACT promotions. Done
2026-08-23. Per
AGENTS.md§6 an agent may propose but never commit these; the maintainer reviewed all three and approved: Find My Buds Left/Right → 🟢 FACT (PROTOCOL.md§4.4,DECISIONS.mdADR-011); the wire-baseline firmware version"release_5.203"→ 🟢 FACT (PROTOCOL.md§0.1, ADR-012); the general-purpose DLCI 0x02 settings-write envelope shape → 🟢 FACT, but explicitly not its 9+ individual field mappings, which stay 🟡 HYPOTHESIS per the maintainer's own narrower decision (PROTOCOL.md§4.5, ADR-013).
- Set up the Android Studio project per
ARCHITECTURE.md— done and verified 2026-09-13 (ai-sessions/0013_FEATURE_RESULT_2026_09_13.mdPhase 7). Five Gradle modules (:app,:ui,:domain,:data,:hardware) atandroid/, version catalog (android/gradle/libs.versions.toml, pinned versions perAGENTS.md§10)../gradlew assembleDebug testDebugUnitTest testactually run in this environment (Gradle 9.5.1 wrapper pinned to 8.9, JDK 21, Android SDKandroid-34/build-tools34.0.0) — builds a realapp-debug.apk, 232 unit tests, 0 failures. - Decide dependency injection approach — resolved 2026-09-13: Hilt (
DECISIONS.mdADR-028,ARCHITECTURE.md§10/§15 updated).:app's composition root wired accordingly this same session — seeai-sessions/0013_FEATURE_RESULT_2026_09_13.md. - Decide the passive-scanning policy for the Fast Pair Battery
Notification — resolved as a bounded exception (filtered,
foreground-triggered, time-boxed); see
DECISIONS.mdADR-006,AGENTS.md§7,ARCHITECTURE.md§9.1 - Implement
ProtocolCodec(FrameEncoder/FrameDecoder) with unit tests for ANC — done and verified 2026-09-13 (ai-sessions/0013_FEATURE_RESULT_2026_09_13.mdPhase 7).:data'sAncFrameEncoder/AncFrameDecoder(DLCI 0x04 Group0x08) tested against realtshark-extracted fixture bytes fromCAP-001/CAP-006/CAP-036(225 passing tests, including a 206-case fuzz-adjacent malformed-input sweep,AGENTS.md§11). EQ/Battery/Find My Buds codecs remain unimplemented, as scoped. - Implement
BudsTransport(RFCOMM primary, secondary GATT for case/charging characteristics) andConnectionStateMachine— partially done 2026-09-13.ConnectionStateMachineand theBudsTransportinterface + a scriptedFakeBudsTransportare implemented and unit-tested (7/7 passing,ai-sessions/0013_FEATURE_RESULT_2026_09_13.mdPhase 7) — but the real,BluetoothSocket-backedRfcommBudsTransportis only sketched (compiles, follows the sealed-error-conversion/Dispatchers.IOrules) and explicitly not verified against real hardware, and per-DLCI socket multiplexing (PROTOCOL.md§2.3's three channels) is left as a documented// TODO(verify), not implemented — this checkbox stays open until that part is. - Implement
BudsRepository/BudsRepositoryImplwiring:datato:domain(ARCHITECTURE.md§2.1,DECISIONS.mdADR-001) — not attempted 2026-09-13, out of that session's own scope (only theBudsRepositoryinterface exists so far, in:domain). - First working end-to-end connection + battery status shown in the UI.
Recommended mechanism (added 2026-08-23): HFP (
PROTOCOL.md§4.3 Option C) — already 🟢 FACT and not blocked, unlike Option A (still inconclusive, see Phase 1) or Option B (battery message code unconfirmed).
- Execute
TESTPLAN_BLUETOOTH_HCI_SNOOP.mdon at least 2 devices (differing Android version and/or OEM, including GrapheneOS as the primary reference target perARCHITECTURE.md§1) - Update
README.mdwith build instructions once the app builds - Decide minimum supported Android API level — resolved 2026-09-13: API 34 (Android 14),
matching compile/target SDK (
DECISIONS.mdADR-029,ARCHITECTURE.md§1/§15). Applied toandroid/'s:app/:hardware/:uimodules (minSdk = 34). - Decide multi-device (multiple paired Buds) support for v1 and record it
in
PROJECT.mdscope +DECISIONS.md(currently open, seeARCHITECTURE.md§15) - Prepare the first public release (tag,
CHANGELOG.mdentry, GitHub Release per the manual-update-distribution decision inAGENTS.md§1)
(Fill in as quick fixes are made — see PROJECT_RULES.md rule 13. Every
entry here should be short-lived: either resolved properly or promoted to a
tracked task above.)
- Capture extraction path matters, added 2026-08-28. Four captures (
CAP-012,CAP-013,CAP-017,CAP-031) lost significant byte-level payload content to severe ACL truncation from thebtsnooz.py-from-bugreport fallback path (CAPTURE_BLUETOOTH_HCI_SNOOP.md§3 step 4); the one session extracted via the rawbtsnoop_hci.logpath instead (CAP-032) came out fully untruncated. Always check §3 step 3 (the raw file) first and prefer it whenever present — seeCAPTURE_BLUETOOTH_HCI_SNOOP.md§3's own PROPOSAL note for the full detail. Verified 2026-08-30 (audit pass): aframe.cap_len == frame.lensweep across all 29 non-plannedcaptures confirms these exact 4 are the only ones affected —CAP-017is a filename-invisible case (named-btsnoop_hci.log, not-btsnooz_hci.log, despite being truncated by a different mechanism, a phone-side snaplen setting) — no further silently-truncated log exists among the remaining 25.
Open architectural and protocol questions are tracked at their source only
— this file does not keep a second, synchronized checkbox list of them, since
that duplication is exactly what caused this file to fall out of sync with
ARCHITECTURE.md once already (see CHANGELOG.md). Each question has exactly
one home:
- Protocol-level open questions (framing hypothesis, unconfirmed opcodes,
wire-visibility of on-device-only features, etc.) →
PROTOCOL.md§6. - Architecture-level open questions (DI framework, minimum Android API
level, multi-device scope, etc.) →
ARCHITECTURE.md§15.
Check those sections directly when deciding what's still undecided; resolving
one only requires updating it in that one place, plus a DECISIONS.md entry
where the rule requires one (PROJECT_RULES.md §3).
https://github.com/tedsluis/opencontrolpixelbudspro2/blob/main/TODO.md - https://tedsluis.github.io/opencontrolpixelbudspro2/TODO