Skip to content

Add AirPods Max support - #767

Open
Spottq wants to merge 1 commit into
librepods-org:mainfrom
Spottq:feat/airpods-max
Open

Add AirPods Max support#767
Spottq wants to merge 1 commit into
librepods-org:mainfrom
Spottq:feat/airpods-max

Conversation

@Spottq

@Spottq Spottq commented Sep 4, 2026

Copy link
Copy Markdown

Closes #30.

The AirPods Max were recognised as ordinary AirPods. Their model number is absent from AirPodsModels, so getModelByModelNumber returns null, airpodsInstance stays null, and loadInstance() falls back to a hardcoded A3049 — an AirPods Pro 2 USB-C. That is why the Max show Pro artwork, a fictitious L/R/case battery and an Adaptive segment they do not have.

What this changes

Model identification. Registers the AirPods Max (A2096), AirPods Max USB-C (A3184) and AirPods Max 2 (A3454). AirPodsBase gains a form factor, a small icon and the BLE proximity-pairing ids that BLEManager already parsed but discarded, and AirPodsModels.resolveModel() falls back from the model number to the BLE id to the Bluetooth name, so an unknown Max is still not mistaken for earbuds.

Battery. isBatteryData required exactly 22 bytes, so the Max packet was rejected outright and no battery ever appeared. It is now parsed by the entry count in data[6], which matches the capture from this thread:

040004000400 01 01 01 64 02 01
             ^^ count=1
                ^^ component 0x01  ^^ level 0x64=100%  ^^ status 0x02

A single battery is mirrored onto both sides so the existing index-based callers of getBattery() keep working, and the UI collapses the two equal readings into one and drops the case.

Listening modes. The Apple-style segmented control hardcoded four buttons with index arithmetic branching on showOffListeningMode. It is now built from the list of supported modes, so Adaptive can be dropped without a second layer of conditionals. Adaptive is gated on Capability.ADAPTIVE_AUDIO, which also removes it from the AirPods Pro 1, which never had it. The QS tile cycle and the Quick Settings dialog skip it too.

Off Listening Mode toggle was gated on Capability.LOUD_SOUND_REDUCTION, so it disappeared on every model without that feature, the Max included. It is gated on LISTENING_MODE now, and falls back to the stored preference when the device does not report the control state.

System metadata. Over-ear models are reported as DEVICE_TYPE_DEFAULT rather than DEVICE_TYPE_UNTETHERED_HEADSET, and the untethered case/left/right icons and thresholds are skipped, so the system stops expecting three batteries.

Unrelated bug found on the way. PopupWindow and resToUri hardcoded "me.kavishdevar.librepods" in resource URIs. With any other application id the video URI resolves to a missing package, VideoView shows an error dialog from a non-activity context and the app dies with BadTokenException. Both now use packageName.

Testing

Hardware-tested on an AirPods Max USB-C (A3184, firmware 74.2256106001000000.83) on a Galaxy Z Fold 7, Android 16, rootless. Correct model name and number, a single 57% battery with no case, no Adaptive segment, the Off toggle present, and the Max silhouette in the status bar.

Main screen Settings

Status bar icon, traced from the product render so it stays crisp at 24dp:

Things worth your call

  • android/rewrite. This targets main, since that is what the RC codebase is. If the rewrite is meant to replace it, say so and I will port this across instead of you carrying it twice — @tobomobo asked the same question in Support for Airpods Max #30 and it is still open.
  • Overlap with @tobomobo's fork. They have an independent Max implementation covering much of the same ground plus Midnight colour detection from the BLE advert. I did not see it until after this was written. Happy to defer, or to merge the two — their colour detection and my packet-count parser and mode-list refactor are complementary.
  • A3454 is untested. The model number and the H2 capability set (Adaptive Audio, Conversation Awareness, Personalized Volume) come from Apple's specs and from Support for Airpods Max #30, not from hardware. It reuses the USB-C artwork, since Apple publishes no separate render yet. @tobomobo has one and could confirm.
  • Artwork is Apple product renders, placed in res-apple/ alongside the existing AirPods Pro renders, following that source set's convention. Swap them if you would rather not add more.

The AirPods Max were recognised as ordinary AirPods: their model number was
absent from the model table, so the lookup returned null and the UI fell back
to a hardcoded AirPods Pro 2. They also have a single battery and no Adaptive
listening mode, neither of which the app accounted for. Fixes librepods-org#30.

- Register the AirPods Max (A2096), AirPods Max USB-C (A3184) and AirPods Max 2
  (A3454) with real product artwork, so the icon follows the connected model
  everywhere: the device image, the notification, the QS tile and the popup.
- Give AirPodsBase a form factor, a small icon and the BLE proximity-pairing
  ids, and resolve the model from the model number, the BLE id or the Bluetooth
  name rather than only the first of those.
- Parse battery packets by their entry count instead of requiring the 22-byte
  three-battery layout, which the Max never send. Their packet is 12 bytes with
  a single component, as captured in librepods-org#30:

      040004000400 01 01 01 64 02 01

  The one battery is mirrored onto both sides so existing callers keep working,
  and the UI collapses it back into a single reading with no case.
- Hide the Adaptive mode on models without it, and build the listening-mode
  control from the supported modes instead of a fixed set of four. This also
  drops Adaptive from the AirPods Pro 1, which never had it.
- Show the Off Listening Mode toggle for every model with listening modes; it
  was gated on Loud Sound Reduction, which hid it on the Max.
- Report over-ear models to the system as a plain headset rather than an
  untethered one, so the system stops expecting three batteries.
- Use the real package name for resource URIs; the hardcoded one broke the
  connection popup whenever the application id differed.

Tested on an AirPods Max USB-C (A3184): correct model name and number, a single
battery reading, no Adaptive segment, and the Max icon in the status bar.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Wj2BSzNmCLkXpcS7YsttjZ
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.

Support for Airpods Max

2 participants