Skip to content

Device: Correct AirPods info fields and expand protocol coverage#542

Merged
d4rken merged 1 commit into
mainfrom
feat/aap-protocol-catalog
Apr 24, 2026
Merged

Device: Correct AirPods info fields and expand protocol coverage#542
d4rken merged 1 commit into
mainfrom
feat/aap-protocol-catalog

Conversation

@d4rken

@d4rken d4rken commented Apr 24, 2026

Copy link
Copy Markdown
Member

What changed

AirPods device info (the details shown in the device settings card) now surfaces more accurate fields than before:

  • Hardware version, pending firmware version, and per-earbud first-pairing dates are captured alongside the existing name / model / serial / firmware.
  • What CAPod used to label "build number" is actually Apple's "marketing version" — the field label is corrected and the value keeps loading from existing cached device state on upgrade (no data loss).
  • On AirPods Pro 3, CAPod now politely asks the case for its own metadata (VID/PID/color/firmware) — captured for future iteration; no UI yet.
  • No change to on-screen controls, ANC, press controls, or battery — this PR is protocol-layer plumbing that unlocks future work.

Debug logs are more informative, which helps troubleshoot user reports: every AAP message that reaches the app is now labeled with its protocol name (e.g. Stream State Info, Sleep Detection Update) instead of a bare hex opcode.

Credits: added the apple-wireshark dissector by Pablo Aul to the Thank You and Licenses sections — this PR adopts its catalog as a third reverse-engineering reference alongside LibrePods and MagicPodsCore.

Technical Context

  • Catalogues all 67 known message types (AapMessageType) and all 66 control IDs (AapControlId) from the Wireshark dissector as typed enums, replacing scattered const val integers. Settings-freshness tracking (KNOWN_NON_SETTINGS_COMMANDS) expanded to every push-only opcode so the AAP quality boost in PodDevice.computeAapBoost keeps working for catalogued-but-not-modeled messages.
  • AapMessage generalised to a sealed AapPacket hierarchy (Connect / ConnectResponse / Disconnect / DisconnectResponse / Message / Unknown). Previously the read loop parsed Connect Response frames as a Message with commandType=0, misinterpreting the status field — now parsed properly. Pro 1 (firmware 51.9.6) reports non-zero feature bits 0xE251_0004_E1B1_0004 here; Pro 2 USB-C and Pro 3 report all zeros.
  • DeviceInfo parser rewritten around the 15-segment schema (segments 11-12 are fixed 17-byte UUIDs that may contain 0x00 — the previous NUL-splitter silently truncated the payload after segment 10 and mis-labeled segments 5-7). Cached state serialises via @SerialName("buildNumber") on the renamed marketingVersion field so upgrades don't drop cached values.
  • Case Info probe (0x22) is allowlisted to AIRPODS_PRO3 only; dispatched from the read loop after a successful Connect Response, bypasses the outbound command queue (not an AapCommand, not ear-gated, not counted as pending).
  • Sleep Detection Update (0x57) and Dynamic End-of-Charge (0x59) are decoded to typed events but log-only — no public SharedFlow until a consumer exists. Keeps the reference catalog complete without adding unused public surface.
  • EqBands renamed to PmeConfig — the Wireshark dissector calls opcode 0x53 "PME Config" and all captured Pro 2 USB-C / Pro 3 payloads are all-zero. The debug-only EQ bar chart auto-hides on all-zero data rather than showing a flat chart that looks like a configured EQ.
  • AapFramer intentionally left untouched. Its payload-length semantics don't match real captures, but it's not in the hot path (production uses L2CAP SEQPACKET which delivers one frame per read()), and fixing it is a separate concern from this PR's scope.

Review checklist

  • ./gradlew :app:testFossDebugUnitTest passes — 1,020 tests, includes per-model golden capture tests (Pro 1 / Pro 2 USB-C / Pro 3) and a cache-migration test for the buildNumber → marketingVersion rename.
  • Confirm a device with a pre-rename cached state upgrades without dropping the marketing version value (CachedDeviceStateMigrationTest).
  • On-device smoke (Pro 2 USB-C or Pro 3): ConnectResponse OK logs with features hex; DeviceInfoDump #173 reports segments=15; HID: service info tokens=[…] instead of HID: unknown.
  • Case Info probe only sent on AirPods Pro 3 (grep SEND CaseInfoProbe in logcat — should be absent on other models).
  • EqBands UI section stays hidden when PME Config is all-zero (debug-only card).

Adopts the Wireshark AAP dissector (pabloaul/apple-wireshark) as a third reference source alongside LibrePods and MagicPodsCore. Catalogues every known message type and control/setting ID, corrects DeviceInfo field labels, and adds sealed AapPacket hierarchy with Connect Response parsing. Case Info probe (Pro 3), Sleep event, and Dynamic End of Charge decoders are in place for future use.
@d4rken d4rken added enhancement Add a new feature of improve an existing feature coms/AAP Uses Apples AirPod Protocol. Requires Android ROM with fixed L2CAP support on the Bluetooth sockets. labels Apr 24, 2026
@d4rken d4rken merged commit a701fdf into main Apr 24, 2026
10 checks passed
@d4rken d4rken deleted the feat/aap-protocol-catalog branch April 24, 2026 06:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

coms/AAP Uses Apples AirPod Protocol. Requires Android ROM with fixed L2CAP support on the Bluetooth sockets. enhancement Add a new feature of improve an existing feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant