Connecting to a bluetooth HID device (mouse) using L2CAP - android

I'm tring to find a way to connect to a HID bevice (mouse) using L2CAP, this for a android app. but i'm getting error when accepting the connection. I'm using reflection to create the socket. but some thing is wrong with this.
can some one please direct me to a example code for android that connect to a HID device using L2CAP this way, but without rooting.

What is your Android device and Android version?
If it's Android 4.2, they're now using Broadcom as I understood and so we're only able to create SDP connection.
I'm having the same problem while making a bluetooth connection between my Nexus 7 (Android 4.2.2 with CyanogenMod ROM 10) and a Wiimote. This is an HID device so I need to use L2CAP. Last versions of Android were able to create this connection (we can figure out just by looking at the market). If you search an application to handle this on the market, you'll see by looking at the description that all devices with Android version 4.0+ are not supported.
I just found this post few minutes ago which could help you: stackoverflow.com/a/7838587/1772805
Let me know if you solve this. I'll keep you in touch if I found anything.
EDIT #1: I tried the solution on the link above. I changed it to use a different constructor like this:
private static final int TYPE_RFCOMM = 1;
private static final int TYPE_SCO = 2;
private static final int TYPE_L2CAP = 3;
/**
* Create a BluetoothSocket using L2CAP protocol
* Useful for HID Bluetooth devices
* #param BluetoothDevice
* #return BluetoothSocket
*/
private static BluetoothSocket createL2CAPBluetoothSocket(BluetoothDevice device){
int type = TYPE_L2CAP; // L2CAP protocol
int fd = -1; // Create a new socket
boolean auth = false; // No authentication
boolean encrypt = false; // Not encrypted
int port = 0; // port to use (useless if UUID is given)
ParcelUuid uuid = new ParcelUuid(wiimoteUuid); // Bluetooth UUID service
try {
Constructor<BluetoothSocket> constructor = BluetoothSocket.class.getDeclaredConstructor(
int.class, int.class, boolean.class, boolean.class,
BluetoothDevice.class, int.class, ParcelUuid.class);
constructor.setAccessible(true);
BluetoothSocket clientSocket = (BluetoothSocket) constructor.newInstance(
type, fd, auth, encrypt, device, port, uuid);
return clientSocket;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
I succeeded to create the socket but when I call the method connect(), I get this error: bt l2cap socket type not supported, type:3. This log is a very bad new for me because I found this thread which says Android 4.2 does not support L2CAP (or just disabled by Google..).
Because my device is rooted with CyanogenMod 10, the feature will maybe come back on a new release. I hope..
EDIT #2: Here's a link pointing on the C file containing the reason of the problem: btif_sock.c. If anyone knows if it's possible re-write this file or how to add the L2CAP feature to Android with an external C library. I'm afraid it's not a simple task.

Related

Xamarin bluetooth in Android connection fails

I'm trying to establish a bluetoothconnection to another phoner. Later on, it'll be a board with a HC5 module, but for debugging I'm just using a phone.
The problem is, that connect fails and throws an IO Exception:
" read failed, socket might closed or timeout, read ret: -1"
A quick google-search shows a lot having this problem. The only way I can see it solved, is by using a method not public in the API
Method m = mmDevice.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
mmSocket = (BluetoothSocket) m.invoke(mmDevice, 1);
The problem just is, that createRfcommSocket has been removed, and the result from getMethod will be null.
My code is from the example: https://github.com/xamarin/monodroid-samples/tree/master/BluetoothChat which for connecting is:
public ConnectThread(BluetoothDevice device, BluetoothChatService service)
{
UUID MY_UUID = UUID.FromString("00001101-0000-1000-8000-00805f9b34fb");
mmDevice = device;
_service = service;
BluetoothSocket tmp = null;
// Get a BluetoothSocket for a connection with the
// given BluetoothDevice
try
{
if ((int)Android.OS.Build.VERSION.SdkInt >= 10) // Gingerbread 2.3.3 2.3.4
tmp = device.CreateInsecureRfcommSocketToServiceRecord(MY_UUID);
else
tmp = device.CreateRfcommSocketToServiceRecord(MY_UUID);
}
catch (Java.IO.IOException e)
{
Log.Error(TAG, "create() failed", e);
}
mmSocket = tmp;
}
Since the 'original hack' doesn't work, and I haven't found any other solution, I'm hoping somebody here knows how to fix this.
Best regards!
This is the issue I and others in my company have come across when working with Bluetooth devices from Android phones. It is well described on SO here: Service Discovery Failed Exception Using Bluetooth On Android
The method you describe as being removed is, in fact, still operational. And it is the one we have successfully used. We attempt connection using the
tmp = device.CreateRfcommSocketToServiceRecord(MY_UUID);
method you show above.
Use a try...catch block around tmp.Connect() (your code does not show the Connect call)
In the catch block "re-create" the BluetoothSocket using the createRfcommSocket method. I used a small method to do this:
private BluetoothSocket CreateRfcommSocket(BluetoothDevice bTdevice)
{ // This is an "undocumented" call that is needed to (mostly) avoid a Bluetooth Connection error
// introduced in Android v4.2 and higher. It is used as a "fallback" connection.
// Full paths version of code!
//Java.Lang.Reflect.Method mi = device.Class.GetMethod("createRfcommSocket", new Java.Lang.Class[] { Java.Lang.Integer.Type });
//_bluetoothSocket = (BluetoothSocket)mi.Invoke(device, 1);
// Compact version of above
var mi = bTdevice.Class.GetMethod("createRfcommSocket", Integer.Type);
return (BluetoothSocket)mi.Invoke(bTdevice, 1);
}
This is the method indicated here: Xamarin Forum Post
I have tested this on Android 4.4.2 and Android 8.0 and it works on these systems. Another person in the company has tested the Java equivalent code on Android: 4.2.2, 4.4.2, 7.0 and 8.0 and it works in every case.

EADDRINUSE When creating server socket on google glass

I am developing a google glass/android application. It is a video streaming application that has a server/client setup where the phone/glasses is the server and hooks the pc up with the session description for playing the video. It works great on the android and everything runs fine but as soon as I try to test it on the google glass it throws an error at this line
sSocket = new ServerSocket(sPort);
The exception message says "EADDRINUSE" which I'm assuming means the port is already opened but I never opened it. Even if I had opened it and my program didn't close it I changed the port a couple of times and it still says it's in use.
Thanks
Tyler,
Google Glass, like android, consistently will have many of it's ports occupied by applications running in the background. When creating a socket for your server to listen on, you have two choices:
1) Have a predetermined list of ports you can choose to have your server listen on.
If you choose to do this, then you can simply have a datastructure (list, queue, heap [if you have some priority of which ports you would like to use], etc) which contain all of your ports, then you can simply traverse them until you find an open port.
This can be achieved in the following manner:
private ServerSocket allocatePort(List<Integer> myArray) throws IOException {
for (int individualPort : myArray) {
try {
return new ServerSocket(individualPort);
} catch (IOException io) {
continue; // An exception will be thrown if this port is currently in use. It's OK, let's try another port.
}
}
// When no ports are available, let's throw an exception stating we were unable to find an open port.
throw new IOException("we were unable to find an open port");
}
Then simply invoke this method within your as follows:
int[] arrayOfPorts = {5000, 5001, 5002, 8000, 8001, 8002, 8003};
List<Integer> myArray = new ArrayList<>();
myArray = IntStream.of(arrayOfPorts).boxed().collect(Collectors.toList());
ServerSocket sSocket = allocatePort(myArray);
2) If you don't mind which port to listen in on, you can use the constructor to pick any available port.
This can be achieved as follows:
ServerSocket sSocket = new ServerSocket(0);
You can read up more on ServerSocket's Javadocs. Notice under the parameter's subsection:
port - the port number, or 0 to use a port number that is automatically allocated.
Please let me know if you have any questions!

