accept() and connect() methods in bluetooth socket - android

I am getting bluetooth socket connection problem and so tried various alternatives to connect.
It is like if first fails then second and if second also fails then third and so the main UI blocks during the process.
The exceptions are like -
"Service discovery failed" OR "Host is down"
My all three alternatives -
1) Connect with a connect() function
m = bdDevice.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
socket = (BluetoothSocket) m.invoke(bdDevice, Integer.valueOf(i));
mBluetoothAdapter.cancelDiscovery();
socket.connect();
2) Connect with a accept() function
m = bdDevice.getClass().getMethod ("listenUsingRfcommOn", new Class [] {int.class});
BluetoothServerSocket returnValue =
(BluetoothServerSocket) m.invoke(bdDevice, new Object [] {29});
socket = returnValue.accept();
3) Connect with a well know SPP UUID
private final UUID my_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
socket = bdDevice.createInsecureRfcommSocketToServiceRecord(my_UUID);
mBluetoothAdapter.cancelDiscovery();
socket.connect();
The second alternative is recently added and so I am not sure that how is it different from first and third one. All will block the UI until connect, all can through IOException.

1) You are connecting as a client to your SPP device. Therefore your device must be expecting a connection before you can actually connect.
2) You are accepting the connection as the server. Your bluetooth device will be responsible for firing the connection requests.
3) You are connecting to a device that uses a particular UUID as it's Serial Port Profile (SPP). Think of this as similar to ports in http protocol. The 00001101-0000-1000-8000-00805F9B34FB, is one of the most common ones. More info : https://developer.bluetooth.org/TechnologyOverview/Pages/SPP.aspx
Additionally, if you are thinking on supporting multiple OS / handset versions. You might see those error messages after an initial broken comm. Android's bluetooth stack (bluez) was totally replaced on 4.2, but you might need to still handle previous known bugs.
Reflection in this case is a good thing (some parts of the bluetooth API wasn't public in older versions). But, in my experience, API level < 17; using createInsecureRfcommSocket instead of createRfcommSocket is much more reliable.
Hope this helps. But if you are building an app for a custom bluetooth spp device of yours, explaining how that devices handles comms could help pointing out the exact root of your problem. Although, bluetooth on Android is never straight forward.

Related

Manually set bluetooth server port in Android

I have a working Bluetooth server running (Android app). I would like to set a specific Bluetooth port for it to listen to. The reason for that is that for the client to connect, it takes about 10-15 seconds because it needs to first discover the server (i do a scan ).
the code to create the server is the following:
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
UUID my_uuid = UUID.fromString("12345678-f6ff-4f6f-1f1f-f8f8f8fffff8");
try {
BluetoothServerSocket serverSocket = adapter.listenUsingRfcommWithServiceRecord("myBluetoothServer", my_uuid);
sock1 = serverSocket.accept();
i_s = sock1.getInputStream();
o_s = new OutputStreamWriter(socket.getOutputStream());
new Thread(writter).start();
...
Question: how to specify a fixed port number for the server?
I have been looking here, of course, but it is not easy to find,:
https://developer.android.com/reference/android/bluetooth/BluetoothSocket?hl=ur
I am looking for something like serverSocket.setPort(myPortNumber) (pseudo - code)
The concept of port does not exist for Bluetooth Sockets since they are not regular TCP/IP sockets. They are just abstracted to behave like one.
As you figured out from your code, what you specify is a UUID which is a service identifier. The process to connect to a Bluetooth server goes like this:
Bluetooth Device scan: You can't skip this part, since you need a valid BluetoothDevice object
Service Discovery for a discovered Device: This is the part where you "check" if that Bluetooth Device is running the service that you are looking for (Your service UUID) So you shouldn't skip this part either, unless you want to connect to all surrounding Bluetooth devices.

How do Bluetooth SDP and UUIDs work? (specifically for Android)

My understanding is that the SDP is a list of UUIDs that other devices can fetch.
According to this PDF from MIT, "A more general way to think of
SDP is as an information database." Does this mean I can add multiple values to SDP? Since Android has BluetoothDevice.fetchUuidsWithSdp(), how do I set the UUIDs of a device?
Also, what does each section of an UUID mean? UUIDs look like 00000000-0000-1000-8000-00805F9B34FB, but what information does this convey?
An UUID identifies a service that is available on a particular device. So if you call BluetoothDevice.fetchUUidsWithSdp() your BroadcastReceiver will receive the relevant Intent ACTION_UUID containing the device and the service UUID.
The bluetooth specification defines some common UUIDs.
If you don't want to connect to one of these well known services but intent to implement your own bluetooth application, then you have to just generate your own UUID (use uuidgen from a unix console or an online generator) that identifies your application/service.
You can create an UUID instance in java like this UUID uuid = UUID.fromString("785da8ea-1220-11e5-9493-1697f925ec7b");.
So if you create the server side for your bluetooth application on Android you typically do this
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
BluetoothServerSocket serverSocket = adapter.listenUsingRfcommWithServiceRecord("YourHumanReadableServiceName", uuid);
And this is where you "set" your UUID. The Android bluetooth API creates the SDP-entry consisting of YOUR application's UUID and name for you. Other devices can now retrieve this entry. Androids bluetooth stack will now associate a bluetooth channel to your BluetoothServerSocket. If you want to connect to this ServerSocket, the connecting side usually connects doing this:
// you will most likely already have this instance from a discovery or paired device list
BluetoothDevice serverDevice = adapter.getRemoteDevice(bluetoothMacAddress);
// connect to your ServerSocket using the uuid
BluetoothSocket socket = serverDevice.createRfcommSocketToServiceRecord(uuid);
socket.connect();
Android will again do the heavy lifting for you: It checks the SDP-Records on the remote device, looks up the bluetooth channel that corresponds to your service's UUID and connects using this information.
There is a common code snippet spooking around here on SO that advices you to use "reflection" to get to a hidden API looking similar to this code:
try {
// this is the way to go
socket = device.createRfcommSocketToServiceRecord(uuid);
socket.connect( );
} catch ( IOException exception ) {
// don't do that! You will bypass SDP and things will go sideways.
Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
socket = (BluetoothSocket) m.invoke(device, 1);
socket.connect();
}
Most people try this and it "just works" in their dev environment but you should know what you do using this. You actively bypass the SDP lookup that retrieves the right bluetooth channel to be used with your service and you will end up connecting to channel 1. If you have more than one Service running on the device, things WILL go sideways in this cases and you will end up in debugging hell ;-)
I developed a small middleware called Blaubot to create small networks using bluetooth/wifi/nfc and experienced all sorts of problems on the devices I used to test with (12 models). It was often the case that the bluetooth stack was not fully functional anymore in cases where it got some load or after many connects/disconnects (which you usually will have, if you are developing your app). In these cases the device.createRfcommSocketToServiceRecord(uuid) would occasionally fail and only turning the bluetooth adapter off and on again helped to bring the bluetooth adapters back to life (in some cases only after a full power cycle). If this happens and you use the reflection method, you will probably not have much fun with bluetooth.
But if you know this and keep concurrent calls to the BluetoothAdapter within bounds, bluetooth connections and the adapters will be pretty stable.

the differences between createRfcommSocketToServiceRecord and createRfcommSocket

What are the differences between these two ways below connecting to a bluetooth Device:
1)
UUID uuid = UUID.fromString(Values.SPP_UUID); //Standard SerialPortService ID
mmSocket = mmDevice.createRfcommSocketToServiceRecord(uuid);
2)
Method m = mmDevice.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
mmSocket = (BluetoothSocket) m.invoke(mmDevice, 1);
I find the first way cannot work all the time, sometimes it will work, but after I close the bluetooth device, it won't work again. The second way is always work well. I know it is just open the channel one to communicate with bluetooth, but I do not know how it can do that to connect to a bluetooth device without using uuid?
Think of it a bit like the difference between opening a TCP connection to a port you specify by number, and opening one to a port you look up by name from /etc/services.
createRfcommSocketToServiceRecord takes the UUID you pass and uses SDP to decide what radio channel to use for the connection. It also checks to make sure that a server is listening on the remote endpoint, with the same UUID. In this way, it's the most reliable way to get a connection: it'll always use the correct channel, and if opening the connection succeeds, you know something at the other end can understand your protocol.
In contrast, createRfcommSocket just connects to the channel you tell it. There's no way to know whether anything is listening on the remote endpoint: you only know the device is there. Also, your choice of radio channel may be completely inappropriate. That's why this function is not published in the API, and the other function is preferred.
createRfcommSocket may appear at first to be more reliable, but it's because it's not checking for the presence of a listener at the other endpoint: it's ignoring some error cases. This might be alright for experimenting, but it's no use for a production system, because often the user will forget to start the server on the other endpoint, and your app will fail in confusing ways.
Of course, as createRfcommSocket isn't published in the API, you've no guarantee it will continue to work at all in future releases of Android.

