How can I instantiate a L2Cap socket in Android?
Asked Answered
G

1

7

I see that a Bluetooth socket can be of type TYPE_L2CAP, but the constructor for BluetoothSocket seems to be private and I can only find a method to instantiate a socket of type RFCOMM. How can I obtain and use a L2CAP socket? Is it actually supported by Android?

Greeting answered 31/7, 2018 at 13:36 Comment(0)
G
10

UPDATE 10/2019

Support is there! Enjoy the new API:

UPDATE 03/2019 With the first Android Q beta out there it looks like support is finally coming. https://developer.android.com/sdk/api_diff/q-beta1/changes.html

UPDATE 10/2018

Got info that L2CAP CoC will be supported starting from Android Q.

Unfortunately I'm unable to cite official sources for that.

OLD ANSWER

I'll post my own results, hoping to help others and maybe get hints if I am missing something.

TL;DR: Looks like I can't

No way to get a L2CAP CoC socket with public API

This page from the Android documentation claims that “LE Connection-Oriented Channels” (l2cap channels) are available since Android 8.0. In the Android API there is a BluetoothSocket class that can have type L2_CAP. But there is no way to get a BluetoothSocket instance with such type, due to the private visibility of the elements of that class. That’s it for the public Android java API: stuck.

So?

Digging under the surface of the public java API. The source code for the android version 8+ offers a way to get a l2cap socket that is evolving in time:

  • A createL2capSocket method in android 8 (source)
  • A createL2capCoCSocket method in android 9 (source)
  • A createL2capChannel in the master branch of the android project (future release I guess?) (source)

But in each released version this code is marked with the @hide annotation, which means that none of these methods is actually available for usage.

Before Android 9 hidden methods were at least accessible, on some devices, with java reflection. That’s how we were able to test on android 8, with awful results.

Since version 9 of the OS this is not possible anymore: the platform actively blocks attempts to improperly access APIs.

Moreover..

Beside the fact that the methods to instantiate a socket are not visible, there is evidence of the immaturity of the API also in the lack of methods to tune parameters of the l2cap connection.

Going native?

I don't have experience with NDK but I think it would be possible to write c code to exploit the same low level functions used by the hidden methods. Unless android is not blocking that kind of usage as well, which is likely. Also this usage would conflict with regular usages of the bluetooth stack on a phone: normal usages are normally all filtered by an Android service. Bypassing this service would lead to unpredictable consequences, and it’s not suitable for a productive app.

Wrap it up

Testing on Android 8.1 with reflection led to very unsatisfying results. The claim that “LE Connection-Oriented Channels” are available since Android 8.0 just can’t be confirmed, despite the efforts.

In newer versions, the hidden methods for creating l2cap channels are evolving, and recently the hide annotation was removed. It’s hard to tell what this means in terms of time before we can have a device in our hands that supports this API. it’s likely that it will take years before l2cap channels will be reliable and available to a significant share of the android users.

Greeting answered 10/10, 2018 at 7:33 Comment(11)
Could you tell a bit more about the "very unsatisfying results"? In what way did it not work as you expected?Electromotor
Transfer was slow, and connecting was unreliable. Hopefully I'll have some time to test with Q soonGreeting
I just tested in Android Q beta. Seems to work nice. I also tested on Oreo, but the transfer was slow because it only allows 1 credit at a time there, so max 1 packet per connection event. However there is a bug (in both versions) that if you send a packet of length 0 to Android, read(byte[]) reports the length of the packet to be -1 instead of 0.Electromotor
Maybe you can update my answer with your fresh results ;)Greeting
I've python script that creates the L2CAP server on Linux and I'm using BluetoothDevice.createInsecureL2capChannel(int) method to create a socket on the app side, but getting IOException. Does anyone have an idea about this?Desorb
@HarshvardhanTrivedi not really, what's the stacktrace?Greeting
I feel like I'm going crazy. I'm using BluetoothAdapter.listenUsingL2capChannel() to create a BluetoothServerSocket and I use BluetoothServerSocket.accept() in a seperate Thread to wait for incoming Connections. I share the server's PSM value with clients meant to connect. On client devices I run BluetoothDevice.createL2capChannel(SHARED_PSM_VALUE) and run connect() on the returned Socket. Anyway, the connect() method throws an IOException with not too many details (read failed, socket might...). Also, I've already tried the Insecure methods. Same result. Did you guys face something similar?Nessus
Sorry I haven't worked on this topic for a while.. I guess there could be many possible problems..The first that pops into my mind.. Are bluetooth and location enabled, and bluetooth and location permissions granted?Greeting
Thanks for your answer @Greeting ! Yep, they are enabled and granted respectively. I did pretty much everything the same way I've always done it, when I used RFCOMM sockets. However, it just doesn't work using L2CAP. It is so strange. Especially cause it works when I replace the L2CAP method calls by RFCOMM method calls.Nessus
@NicoFeulner did you ever figure this out with L2CAP? I'm seeing the same thing, namely that RFCOMM works but L2CAP doesn't. I'd like to avoid using RFCOMM because we need to support iOS down the road.Weltpolitik
I haven't tried it again ever since but I had the same intention of supporting both iOS and Android. I thought about writing a Flutter library when I was into this topic. As it was a private project I could easily suspend my efforts and started working on other projects. Would still be interesting to know if it can somehow work (maybe thanks to API updates in newer versions of Android, I haven't checked it). Anyway, I haven't found a solution...Nessus

© 2022 - 2024 — McMap. All rights reserved.