Android KitKat Bluetooth connect

Am i the only one having problems connecting to the bluetooth with startBluetoothSco? This works fine in all versions of Android except 4.4.2 (kitkat). Any suggestions? And yes, I have verified that I am connected to Bluetooth before I call this. Did something changed in 4.4.2?
Here is my code:
am = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
am.setMode(AudioManager.MODE_IN_CALL);
am.setBluetoothScoOn(true);
am.startBluetoothSco();
Following your suggestion i did the following, but this is driving me nuts! What am I doing wrong. I have the listener in my MainActivity as follows...
private final BluetoothHandler.Listener mBluetoothListener = new BluetoothHandler.Listener() {
#Override
public void onConnectionComplete() {
final BluetoothHandler bluetoothHandler = mBluetoothHandler;
if (bluetoothHandler != null) {
am.setMode(AudioManager.MODE_IN_COMMUNICATION);
}
}
};
Then in my OnCreate I initialize the BluetoothHandler
if(mBluetoothHandler == null){
mBluetoothHandler = new BluetoothHandler(5000, mBluetoothListener);
} else {
mBluetoothHandler.stopSco();
mBluetoothHandler.stop();
mBluetoothHandler = null;
}
if (!mBluetoothHandler.isAudioConnected()) {
mBluetoothHandler.start(mContext);
}
The problem I'm having is that the listener doesn't detect when a BT device connects or even says that one is connected. Any suggestions? I appreciate your help...
The functionality of startBluetoothSco() changed between API 17 and API 18. In API 17, this function initiates a virtual call via SCO. In API 18, the function opens a raw SCO link. Some Bluetooth units will only respond to a virtual call.
Unfortunately it doesn't seem that Google have given us an option of choosing whether to open a virtual call or raw link so if you require a virtual call, you will need to ensure your app is built with API 17.
From the Android Developer reference:
"NOTE: up to and including API version JELLY_BEAN_MR1, this method
initiates a virtual voice call to the bluetooth headset. After API
version JELLY_BEAN_MR2 only a raw SCO audio connection is
established."
http://developer.android.com/reference/android/media/AudioManager.html#startBluetoothSco()
Which device do you use? I know that there is sometimes a Bluetooth problem with nexus devices (nexus 5 with kit at also) as described here:
http://www.androidpolice.com/2013/12/28/bug-watch-many-nexus-devices-still-suffer-from-assorted-bluetooth-issues/
Had the same problem with an app I was developming. Upong updating my phone to KitKat, the SCO connection to my car stopped working.
I finally come up with a solultion using a somehow private api in the bluetooth headset profile, while keeping my code compatible with API 19.
Fist I'm checking if current version is API 17, in that case, I use the standard startBluetoothSco from AudioManager, if not the case, I get the current BluetoothHeadset profile and use the following method to create the virtual call sco link (I can't take credit for this, I found it in the Google TalkBack application):
class BluetoothHeadsetCompatWrapper {
private static final Class<?> CLASS_BluetoothHeadset = BluetoothHeadset.class;
private static final Method METHOD_startScoUsingVirtualVoiceCall = CompatUtils.getMethod(
CLASS_BluetoothHeadset, "startScoUsingVirtualVoiceCall", BluetoothDevice.class);
private static final Method METHOD_stopScoUsingVirtualVoiceCall = CompatUtils.getMethod(
CLASS_BluetoothHeadset, "stopScoUsingVirtualVoiceCall", BluetoothDevice.class);
private final BluetoothHeadset mHeadset;
public BluetoothHeadsetCompatWrapper(BluetoothHeadset headset) {
mHeadset = headset;
}
public boolean startScoUsingVirtualVoiceCall(BluetoothDevice device) {
return (Boolean) CompatUtils.invoke(mHeadset, false, METHOD_startScoUsingVirtualVoiceCall,
device);
}
public boolean stopScoUsingVirtualVoiceCall(BluetoothDevice device) {
return (Boolean) CompatUtils.invoke(mHeadset, false, METHOD_stopScoUsingVirtualVoiceCall,
device);
}
}
I found this solution when I was looking into the BluetoothHeadset code and found out the method actually exists, but it's hidden to the compiler https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/bluetooth/BluetoothHeadset.java
I know my anwser may be late, but I posted anyway to help others.