RFCOMM_CreateConnection - already opened state:2, RFC state:4, MCB state:5

I´m writing an android app that connects to a device through bluetooth using RFCOMM. I use the BluetoothChat example as basis for establishing a connection and everything works perfectly most of the time.
However, sometimes I cannot reconnect due to a message that the socket is already open:
RFCOMM_CreateConnection - already opened state:2, RFC state:4, MCB
state:5
This tends to happen if I connect to the device, close the app (call onDestroy()), reopen it and try to connect again, which results in the above.
I use this method for connecting in the ConnectThread(ref.BluetoothChat example):
Method m = device.getClass().getMethod("createRfcommSocket",new Class[] {int.class });
tmp = (BluetoothSocket) m.invoke(device, Integer.valueOf(1));
mmSocket = tmp;
The only thing that resolves this problem is turning off/on the Bluetooth of the Android phone.
This leads me to believe that the socket is not being closed in onDestroy() but still I´m calling on closing all threads as shown in the before mentioned example.
Any ideas would be appreciated.
I stumbled upon this one too, and here is the answer I found:
This error may happen, if you open and close a bluetooth socket connection multiple times.
Solution
Starting from API Level 14 there is a Method in BluetoothSocket called isConected(), which returns true, if this socket is already connected and false otherwise, here the original excerpt from the API:
Get the connection status of this socket, ie, whether there is an
active connection with remote device.
For API levels < 14 you can work around this issue by putting your Bluetooth Handling Thread to sleep after closing the connection - 1000 ms should be enough, here is an example (btDevice is of the type BluetoothDevice and has been initialized prior to the code snippet below):
try {
//Open the socket to an SPP device (UUID taken from Android API for createRfcommSocketToServiceRecord)
BluetoothSocket btSocket = btDevice.createRfcommSocketToServiceRecord("00001101-0000-1000-8000-00805F9B34FB");
//Connect to the socket
btSocket.connect();
//Close the socket
btSocket.close();
//Sleep time of 1000ms after closing the socket
SystemClock.sleep(POST_RESET_DELAY);
} catch (Throwable e) {
// Log error message
}
P.s. Instead of SystemClock.sleep you can also use Thread.sleep - however the SystemCock's sleep can't be interrupted, whereas the Thread.sleep can be interrupted, so it depends on your use-case which option better suits your purpose.
Source: Louis A. Prado

