librepods icon indicating copy to clipboard operation
librepods copied to clipboard

L2CAP connection fails on A16 GrapheneOS

Open MihkelMK opened this issue 3 months ago • 12 comments

Commented on #188 as well, but creating a new issue as I have gathered some more potentially helpful info.

As zygisk does not work on rooted GrapheneOS, I can't (correct me if I'm wrong) use the Xposed framework. Thus I'm installing the btl2capfix module with Magisk.

I tried the module from #61 using the nightly build and got the same error mentioned in #188. I then tried investigating the module to see if it does what it has to. Android is not my expertise so my assumptions might be wrong but here are my findings (looking at the phones filesystem after module install and reboot):

  1. My phones bluetooth library is located at /apex/com.android.bt/lib64/libbluetooth_jni.so.
  2. post-data-fs.sh creates an overlay mount from /data/adb/modules_update/btl2capfix/apex/com.android.bt/lib64 to /apex/com.android.bt/lib64
    Contents of post-data-fs.sh:
    #!/system/bin/sh
    mount -t overlay overlay -o lowerdir=/apex/com.android.bt/lib64,upperdir=/data/adb/modules_update/btl2capfix/apex/com.android.bt/lib64,workdir=/data/adb/modules_update/btl2capfix/apex/com.android.bt/work /apex/com.android.bt/lib64
    
  3. /data/adb/modules_update/ does not exist, but /data/adb/modules/btl2capfix/ does
  4. With this info I tried patching the module to instead mount /data/adb/modules/btl2capfix/apex/com.android.bt/lib64 to /apex/.../lib64
  5. After installing the modified module I checked the sha512 hashes for both files and they didn't match + mount | grep overlay | grep bluetooth and mount | grep apex.*bt didn't return anything to indicate an overlay mount to /apex/.../lib64
  6. I then tried to instead create a bind mount, that did not work as well. Then I tried the bind mount, but in service.sh instead of post-data-fs.sh. This finally worked
    By worked I mean the sha512 hashes of /data/adb/modules/btl2capfix/apex/com.android.bt/lib64/libbluetooth_jni.so and /apex/com.android.bt/lib64/libbluetooth_jni.so matched. I'm not sure that's the correct way to measure success in this case, but there was a change.
  7. I copied the patched libbluetooth_jni.so file over to my computer to check if the patches were successful. I looked at the addresses dispalyed for both functions is the Magisk install log and both had a return at the start of the function - matching the hex in customize.sh
  8. Even with the now confirmed patched libbluetooth_jni.so bind mounted into /apex/.../lib64, the same error of L2CAP connection failing persisted.

I captured troubleshooting logs with the module mentioned in #61: airpods_log_1759102497486.txt And the modified version with bind mounting and matching file hashes: airpods_log_1759102773514.txt

I am aware that this is a massive edge case as Pixel phones with A16 seem to work when using the Xposed framework. I might be the only person trying to run Librepods on a Pixel6 running rooted GrapheneOS.

I am very thankful for your continued work on this project. Let me know if I can provide any additional information.

MihkelMK avatar Sep 29 '25 00:09 MihkelMK

Hi!

As zygisk does not work on rooted OxygenOS

Which OxygenOS version? I have used it with A14, not sure why A15 would break it.

Weirdly, Android seems to be using the patched library anyway without the bind mounts too, or the patched function is not called anyway. I couldn't find any logs saying this.

The problem is this. If you are able to use xposed, I can try to create another hook for this function and force the modes to be equal.

Also, are you on Graphene or OxygenOS?

kavishdevar avatar Sep 29 '25 05:09 kavishdevar

Late night mistake, I meant to write GrapheneOS. I edited the original issue to fix the typo.

Is it possible to patch l2c_fcr_adj_our_rsp_options by installing a Magisk module (like I did above) or is xposed necessary?

MihkelMK avatar Sep 29 '25 11:09 MihkelMK

Late night mistake, I meant to write GrapheneOS. I edited the original issue to fix the typo.

Got it. I don't know about GrapheneOS. Is there a reason why it doesn't work? quick google search just shows people suggesting not to do it, but not that it doesn't work.

Is it possible to patch l2c_fcr_adj_our_rsp_options by installing a Magisk module (like I did above) or is xposed necessary?

Sorry, I was looking at the wrong code. We'd have to patch this itself. I don't have much knowledge about that, though. And here, xposed will also be a bit difficult because it's part of a big function.

kavishdevar avatar Sep 29 '25 11:09 kavishdevar

I'm not that knowledgeable about root/GrapheneOS myself, but when I tried to enable Zygisk from Magisk settings it still said "reboot to apply settings" regardless of how many times I rebootes the phone. There might be a way to enable it, but I couldn't make it work.

The patch would just make it so that "Verify two sides are in compatible modes before continuing" if statement is alwats false? I could try to create that patch. Shouldn't be too difficult?

MihkelMK avatar Sep 29 '25 12:09 MihkelMK

I'm not that knowledgeable about root/GrapheneOS myself, but when I tried to enable Zygisk from Magisk settings it still said "reboot to apply settings" regardless of how many times I rebootes the phone. There might be a way to enable it, but I couldn't make it work.

You could try installing https://github.com/PerformanC/ReZygisk.

The patch would just make it so that "Verify two sides are in compatible modes before continuing" if statement is alwats false?