createRfcommSocketToServiceRecord works but listenUsingRfcommWithServiceRecord does not

I am trying to control a Hands-Free link with my device. The following works just fine:
UUID HFP_UUID_GET_HF = UUID.fromString("0000111E-0000-1000-8000-00805F9B34FB");
BluetoothSocket aBluetoothSocket = mDevice
.createRfcommSocketToServiceRecord(HFP_UUID_GET_HF);
and I get a socket that I can read and right to. No problem. However, I also want to listen for an incoming connection and get that socket. I tried this:
UUID HFP_UUID = UUID.fromString("0000111F-0000-1000-8000-00805F9B34FB");
UUID HFP_UUID_GET_HF = UUID.fromString("0000111E-0000-1000-8000-00805F9B34FB");
BluetoothServerSocket tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord("HFP", HFP_UUID);
BluetoothSocket aBluetoothSocket = tmp.accept();
However, even though the two devices connect I never get a socket back. BTW if I use the UUID that starts with 111E in this second code block here I get a service discovery io error, which makes sense -- I know that my device is using uuid 111F and the other device uses UUID 111E.
Has anyone ran into this issue before? I need to be able to have complete control over all data that gets sent from the phone on that rfcomm channel. I cannot use reflection ; i.e.
Class<?>[] args = new Class[] { int.class };
int HFP_CHANNEL = 10;
Method listenOn = BluetoothAdapter.class.getDeclaredMethod("listenUsingRfcommOn", args);
BluetoothServerSocket my_server = (BluetoothServerSocket) (listenOn.invoke(mBluetoothAdapter,
new Object[] { HFP_CHANNEL }));
BluetoothSocket m_BluetoothSocket = my_server.accept();
because that also throws an io error -- channel already in use, unless anyone knows a way to turn off the hands-free system service. Afaik that is part of bluetoothd (Im using Android 4.1 here) and I need that to remain running (Im not sure if I even can turn it off)

