Files
librepods/AAP Definitions.md
Kavish Devar 8eb6eba049 android: multidevice capabilites and accessiblity features (and "liquid glass") (#202)
many thanks to @rithvikvibhu for help with the hearing aids feature

adds:
hearing aid
two-device connection
new UI
transparency mode customization

commits:
* android: add accessibility stuff

adds option for customizing transparency mode, amplification, tone, etc.

* docs: update transparency mode format

* android: don't 'start' service every time MainActivity is launched

* android: add basic multidevice capabilities

use at your own risk, may or may not work

* android: clean up a bit of AI gen'd code

* android: clean up main service and remove minimum API on head gestures

* android: clean up a lot of stuff

* android: implement the accessiblity settings page

* android: add EQ settings for phone and media

* android: add toggle for DID hook

* docs: add 'has ownership' control cmd

* android: fix balance NaN error when amplification L/R is both zero

* android: bring back some accessiblity settings and add listeners for all config

* android: add header to ATTManager

* android: use device name sent by the connected device in island

* android: fix track color in tone volume

* android: remove unused composable

* android: update eq sliders style

* android: fix text color in selectors

* android: add delay before starting head tracking again

* android: add a few options

ik not the right branch/pr but, eh, i am not merging this hook until i test further, and if i don't merge, conflicts, a lot of 'em

* android: a small ui fix

* docs: a few more control cmds

* android: add microphone setting

also, un-hardcoded strings, and updated text sizes

* android: improve dropdowns

ai generated

* android: move attmanager to service to avoid trying to connect multiple times

* android: add ui for hearing stuff

mostly copied from the transparency settings, which are now updated to match ios <26 ui

* android: add media assist options in hearing aid

ui only

* android: add hearing aid adjustments

* android: liquidglass sliders

* android: improve liquid glass sliders

* android: little more liquid glass

* android: fix hearing aid parsing

* android: remove customdeviceactivity from manifest

* android: remove unused strings

* android: small ui tweaks

* android: a very big commit

refactoring ui, mostly

* android: move padding to StyledScaffold's content

because haze needs it

* android: revert accidental capitalization on toggle label

* android: update usages for toggle

* android: liquidglass, maybe?

the switch and icon button took quite a while. i forgot the order of modifiers matters!

* remove bleonly mode, use CAPod instead

* remove bleonly mode, use CAPod instead

* android: fix switch styling

* android: remove fade from transition

* android: add A16's new bluetooth identifier for log collection

just why...

* android: fix crash in head gestures screen

* android: show head gestures status in the navigation button

* android: don't crash if att not available

* android: use lazycolumn in airpods settings for better performance and navigation transitions

* android: fix text color in troubshooting button and pressandhold settings

* android: bring back original confirmation dialog

too lazy to fix/implement properly the glassy one

* android: prevent hearing aid turning off itself

* android: hide media assist, not implemented

* docs: update README with new features

* docs: add demo video

* docs: add new screenshots for android

* docs: update demo video position

* docs: app3 compatibility

* docs: new control cmds '25 (again)

* docs: change section title in control cmd doc

Updated section title from 'Control Commands' to 'Identifiers and details'.

* android: ui tweaks

* android: update styled slider thumb

* android: add accessiblity service for camera control

* android: add camera control, finally

i got too lazy to find out how to listen to app openings earlier, wasn't too hard

* android: add option to change camera app id

* android: not use relative paths for executing commands

i hope it's the same across all skins

* android: fix transparency and noise cancellation flags

huh... was it always like this?

* android: revert to using relative paths for su

compatibility issues with magisk

* android: bump version

* android: don't crash if self MAC is not available

* android: remove unused LOCAL_ADDRESS permission

* android: add opensource licenses

should've done this a long time ago!

* android: move navigation button to activity level

* android: update animation time on switch tap

* android: implement setting hearing test results

* android: update title in hearing test screen

* docs: add screenshot for hearing test

* android: fix haze for dialog when enabling hearing aid

* android: parse device info

* android: add support for various models

still need to update images or find a way to fetch from apple's cdn

* android: fix a2dp connection

* android: remove stray eq config in accessibility settings

* android: improve connection handling

* android: add a (very important) support dialog

to not be invasive, this only shows up once, and never again.

* docs: add note for DID hook on android
2025-10-26 21:03:49 +05:30

16 KiB
Raw Permalink Blame History

AAP Definitions (As per AirPods Pro 2 (USB-C) Firmware 7A305)

AAP runs on top of L2CAP, with a PSM of 0x1001 or 4097.

Handshake

This packet is necessary to establish a connection with the AirPods. Or else, the AirPods will not respond to any packets.

00 00 04 00 01 00 02 00 00 00 00 00 00 00 00 00

Setting specific features for AirPods Pro 2

may work for airpods 4 anc also, not tested

Since apple likes to wall off some features behind specific OS versions, and apple silicon devices, some packets are necessary to enable these features.

I captured the following packet only accidentally, because Apple being Apple decided to hide this and the handshake from packetlogger, but sometimes it shows up.

Captured using PacketLogger on an Intel Mac running macOS Sequoia 15.0.1

04 00 04 00 4d 00 ff 00 00 00 00 00 00 00

This packet enables conversational awareness when playing audio. (CA works without this packet only when no audio is playing)

It also enables the Adaptive Transparency feature. (We can set Adaptive Transparency, but it doesn't respond with the same packet See Noise Cancellation)

Requesting notifications

This packet is necessary to receive notifications from the AirPods like ear detection, noise control mode, conversational awareness, battery status, etc.

Captured using PacketLogger on an Intel Mac running macOS Sequoia 15.0.1

04 00 04 00 0F 00 FF FF FE FF

This packet also works.

04 00 04 00 0F 00 FF FF FF FF

Notifications

Battery

AirPods occasionally send battery status packets. The packet format is as follows:

04 00 04 00 04 00 [battery count] ([component] 01 [level] [status] 01) times the battery count
Components Byte value
Case 08
Left 04
Right 02
Status Byte value
Unknown 00
Charging 01
Discharging 02
Disconnected 04

Example packet from AirPods Pro 2

04 00 04 00 04 00 03 02 01 64 02 01 04 01 63 01 01 08 01 11 02 01
Byte Interpretation
7th byte Battery Count - 3
8th byte Battery type - Left
9th byte Spacer, value = 0x01
10th byte Battery level 100%
11th byte Battery status - Discharging
12th byte Battery component end value = 0x01
13th byte Battery type - Right
14th byte Spacer, value = 0x01
15th byte Battery level 99%
16th byte Battery status - Charging
17th byte Battery component end value = 0x01
18th byte Battery type - Case
19th byte Spacer, value = 0x01
20th byte Battery level 17%
21st byte Battery status - Discharging
22nd byte Battery component end value = 0x01

Noise Control

The AirPods Pro 2 send noise control packets when the noise control mode is changed (either by a stem long press or by the connected device, see Changing noise control). The packet format is as follows:

04 00 04 00 09 00 0D [mode] 00 00 00
Noise Control Mode Byte value
Off 01
Noise Cancellation 02
Transparency 03
Adaptive Transparency 04

Ear Detection

AirPods send ear detection packets when the ear detection status changes. The packet format is as follows:

04 00 04 00 06 00 [primary pod] [secondary pod]

If primary is removed, mic will be changed and the secondary will be the new primary, so the primary will be the one in the ear, and the packet will be sent again.

Pod Status Byte value
In Ear 00
Out of Ear 01
In Case 02

Conversational Awareness

AirPods send conversational awareness packets when the person wearing them start speaking. The packet format is as follows:

04 00 04 00 4B 00 02 00 01 [level]
Level Byte Value Meaning
01/02 Person Started Speaking; greatly reduce volume
03 Person Stopped Speaking; increase volume back to normal
Intermediate values Intermediate volume levels
08/09 Normal Volume

Reading Conversational Awareness State

After requesting notifications, the AirPods send a packet indicating the current state of Conversational Awareness (CA). This packet is only sent once after notifications are requested, not when the CA state is changed.

The packet format is:

04 00 04 00 09 00 28 [status] 00 00 00
  • [status] is a single byte at offset 7 (zero-based), immediately after the header.
    • 0x01 — Conversational Awareness is enabled
    • 0x02 — Conversational Awareness is disabled
    • Any other value — Unknown/undetermined state

Example:

04 00 04 00 09 00 28 01 00 00 00

Here, 01 at the 8th byte (offset 7) means CA is enabled.

Metadata

This packet contains device information like name, model number, etc. The packet format is:

04 00 04 00 1d [strings...]

The strings are null-terminated UTF-8 strings in the following order:

  1. Bluetooth advertising name (varies in length)
  2. Model number
  3. Manufacturer
  4. Serial number
  5. Firmware version
  6. Firmware version 2 (the exact same as before??)
  7. Software version (1.0.0 why would we need it?)
  8. App identifier (com.apple.accessory.updater.app.71 what?)
  9. Serial number 1
  10. Serial number 2
  11. Unknown numeric value
  12. Encrypted data
  13. Additional encrypted data

Example packet:

040004001d0002d5000400416972506f64732050726f004133303438004170706c6520496e632e0051584e524848595850360036312e313836383034303030323030303030302e323731330036312e313836383034303030323030303030302e3237313300312e302e3000636f6d2e6170706c652e6163636573736f72792e757064617465722e6170702e3731004859394c5432454632364a59004833504c5748444a32364b3000363335373533360089312a6567a5400f84a3ca234947efd40b90d78436ae5946748d70273e66066a2589300035333935303630363400```

The packet contains device identification and version information followed by some encrypted data whose format is not known.

Writing to the AirPods

Changing Noise Control

We can send a packet to change the noise control mode. The packet format is as follows:

04 00 04 00 09 00 0D [mode] 00 00 00
Noise Control Mode Byte value
Off 01
Noise Cancellation 02
Transparency 03
Adaptive Transparency 04

The airpods will respond with the same packet after the mode has been changed.

But if your airpods support Adaptive Transparency, and you haven't sent that special packet to enable it, the airpods will respond with the same packet but with a different mode (like 0x02).

Renaming AirPods

We can send a packet to rename the AirPods. The packet format is as follows:

04 00 04 00 1A 00 01 [size] 00 [name]

Toggle case charging sounds

This feature is only for cases with a speaker, i.e. the AirPods Pro 2 and the new AirPods 4. Tested only on AirPods Pro 2

We can send a packet to toggle if sounds should be played when the case is connected to a charger. The packet format is as follows:

12 3A 00 01 00 08 [setting]
Byte Value Sound
00 On
01 Off

Toggle Conversational Awareness

This feature is only for AirPods Pro 2 and the new AirPods 4 with ANC. Tested only on AirPods Pro 2

We can send a packet to toggle Conversational Awareness. If enabled, the AirPods will switch to Transparency mode when the person wearing them starts speaking (and sends packet for notifying the device to reduce volume). The packet format is as follows:

04 00 04 00 09 00 28 [setting] 00 00 00
Byte Value C.A.
01 On
02 Off

Adaptive Audio Noise

This feature is only for AirPods Pro 2 and the new AirPods 4 with ANC. Tested only on AirPods Pro 2

The new firmware 7A305 for app2 has a new feature called Adaptive Audio Noise. This allows us to control how much noise is passed through the AirPods when the noise control mode is set to Adaptive. The packet format is as follows:

04 00 04 00 09 00 2E [level] 00 00 00

The level can be any value between 0 and 100, 0 to allow maximum noise (i.e. minimum noise filtering), and 100 to filter out more noise.

This feature is only effective when the noise control mode is set to Adaptive.

I find it quite funny how I have greater control over the noise control on the AirPods on non-Apple devices than on Apple devices, becuase on Apple Devices, there are just 3 options More Noise (0), Midway through (50), and Less Noise (100), but here I can set any value between 0 and 100.

Accessiblity Settings

Headphone Accomodation

04 00 04 00 53 00 84 00 02 02 [Phone] [Media]
[EQ1][EQ2][EQ3][EQ4][EQ5][EQ6][EQ7][EQ8]
duplicated thrice for some reason
Data Type Value range
Phone Decimal 1 (Enabled) or 2 (Disabled)
Media Decimal 1 (Enabled) or 2 (Disabled)
EQ Little Endian 0 to 100

Customize Transparency mode

12 18 00 [enabled]
<left bud>
[EQ1][EQ2][EQ3][EQ4][EQ5][EQ6][EQ7][EQ8]
[Amplification]
[Tone]
[Conversation Boost]
[Ambient Noise Reduction]
<repeat for right bud>

All values are formatted as IEEE 754 floats in little endian order.

Data Type Range
Enabled IEEE754 Float 0/1
EQ IEEE754 Float 0-100
Amplification IEEE754 Float 0-2
Tone IEEE754 Float 0-2
Conversation Boost IEEE754 Float 0/1
Ambient Noise Reduction IEEE754 Float 0-1
Ambient Noise Reduction IEEE754 Float 0-1

Important

Also send the Headphone Accomodation after this.

Configure Stem Long Press

I have noted all the packets sent to configure what the press and hold of the steam should do. The packets sent are specific to the current state. And are probably overwritten everytime the AirPods are connected to a new (apple) device that is not synced with icloud (i think)... So, for non-Apple device too, the configuration needs to be stored and overwritten everytime the AirPods are connected to the device. That is the only way to keep the configuration.

This is also the only way to control the configuration as the previous state needs to be known, and then the new state can be set.

The packets sent (based on the previous states) are as follows:

Toggling Adaptive

04 00 04 00 09 00 1A 0B 00 00 00 - Turns on Adaptive from O and ANC
04 00 04 00 09 00 1A 0D 00 00 00 - Turns on Adaptive from O and T
04 00 04 00 09 00 1A 0E 00 00 00 - Turns on Adaptive from T and ANC
04 00 04 00 09 00 1A 0F 00 00 00 - Turns on Adaptive from O, T, ANC

04 00 04 00 09 00 1A 03 00 00 00 - Turns off Adaptive from O and ANC (and Adaptive)
04 00 04 00 09 00 1A 05 00 00 00 - Turns off Adaptive from O and T (and Adaptive)
04 00 04 00 09 00 1A 06 00 00 00 - Turns off Adaptive from T and ANC (and Adaptive)
04 00 04 00 09 00 1A 07 00 00 00 - Turns off Adaptive from O, T, ANC (and Adaptive)

Toggling Transparency

04 00 04 00 09 00 1A 07 00 00 00 - Turns on Transparency from O and ANC
04 00 04 00 09 00 1A 0D 00 00 00 - Turns on Transparency from O and Adaptive
04 00 04 00 09 00 1A 0E 00 00 00 - Turns on Transparency from Adaptive, and ANC
04 00 04 00 09 00 1A 0F 00 00 00 - Turns on Transparency from O and Adaptive and ANC

04 00 04 00 09 00 1A 03 00 00 00 - Turns off Transparency from O and ANC (and Transparency)
04 00 04 00 09 00 1A 09 00 00 00 - Turns off Transparency from O and Adaptive (and Transparency)
04 00 04 00 09 00 1A 0A 00 00 00 - Turns off Transparency from Adaptive, and ANC (and Transparency)
04 00 04 00 09 00 1A 0B 00 00 00 - Turns off Transparency from O and Adaptive and ANC (and Transparency)

Toggling ANC

04 00 04 00 09 00 1A 07 00 00 00 - Turns on ANC from O, and Transparency
04 00 04 00 09 00 1A 0B 00 00 00 - Turns on ANC from O, and Adaptive
04 00 04 00 09 00 1A 0E 00 00 00 - Turns on ANC from Adaptive, and Transparency
04 00 04 00 09 00 1A 0F 00 00 00 - Turns on ANC from O and Adaptive and Transparency

04 00 04 00 09 00 1A 05 00 00 00 - Turns off ANC from O and Transparency (and ANC)
04 00 04 00 09 00 1A 09 00 00 00 - Turns off ANC from O and Adaptive (and ANC)
04 00 04 00 09 00 1A 0C 00 00 00 - Turns off ANC from Adaptive, and Transparency (and ANC)
04 00 04 00 09 00 1A 0D 00 00 00 - Turns off ANC from O and Adaptive and Transparency (and ANC)

Toggling O

04 00 04 00 09 00 1A 07 00 00 00 - Turns on O from Transparency, and ANC
04 00 04 00 09 00 1A 0B 00 00 00 - Turns on O from Adaptive, and ANC
04 00 04 00 09 00 1A 0D 00 00 00 - Turns on O from Transparency, and Adaptive
04 00 04 00 09 00 1A 0F 00 00 00 - Turns on O from Transparency, and Adaptive, and ANC

04 00 04 00 09 00 1A 06 00 00 00 - Turns off O from Transparency, and ANC (and O)
04 00 04 00 09 00 1A 0A 00 00 00 - Turns off O from Adaptive, and ANC (and O)
04 00 04 00 09 00 1A 0C 00 00 00 - Turns off O from Transparency, and Adaptive (and O)
04 00 04 00 09 00 1A 0E 00 00 00 - Turns off O from Transparency, and Adaptive, and ANC (and O)

i do hate apple for not hardcoding these, like there are literally only 4^2 - {\binom{4}{1}} - $\binom{4}{2}$

Head Tracking

Start Tracking

This packet initiates head tracking. When sent, the AirPods begin streaming head tracking data (e.g. orientation and acceleration) for live plotting and analysis.

04 00 04 00 17 00 00 00 10 00 10 00 08 A1 02 42 0B 08 0E 10 02 1A 05 01 40 9C 00 00

Stop Tracking

This packet stops the head tracking data stream.

04 00 04 00 17 00 00 00 10 00 11 00 08 7E 10 02 42 0B 08 4E 10 02 1A 05 01 00 00 00 00

Received Head Tracking Sensor Data

Once tracking is active, the AirPods stream sensor packets with the following common structure:

Field Offset Length (bytes)
orientation 1 43 2
orientation 2 45 2
orientation 3 47 2
Horizontal Acceleration 51 2
Vertical Acceleration 53 2

LICENSE

LibrePods - AirPods liberated from Apples ecosystem Copyright (C) 2025 LibrePods contributors

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.