Yup, and bypass the disconnection. Actually, breaking that if-else chain before calling l2cu_send_peer_disc_req(p_ccb) is what we want.

kavishdevar avatar Sep 29 '25 12:09 kavishdevar

You could try installing https://github.com/PerformanC/ReZygisk.

ReZygisk installed successfully, but GrapheneOS didn't allow opening the LSPosed manager. It tried to use DCL from memory, which is restricted for preinstalled apps (Shell in this case). I didn't find any workaround for this.

Yup, and bypass the disconnection. Actually, breaking that if-else chain before calling l2cu_send_peer_disc_req(p_ccb) is what we want.

This worked somewhat. I had to manually locate the memory address of the comparison. Don't know if/how to make it automatic like the 2 function patches currently in btl2capfix.

I set the address (0x009308d4 for me) to 1f2003d5 (nop) and the L2CAP error disappeared. Had no luck with the overlay mount, only my modified bind mount approach.

To recap: I successfully got rid of the "L2CAP connection fails" error message by switching to a bind mount and patching the if (p_ccb->our_cfg.fcr.mode != p_ccb->peer_cfg.fcr.mode) comparison to a nop instruction.

What works:

  • Battery indicator
  • ANC/Transparency mode switching and indicator

What doesn't seem to work:

  • Head tracking (both the default and alternative packets)
  • Switching the longpress actions
  • Media play/pause with inear detection
  • Disabling Volume Control (works regardless of switch state in UI)
  • "Disconnect AirPods when not wearing" - music still plays with AirPods on table

Based on this: maybe the error isn't displayed, but the connection is still not successful? Or maybe I have to set IRK and ENV_KEY manually?

Thiis is the customize.sh that worked: customize_bind.sh

MihkelMK avatar Sep 29 '25 21:09 MihkelMK

ReZygisk installed successfully, but GrapheneOS didn't allow opening the LSPosed manager. It tried to use DCL from memory, which is restricted for preinstalled apps (Shell in this case). I didn't find any workaround for this.

Okay, thanks for trying!


Only those two things working is weird. Do these work only after some time of the connection or stay working till the airpods are connected?

Or maybe I have to set IRK and ENV_KEY manually?

Nope, you shouldn't need to- that's just for BLE (scanning when AirPods are nearby. And anyway, if you can change the noise control modes, the app has already probably fetched the keys.


Don't know if/how to make it automatic like the 2 function patches currently in btl2capfix.

would probably have to find out based on the log string, and work up to the nearest if comparison. Too much effort for now.

kavishdevar avatar Sep 30 '25 04:09 kavishdevar

Could you please collect the logs yourself with adb for the bluetooth process and the app's process? Because the connection is apparently successful, the troubleshooter might not be exactly helpful.

kavishdevar avatar Sep 30 '25 04:09 kavishdevar

Happy to report that when I installed 0.20.0-alpha some things started working. Not sure if it was a case of reinstalling or rebooting or the update itself.

Inear detection worked great, head tracking didn't and iirc some other things as well. Sometimes there was also a weird moment where the airpods disconnected and reconnected a couple of times before getting a stable connection. They also seemed to disconnect right after media stopped playing, then reconnect and pause the media when I pressed play on something.

An OTA update I just did seems to have broken the magisk patch - airpods keep disconnecting and even when they do connect, LibrePods doesn't see them.

I'll provide the app logs right now: _data_user_0_me.kavishdevar.librepods_files_logs_airpods_log_20251004_213842.txt

I'll try checking if the memory address of that if statement changed and collect adb logs tomorrow. What command should I use for collecting logs via adb?

MihkelMK avatar Oct 04 '25 18:10 MihkelMK

Happy to report that when I installed 0.20.0-alpha some things started working. Not sure if it was a case of reinstalling or rebooting or the update itself.

Great to hear that! Haven't changed any connection part. Probably rebooting or something might've fixed.

head tracking didn't and iirc some other things as well.

Could you please try enabling alternate head tracking packets from app settings?

They also seemed to disconnect right after media stopped playing, then reconnect and pause the media when I pressed play on something.

That's weird, this should only happen when there is another device connected too, and that takes ownership of the connection. And this needs a different hook, which you do not have. I'll try to reproduce.

An OTA update I just did seems to have broken the magisk patch

Yeah, you will have to repatch the library. Does your Bluetooth work after the OTA? If the library was changed, it usually should just stop working entirely.

For logs (after you have repatched the library):

dumpsys package me.kavishdevar.librepods | grep -m 1 "uid="
dumpsys package com.google.android.bluetooth | grep -m 1 "uid="

And, then logcat --uid=uid1,uid2.

kavishdevar avatar Oct 05 '25 02:10 kavishdevar

@kavishdevar is it possible if someone can test out a change on bluetooth stack,

https://github.com/danascape/android_packages_modules_Bluetooth/commit/f369e7813ed86a1439823bfb43cd8bae1492cfb4

^ Based on the comments in the issuetracker, Ill reframe commit message later on

I can help out with AOSP-based patchsets for airpods support, and until next month I wont have access to airpods to try it out myself, just leaving this here, incase someone tries it out.

Thanks

danascape avatar Nov 20 '25 08:11 danascape

Thank you so much, @danascape! I don't have sources downloaded. I'll test with a GSI when I have and get back to you.

kavishdevar avatar Nov 20 '25 20:11 kavishdevar