How to connect to Bluetooth SPP using dot42?

After reading the dot42 comments and trolling Java examples I managed to setup a Bluetooth connection but fail to open the connection. I cannot determine the problem. I followed the docs step by step.
My target device is a HTC Explorer running on 2.3 Gingerbread. Here is my code.
//Target 2.3 (Gingerbread)
[assembly: Application("dot42Application1")]
[assembly: UsesPermission(Android.Manifest.Permission.BLUETOOTH)]
[assembly: UsesPermission(Android.Manifest.Permission.BLUETOOTH_ADMIN)]
namespace dot42Application1
{
[Activity]
public class MainActivity : Activity
{
private TextView txStatus;
protected override void OnCreate(Bundle savedInstance)
{
base.OnCreate(savedInstance);
SetContentView(R.Layouts.MainLayout);
// Find UI controls
txStatus = FindViewById<TextView>(R.Ids.txStatus);
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
var bt = BluetoothAdapter.GetDefaultAdapter();
if (bt != null) //If device has not Bluetooth this will be null
{
if (bt.IsEnabled()) //Is Bluetooth device enabled?
{
var BT_My_Addr = bt.Address; //Get the devices MAC
var BT_Bonded = bt.GetBondedDevices().ToList(); //Get a list of bonded devices- I bonded to a BT2TTL Board earlier.
txStatus.Text = BT_My_Addr + System.Environment.NewLine; //Shows my MAC on screen.
string BT_Remote_Address = string.Empty;
foreach (var BTDevice in BT_Bonded) //Just searchging for string in bonded list
{
if (BTDevice.Name.Contains("linvor"))
{
BT_Remote_Address = BTDevice.Address;
}
}
//Gets remote device
var BT_Remote_Device = bt.GetRemoteDevice(BT_Remote_Address);
//Create a RFCOMM Socket to remote device using popular UUID ofr BT Serial boards
var BTsocket = BT_Remote_Device.CreateInsecureRfcommSocketToServiceRecord(Java.Util.UUID.FromString("00001101-0000-1000-8000-00805F9B34FB"));
//Call anyway to make sure there is no discvoerry in the backgorund. It slows stuff down.
bt.CancelDiscovery();
//Exception here? Dont know why :(
BTsocket.Connect();
//Suppsoed to dump 0 to 99999 to my listening serial device but I never get this far.
var BT_Out = BTsocket.GetOutputStream();
for (int i = 0; i < 99999; i++)
{
BT_Out.Write(Encoding.ASCII.GetBytes(i.ToString()));
}
}
else
{
txStatus.Text = "Bluetooth is disabled :(";
}
}
}
}
And this is what it shows after the socket creation
and the error...
What am I doing wrong? :(
I seem to have solved the problem by analysing various code snippets on the internet. I think the problem was trying to do everything in the OnCreate method. The steps I followed are the following:
Created a button on the main view (MainActivity.xml) and attached a onClick method.
Moved all the code OUT of the OnCreate method. (I think this allows the application to fully initialise.) Created an event handler for the button with two methods.
The two methods are the same as the code I posted in my original question. Just they are separated out and called when the user clicks the button.
findBT() Gets the default adapter. Checks if Bluetooth is enabled if not does the intent filter. Or if it is it will cycle through the bonded list and match a device name and store the BluetoohDevice in a variable. This is another thing that is different from my code. I do not use GetRemoteDevice I just assign the device from the BondedList to my global variable.
openBT() creates the RFCOMM socket (this did not work with unsecure - it threw an exception but using the secure method worked!)
You have to pair to the remote device using the Androids Bluetooth control panel. This code will not scan or connect to devices that are not paired. It will just throw null exceptions.
Also I left the target SDK 2.3.x but I am using the 4.x API.
-Disclosure. I am not a seasoned Android developer and just learning about the life cycle of Java applications in the Android context. I hope this can help other C# developers trying to do the same.

Categories

Resources