Skip to content

Conversation

@kavishdevar
Copy link
Owner

@kavishdevar kavishdevar commented Sep 10, 2025

The app now supports

  • Accessibility Features
    • customizing transparency mode
    • enabling loud sound reduction
    • eq settings for transparency mode
    • eq settings for media and phone
    • hearing aid customization (Help needed from users in supported regions 🙏)
    • implement adding hearing test results and display active audiogram data (hearing loss data for 8 frequencies)
  • Multi-device connectivity
    • connect your AirPods with up to two devices simultaneously (more devices can be connected in Apple's ecosystem probably by iCloud)
    • shows popups on phone when other device takes control
    • asks the other device to show popups the same way iDevices do (will show up as an iphone because unfortunately apple's OSes don't actually pick up the names other than Mac, iPad, and iPhone specifically.
    • replicates most of the behavior for multi-device pairing such as hijack requests, reverse connection on banner tap, etc.
    • quirk: connect your apple device before connecting your non-apple device.

(Accessibility features needs writing a ATTManager because I'd love to have more direct control over the connection, WIP) done

other chores (not specific to the PR):

  • remove READ_LOCAL_ADDRESS permission
  • figure out where MODIFY_PHONE_STATE is used
  • automatically switch to using alternate head tracking packets

How?

After hours of digging, I finally found that these things require the vendorId of the Device Identification Profile to be set to Apple's.

  • On Linux, this can be easily done by editing the /etc/bluetooth/main.conf file and setting the DeviceID parameter.
  • And for Android, we again fall back to xposed, where the function that adds this DID record to SDP (Service Display Protocol is hijacked). the hooks need to be reset to find the offset for the said function (and no, i'm not going for heuristics any time soon, this is just much more reliable, one-time setup).
    • add some indication for new features/updates for existing users so that they don't need to turn to the README and/or the release notes every update

oh, and i tried android studio's code inspection and cleaned up the code a little

@kavishdevar kavishdevar self-assigned this Sep 10, 2025
@kavishdevar kavishdevar added enhancement New feature or request help wanted Extra attention is needed android Android app related issues labels Sep 10, 2025
@kavishdevar

This comment was marked as outdated.

@Leclowndu93150
Copy link

the work done here is amazing, i hope the pros 3 keep the same packet format

@kavishdevar
Copy link
Owner Author

the work done here is amazing

thanks!

i hope the pros 3 keep the same packet format

Hopefully, Apple will not go all the way to change their entire protocol that they've been using over so many years

Have you been able to try this build out, @Leclowndu93150? The accessibility fetures and stuff?

I'm waiting for a few more days just to test it out myself before merging it.

@Leclowndu93150
Copy link

Leclowndu93150 commented Sep 17, 2025

Have you been able to try this build out, @Leclowndu93150? The accessibility fetures and stuff?

not yet not yet ! i was looking for this kind of apps cuz i'm getting my first ever airpods (pros 3) on the 19th, i never had an apple product before so i'll see how it goes. but i'd be happy to help !

@Leclowndu93150
Copy link

Leclowndu93150 commented Sep 19, 2025

pfft the store is late, tomorow for sure

@kavishdevar kavishdevar closed this Oct 2, 2025
@kavishdevar kavishdevar deleted the multi-device-and-accessibility branch October 2, 2025 05:22
@kavishdevar kavishdevar restored the multi-device-and-accessibility branch October 2, 2025 05:24
@kavishdevar kavishdevar reopened this Oct 2, 2025
@randshell
Copy link

I've realised that the latest nightly is not packaged as a magisk module, so I've used the zip from v0.1.0-rc.4 and replaced the APK binary with the nightly.

For starters, there's bootloop when flashing the magisk module on my A16 device:

java.lang.IllegalStateException: Signature|privileged permissions not in privileged permission allowlist: {me.kavishdevar.librep
ods (/system/priv-app/LibrePods): android.permission.LOCAL_MAC_ADDRESS}

So I've changed the XML permission as follows which fixes it:

<?xml version="1.0" encoding="utf-8"?>
<permissions>
    <privapp-permissions package="me.kavishdevar.librepods">
        <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
        <permission name="android.permission.LOCAL_MAC_ADDRESS"/>
        <permission name="android.permission.READ_PHONE_STATE"/>
    </privapp-permissions>
</permissions>

The READ_PHONE_STATE doesn't bootloop when it's missing, but it still creates some errors in the logcat. I didn't find that it's a privileged permission, so perhaps it's missing the permission prompt at runtime, rather than editing the XML file?

Ah, I don't think that's the Media Assist feature (correct me if i'm wrong). The amplification and balance would should up under Adjustments once you enable Hearing Aid. What you're seeing is from the hearing test (I can't be bothered to be deal with accurate testing using AirPods, instead I'll just provide adding audiogram results and using it for hearing aid like Apple already provides).

I think this should be correct afaik.

From the screenshots I got from someone (I am in a region where the feature is not supported), Here's a list of things I know:

hearing test results: either from the hearing test that's taken using AirPods, or adding it manually via the Health app.
hearing aid:
hearing test results: the hearing loss data (raw dBHL values) from the hearing test. this is not customizable.
adjustments: once hearing aid is enabled, there are a few things that are customizable, like the amplification and balance (which do not alter the loss data in any way, these are different fields in the data sent), tone ambient noise etc..
if enabled, the "customize transparency mode" and "headphone accommodation" (EQ) are disabled.

I think this is also correct.

I've put up a very quickly made demo video in the README. Currently the LibrePods presents itself as "Android" which is unfortunately not used by iOS/macOS. It just checks for "Mac" or "iPad" and defaults to iPhone. I guess that iOS doesn't show the UI unless it's specifically Mac/iPad.

I've enabled the iPhone feature. Anyway, what I meant is that LibrePods doesn't show the popup when the control goes to the iPhone. The iPhone correctly shows the popup when the control goes to LibrePods and Android. This was before. Now with the nightly and with the correct permissions set by the root module, I see the popup of LibrePods. However, I'm experiencing a new issue. The switch happens Android -> iPhone, but then it doesn't come back iPhone -> Android anymore. I can still change the settings on LibrePods, e.g., enable noise cancellation, but there's no audio. It's worth noting that the system bluetooth widget shows no connected device, while LibrePods shows in-app the AirPods but does send a notification for disconnected AirPods.

Lastly, I can enable now Hearing Aid without crashing, but the settings don't work at all, for example amplification. When I switch to iPhone I can see that hearing aid was enabled, so something did happen (I couldn't tell the difference just by listening, perhaps cause it was on default amplification settings).

Recap:

  1. needed new xml privapp perms
  2. seamless transition works Android to iPhone but not the other way around
  3. Hearing Aid settings, e.g., Amplification, don't work.
  4. I experience random disconnects, perhaps related to point 2?

I haven't retested the transparency and EQs.

@randshell
Copy link

I'll attach a logcat here to help narrow down other remaining issues.

logcat_2025-10-04_15-20-08.txt


A few things to note:

  • Caused by: java.lang.SecurityException: Neither user 10050 nor current process has android.permission.MODIFY_PHONE_STATE I didn't see this one before 🤔
  • Received HEADTRACKING packet too short: for me the "Use alternate packets for headtracking" works, which I forgot to enable after reinstalling LibrePods. Perhaps in the future the app could listen for packets for a while and if it doesn't receive any data it could automatically try the alternate packets and save this setting for next time AirPods are connected? Anyway, this is beyond the scope of this MR.

@kavishdevar
Copy link
Owner Author

kavishdevar commented Oct 6, 2025

For starters, there's bootloop when flashing the magisk module on my A16 device:

I accidentally kept that READ_LOCAL_ADDRESS in the manifest, I had to remove it and use something else to get the MAC because not everyone has the app installed as a system app. (especially because I don't give the module in the release). Removing that permission. Apologies for the inconvenience!

I can still change the settings on LibrePods, e.g., enable noise cancellation, but there's no audio. It's worth noting that the system bluetooth widget shows no connected device, while LibrePods shows in-app the AirPods but does send a notification for disconnected AirPods.

I added disconnecting the audio source when other device takes over and reconnect when media starts playing. Else, when other device wants to start playing, AirPods pause the media for some reason. So when a request to lose ownership is received, the app had to disconnect A2DP. I'll add this as an option instead of forcing it.

MODIFY_PHONE_STATE permission:

Not sure at this point where that was needed, but I'll have a look and clean it up.

Hearing aid stuff:

Did you have any hearing test active? If a hearing test hasn't been sent to the AirPods, the app just defaults to zero gains for all frequency (I just copy all the audiogram data that's on the AirPods because of the reason I mentioned previously- I haven't implemented actual hearing tests, just "adjustments").

Since your iPhone did show it enabled, probably the adjustments are also updated, unless something went wrong.

I experience random disconnects

Is that when any audio is playing from Android? I have had this issue where if another device is playing audio for some time, then AirPods just disconnect the Android device, but not if it's actively playing audio or something. Don't know how to fix this, maybe it's cloud-based.

Oh, and yeah- I'll implement automatically switching to using alternate head tracking packets on this branch itself, not a major change. I haven't done it yet because that opcode isn't just for the head positioning information, so probably need to figure out another way than just length.

@randshell
Copy link

randshell commented Oct 14, 2025

Perhaps a little unrelated: I've recently bumped into a payed software for controlling AirPods at https://magicpods.app/.

I doubt that someone else took the effort to reverse engineer the protocol from scratch when you already did plenty of work, so I suspect it could be based on LibrePods. I don't have the time to check it better, so I'm sharing for you to know.

Edit. One of the public repos seems to be at https://github.com/steam3d/MagicPodsCore/.

@kavishdevar
Copy link
Owner Author

Perhaps a little unrelated: I've recently bumped into a payed software for controlling AirPods at https://magicpods.app/.

I doubt that someone else took the effort to reverse engineer the protocol from scratch when you already did plenty of work, so I suspect it could be based on LibrePods. I don't have the time to check it better, so I'm sharing for you to know.

Thanks for sharing-- I have actually shared a few things (conversational awareness, adaptive transparency, encrypted LE advertisments which I have also shared with CAPod’s developer because CAPod has a greater number of users and it was definitely an improvement for non-rooted devices) with steam3d, the developer of MagicPods. And they

Though I have written the entire documentation myself and reverse engineered the protocol myself, MagicPods had a few features before LibrePods even existed :)

@randshell
Copy link

Apologies for the inconvenience!

Not at all! 🙂

Did you have any hearing test active? If a hearing test hasn't been sent to the AirPods, the app just defaults to zero gains for all frequency (I just copy all the audiogram data that's on the AirPods because of the reason I mentioned previously- I haven't implemented actual hearing tests, just "adjustments").

Yes, I did have a hearing test done. Not sure what happened then.

Is that when any audio is playing from Android? I have had this issue where if another device is playing audio for some time, then AirPods just disconnect the Android device, but not if it's actively playing audio or something. Don't know how to fix this, maybe it's cloud-based.

I don't remember if it's also when playing. 🤔

[...] Though I have written the entire documentation myself and reverse engineered the protocol myself, MagicPods had a few features before LibrePods even existed :)

Thanks! I was curious about it.


I don't think I'll be able to test this MR anytime soon, so I hope I could be of help so far.
Thanks again for all your work!

@kavishdevar
Copy link
Owner Author

I don't think I'll be able to test this MR anytime soon, so I hope I could be of help so far.

You've been great help-- thank you very much!

I think I am done with the original goal of this PR now that I have adding hearing test results. Going to clean up the patch/hook mess and the root modules and going to merge it.

@kavishdevar kavishdevar marked this pull request as ready for review October 26, 2025 15:23
@kavishdevar kavishdevar merged commit 8eb6eba into main Oct 26, 2025
6 of 7 checks passed
kavishdevar added a commit that referenced this pull request Nov 10, 2025
…id 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

android Android app related issues enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants