Skip to content

Commit 885bba0

Browse files
tedsluisclaude
andcommitted
docs: decide minimum Android API level (34) and refresh README (session 0014)
Maintainer decision: minimum supported Android API is 34 (Android 14), matching the already-fixed compile/target SDK exactly (DECISIONS.md ADR-029) — the last remaining open item in ARCHITECTURE.md §15. Applied to android/'s :app/:hardware/:ui modules (minSdk 26 -> 34); rebuilt and re-tested, zero failures. README.md refreshed to reflect the project's actual current state: capture count, decision count, the three previously-open architecture questions now resolved (Hilt, minimum API, Find My Buds Case scope), and the Android app scaffolding that now exists under android/. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01V2WfHWj4DFbq79FSae5KdV
1 parent abb5c0c commit 885bba0

11 files changed

Lines changed: 208 additions & 45 deletions

ARCHITECTURE.md

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ Communication happens over two transports:
3131
proprietary RFCOMM traffic — not confirmed, no HID report content has been captured or decoded.
3232
Not treated as a confirmed transport for this app's own use until decoded.
3333

34-
Compile/target SDK: API 34 (Android 14). Minimum supported Android API: **TBD**see
35-
the open question in §15; do not treat "API 34" as if it already answered that. Primary
36-
reference OS: GrapheneOS, with compatibility maintained for stock AOSP-based ROMs.
34+
Compile/target/minimum SDK: **API 34 (Android 14)**decided 2026-09-13, `DECISIONS.md` ADR-029;
35+
see §15's "already decided" list. Primary reference OS: GrapheneOS, with compatibility maintained
36+
for stock AOSP-based ROMs.
3737