Android BluetoothSocket.connect() throws IOExceptions "Connection Refused" and "Service discovery failed"

My goal is to write an app that allows me to control my Motorola Xoom with a Playstation 3 Bluetooth Remote Control.
The device is able to be discovered by the native bluetooth app & classified as being a joystick. However, I cannot pair via the native bluetooth app because the app requires a PIN & the device does not have a pin that I am aware of.
So far I am able to programmatically discover the device & create a socket, however all attempts to connect to the device fail.
In both cases:
UUID u = UUID.fromString("00001124-0000-1000-8000-00805f9b34fb");
This is supposed to be the UUID used by HID devices. I also used the method described on another site to verify the UUID is available on the device.
Method1 (many people seem to have issues with this):
BluetoothSocket socket = device.createRfcommSocketToServiceRecord(u);
socket.connect();
Result: IOException "Service discovery failed"
Method2 (the accepted workaround to Method1. I also tried ports 1-100):
Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
BluetoothSocket socket = (BluetoothSocket) m.invoke(device, 1);
socket.connect();
Result: IOException "Connection refused"
I have tried this code using 3 devices (plus their UUIDs):
Playstation Remote
00001124-0000-1000-8000-00805f9b34fb
00001200-0000-1000-8000-00805f9b34fb
Nintento Wiimote
00001000-0000-1000-8000-00805f9b34fb
00001124-0000-1000-8000-00805f9b34fb
00001200-0000-1000-8000-00805f9b34fb
Microsoft Bluetooth Number Pad
(which sucessfully pairs, with a pin, via the natvie bluetooth app)
00001000-0000-1000-8000-00805f9b34fb
00001124-0000-1000-8000-00805f9b34fb
00001200-0000-1000-8000-00805f9b34fb
To be able to connect to the devices you have to connect over the HID profile, what you are trying is connecting over SPP (Serial Port Profile) to the UUID for HID etc, this will not work.
In addition these devices have some "custom" HID protocol descriptors that allow it to work with this pre-paired gaming consoles, you will need to get access to those to be able to control the Xoom with these controllers

Categories

Resources