3838
```
3939
┌──────────────────────────────────────────────────────┐
@@ -529,18 +529,6 @@ undecided (see §15's "Already decided, not open" list, updated to match).
529529

530530
> Move to `DECISIONS.md` once decided, following the ADR template.
531531
532-
- [ ] Minimum supported Android API level: compile/target SDK is set at API 34
533-
(Android 14), but the minimum SDK for broader AOSP-ROM compatibility is
534-
not yet fixed. This is not blocked on a single API, so "TBD" here does
535-
not mean unplanned: the generic battery broadcast (§4 option 0,
536-
`BluetoothDevice.ACTION_BATTERY_LEVEL_CHANGED`) requires **API 31+**,
537-
but §4's options 1–4 (Fast Pair advertisement, Message Stream, HFP,
538-
GATT) are the confirmed primary paths and do not depend on API 31 — so a
539-
lower min API (e.g. API 26 for `CompanionDeviceManager`, ADR-005) is not
540-
blocked by option 0's availability, which simply degrades gracefully to
541-
"unavailable on this API level, other options still work." The exact
542-
floor still depends on which other BLE/Bluetooth APIs (foreground
543-
service types, etc.) turn out to be required.
544532
- [ ] Added 2026-08-14: is the observed Bluetooth HID surface (§1) architecturally relevant to
545533
`:hardware`/`BudsTransport` — i.e. does any control feature this app needs actually route
546534
through HID reports rather than RFCOMM/GATT — or is it exclusively used by parts of the
@@ -561,7 +549,11 @@ undecided (see §15's "Already decided, not open" list, updated to match).
561549
> **dependency injection — Hilt** (see §10, `DECISIONS.md` ADR-028, decided
562550
> 2026-09-13); **Find My Buds Case/"both simultaneously" — out of scope for v1**
563551
> (`PROJECT.md` non-goals, `DECISIONS.md` ADR-027, decided 2026-09-13) — Left/Right
564-
> ring is unaffected and already implemented.
552+
> ring is unaffected and already implemented; **minimum supported Android API level
553+
> — API 34 (Android 14), same as compile/target SDK** (§1, `DECISIONS.md` ADR-029,
554+
> decided 2026-09-13) — no lower-API compatibility path is pursued; `CompanionDeviceManager`
555+
> (API 26, ADR-005) and the generic battery broadcast (API 31, §4 option 0) are both
556+
> trivially satisfied at this floor.
565557
566558
## 16. Attribution
567559

DECISIONS.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,5 +1417,34 @@ motivated this).
14171417
open. Every future module needing a dependency graph entry uses Hilt's `@Inject`/`@Provides`
14181418
conventions rather than a hand-rolled locator.
14191419

1420+
## ADR-029 — Minimum supported Android API level: API 34 (Android 14)
1421+
1422+
- **Date**: 2026-09-13
1423+
- **Status**: Accepted
1424+
- **Context**: `ARCHITECTURE.md` §15 left the minimum supported Android API level open — compile/
1425+
target SDK was already fixed at API 34 (Android 14), but how far down the minimum should go for
1426+
broader AOSP-ROM compatibility was undecided. The open item itself noted this wasn't blocked on a
1427+
single API: the generic battery broadcast (`ARCHITECTURE.md` §4 option 0) needs API 31+, but the
1428+
confirmed primary battery/ANC/EQ paths (Fast Pair advertisement, Message Stream, HFP, GATT) don't
1429+
depend on it, and `CompanionDeviceManager` (`DECISIONS.md` ADR-005) only needs API 26. The
1430+
maintainer decided directly, in conversation, that the minimum should simply match the
1431+
already-fixed compile/target SDK rather than support a wider, lower floor.
1432+
- **Options considered**:
1433+
- A lower minimum (e.g. API 26, the floor `CompanionDeviceManager` itself needs) for broader
1434+
AOSP-ROM/older-device compatibility, accepting that API 31's generic battery broadcast (a cheap
1435+
supplementary check, not a required mechanism) degrades gracefully below that version.
1436+
- **API 34 (Android 14), matching compile/target SDK exactly** — chosen. Simplest option: no
1437+
version-gated code paths anywhere in the app, and this project's primary reference platform
1438+
(GrapheneOS, `ARCHITECTURE.md` §1) tracks current Android releases closely, so a lower floor
1439+
buys little real compatibility benefit for this project's actual user base.
1440+
- **Decision**: minimum supported Android API level is **34 (Android 14)**, identical to
1441+
compile/target SDK. No lower-API compatibility path is pursued.
1442+
- **Consequences**: `ARCHITECTURE.md` §1/§15 updated to record this as decided, not open. The
1443+
Android Gradle project's `minSdk` is set to 34 across every module that declares one (`:app`,
1444+
`:hardware`, `:ui`) — simpler than the API-26 floor used provisionally before this decision, since
1445+
every Bluetooth/battery mechanism this project relies on is available well below API 34 anyway.
1446+
This forecloses running on older Android versions/ROMs that can't be updated past API 33, a
1447+
deliberate trade-off given this project's GrapheneOS-first target.
1448+
14201449
---
14211450
https://github.com/tedsluis/opencontrolpixelbudspro2/blob/main/DECISIONS.md - https://tedsluis.github.io/opencontrolpixelbudspro2/DECISIONS

README.md

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@
33
An independent, open-source Android app to fully control the **Google Pixel Buds
44
Pro 2** without the official Pixel Buds app or Google Play Services.
55

6-
> **Status:** protocol-reconstruction phase. There is no application code yet — no
7-
> Gradle project or module skeleton has been created (see `TODO.md` Phase 4). The
8-
> protocol knowledge itself, however, has matured well beyond "early research": ANC
9-
> mode switching, Find My Buds (Left/Right), battery reporting (via HFP), and the
10-
> equalizer's live-write path are all confirmed 🟢 FACT and implementation-ready
11-
> (`PROTOCOL.md`, `DECISIONS.md`). See `TODO.md` for the current task list and
12-
> readiness assessment.
6+
> **Status:** protocol reconstruction is mature, and Android app development has begun. ANC mode
7+
> switching, Find My Buds (Left/Right), battery reporting (via HFP), and the equalizer's live-write
8+
> path are all confirmed 🟢 FACT and implementation-ready (`PROTOCOL.md`, `DECISIONS.md`). A real,
9+
> building, unit-tested Android Studio project now exists at [`android/`](./android) — five Gradle
10+
> modules, ANC's `FrameEncoder`/`FrameDecoder` implemented and tested against real capture bytes, a
11+
> working Hilt-wired composition root — but it has **no UI screens for real device control yet** and
12+
> has **never been run against real Pixel Buds hardware** (see `TODO.md` Phase 4 for exactly what's
13+
> done vs. still open).
1314
1415
> ## ⚠️ Disclaimer: hardware risk
1516
>
@@ -47,26 +48,37 @@ the Pixel Buds Pro 2 first has to be reconstructed through Bluetooth traffic
4748
analysis and reverse engineering of the Android APK. That knowledge is then used
4849
to design, implement, test, and document a native Android app.
4950

50-
## Current state (2026-09-08)
51+
## Current state (2026-09-13)
5152

52-
- **Captures:** 42 registered sessions (`CAP-001``CAP-042`), most analyzed and a handful still
53-
planned — see `CAPTURE_BLUETOOTH_HCI_SNOOP.md` §9's Capture Index.
53+
- **Captures:** 52 registered sessions (`CAP-001``CAP-052`), most analyzed and a handful still
54+
planned — see `CAPTURE_BLUETOOTH_HCI_SNOOP.md` §9's Capture Index. A full, independent,
55+
non-sampled re-derivation of every finding from an earlier full-catalog review found the core
56+
protocol-decode content held up with zero errors (`ai-sessions/0012_CROSSCHECK_RESULT_2026_09_12.md`).
5457
- **APK analysis:** one companion-app version fully pulled, decompiled, and analyzed
5558
(`v1.0.955078536-10253511`) — see `reverse-engineering/APK_VERSIONS.md`. DLCI 0x04/0x08's own
5659
transport code was not found anywhere in it (`DECISIONS.md` ADR-025) — both channels are
5760
implemented from wire-capture evidence alone, not by decompiled-code cross-reference.
58-
- **Decisions:** 25 recorded architecture/protocol decisions (`DECISIONS.md`), every
59-
🟢 FACT promotion in `PROTOCOL.md` traceable to an explicit maintainer sign-off.
61+
- **Decisions:** 29 recorded architecture/protocol decisions (`DECISIONS.md`), every
62+
🟢 FACT promotion in `PROTOCOL.md` traceable to an explicit maintainer sign-off. The three
63+
previously-open architecture questions are now all decided: dependency injection is **Hilt**
64+
(ADR-028), minimum supported Android API is **34/Android 14**, matching compile/target SDK
65+
(ADR-029), and Find My Buds for the Case/"both simultaneously" is an explicit, permanent v1
66+
non-goal (ADR-027, Zero-GMS scope limit — see `PROJECT.md`).
6067
- **Confirmed and implementation-ready:** ANC/Transparency/Adaptive mode switching,
6168
Find My Buds (Left/Right), battery reporting (HFP), the equalizer's live-write
6269
path, touch-controls top-level toggle and press-and-hold assignment, mono audio,
63-
multipoint, volume EQ, volume balance, and the "Bud return" case sound.
70+
multipoint, volume EQ, volume balance (including its Left/Right polarity, ADR-026),
71+
and the "Bud return" case sound.
6472
- **Still open:** touch-controls' head-gestures and ANC-mode-rotation sub-features,
65-
in-ear detection, EQ preset persistence semantics, Find My Buds for
66-
the case (a Zero-GMS scope question, not just a research gap), and per-component
73+
in-ear detection, EQ preset persistence semantics, and per-component
6774
serial-number reading.
68-
- **Not started:** the Android app itself — no Gradle project exists in this
69-
repository yet.
75+
- **App development has started** — see [`android/`](./android): five Gradle modules
76+
(`:app`, `:ui`, `:domain`, `:data`, `:hardware`), a Hilt-wired `:app` composition root, and ANC's
77+
`FrameEncoder`/`FrameDecoder` implemented and unit-tested against real, `tshark`-extracted capture
78+
bytes (`./gradlew assembleDebug test` builds a real debug APK, all unit tests passing). **Not yet
79+
done:** any UI screen for actually controlling the Buds, a real `BluetoothSocket`-backed transport
80+
verified against hardware, and EQ/Battery/Find-My-Buds codecs (`:data` has ANC only so far) — see
81+
`TODO.md` Phase 4 for the exact, up-to-date checklist.
7082

7183
## Approach
7284

@@ -143,11 +155,11 @@ humans and AI coding assistants working on it:
143155
| `AI_SESSION_LOG_PROCEDURE.md` | Naming scheme, category vocabulary, and numbering discipline for logging AI-agent prompts/results into `ai-sessions/` |
144156
| `ai-sessions/INDEX.md` | Registry of every logged AI-agent prompt/result pair under `ai-sessions/` — check before assigning the next number |
145157
| `scripts/lint_docs.py` | Grep-based doc lint (dead filenames, unregistered IDs, stale project name) — run before committing a doc change |
158+
| `android/` | The Android Studio project itself (five Gradle modules: `:app`, `:ui`, `:domain`, `:data`, `:hardware`) — see the "Current state" section above for what's implemented so far |
146159

147160
## Target platform
148161

149-
- Compile/target SDK: API 34 (Android 14)
150-
- Minimum supported Android API: **TBD** (see `ARCHITECTURE.md` §15)
162+
- Compile/target/minimum SDK: **API 34 (Android 14)**`DECISIONS.md` ADR-029
151163
- Primary reference OS: GrapheneOS, with compatibility maintained for stock
152164
AOSP-based ROMs
153165

TODO.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ nothing here is a second copy of that detail, only a pointer plus the reasoning
1717
current list. Per `AGENTS.md` §6 this step can only be done by the maintainer, not an agent.
1818
- ~~DI approach (Hilt vs. manual) — Phase 4.~~ **Resolved 2026-09-13: Hilt** (`DECISIONS.md`
1919
ADR-028).
20-
- Minimum Android API level — Phase 5.
20+
- ~~Minimum Android API level — Phase 5.~~ **Resolved 2026-09-13: API 34** (`DECISIONS.md` ADR-029).
2121
- ~~Find My Buds Case/"both simultaneously" — whether to accept a Google Find Hub/account-mediated
2222
fallback for this one sub-feature or ship v1 without local Case-ring support (`PROTOCOL.md` §6,
2323
Behavior) — a genuine Zero-GMS scope trade-off, not a research gap; no capture or static
@@ -525,8 +525,9 @@ lower priority than finishing ANC/Battery/EQ):**
525525
(differing Android version and/or OEM, including GrapheneOS as the
526526
primary reference target per `ARCHITECTURE.md` §1)
527527
- [ ] Update `README.md` with build instructions once the app builds
528-
- [ ] Decide minimum supported Android API level and record it in
529-
`DECISIONS.md` (currently open, see `ARCHITECTURE.md` §15)
528+
- [x] **Decide minimum supported Android API level — resolved 2026-09-13: API 34 (Android 14),
529+
matching compile/target SDK** (`DECISIONS.md` ADR-029, `ARCHITECTURE.md` §1/§15). Applied to
530+
`android/`'s `:app`/`:hardware`/`:ui` modules (`minSdk = 34`).
530531
- [ ] Decide multi-device (multiple paired Buds) support for v1 and record it
531532
in `PROJECT.md` scope + `DECISIONS.md` (currently open, see
532533
`ARCHITECTURE.md` §15)
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
# 0014_MAINTENANCE_PROMPT_2026_09_13.md — Decide minimum Android API level, refresh README.md, commit and push
2+
3+
**Number:** 0014
4+
**Category:** MAINTENANCE
5+
**Date:** 2026-09-13
6+
**Title:** Decide minimum Android API level, refresh README.md's project status, commit and push
7+
8+
---
9+
10+
## Mandatory reading order
11+
12+
Per `AGENTS.md` §0.1 and `AI_SESSION_LOG_PROCEDURE.md` §8: `AGENTS.md`, `PROJECT.md`,
13+
`PROJECT_RULES.md`, and `DECISIONS.md` in full at minimum before any other action, plus
14+
`ARCHITECTURE.md` (§1/§15 specifically, for the open minimum-API question this prompt resolves),
15+
`TODO.md` (Phase 5's matching checklist item), and `README.md` (the file being updated) — all
16+
already read in full earlier in this same conversation, still current.
17+
18+
## Task (verbatim, as given by the maintainer in chat, translated from Dutch)
19+
20+
> 1) I decide that Android 14 is the minimum version. Process this decision in the relevant files.
21+
> 2) Update the current state of the project in README.md.
22+
> 3) Commit and push the files. If possible, make separate commits per prompt.
23+
24+
This is a direct continuation of the same chat session that authored and resolved
25+
`ai-sessions/0013_FEATURE_PROMPT_2026_09_13.md`/`ai-sessions/0013_FEATURE_RESULT_2026_09_13.md`
26+
that session's own Phase 5 (`TODO.md`'s "Decide minimum supported Android API level" item,
27+
`ARCHITECTURE.md` §15) is what this prompt closes.
28+
29+
## Scope
30+
31+
1. Record the minimum-Android-API-level decision (Android 14 / API 34, matching this project's
32+
already-fixed compile/target SDK) as a `DECISIONS.md` ADR, per `AGENTS.md` §6/§15 — this session
33+
has the maintainer's direct, explicit decision in-conversation, so no proposal/sign-off gate
34+
applies here (unlike `0013`'s Phase 4/5/6 items, which were AI-drafted proposals). Apply the
35+
decision to every relevant document (`ARCHITECTURE.md`, `TODO.md`) and to the actual Android
36+
project (`android/`'s `:app`/`:hardware`/`:ui` `minSdk`), then rebuild/re-test to confirm nothing
37+
regresses.
38+
2. Update `README.md`'s "Current state" section (and any other stale summary text, e.g. the top
39+
status callout and the "Target platform" section) to reflect the project's actual current state —
40+
captures, decisions, protocol readiness, and, new since the last README update, the Android app
41+
scaffolding itself (five Gradle modules, ANC codec implemented/tested, Hilt wired, minimum API
42+
decided).
43+
3. Commit and push. Per the maintainer's own instruction, make separate commits per distinct prior
44+
"prompt" where practical, rather than one bundled commit — applied here as: session `0012`'s own
45+
files, session `0013`'s own files (including the three decisions approved earlier this same chat
46+
turn and the new `android/` project), and this prompt's own files (minSdk decision + README
47+
refresh), as three separate commits, in that order, followed by a single push.
48+
49+
---
50+
https://github.com/tedsluis/opencontrolpixelbudspro2/blob/main/ai-sessions/0014_MAINTENANCE_PROMPT_2026_09_13.md - https://tedsluis.github.io/opencontrolpixelbudspro2/ai-sessions/0014_MAINTENANCE_PROMPT_2026_09_13

0 commit comments

Comments
 (0)