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.
Related
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.
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.
So im working around with bluetooth and trying to figure out how to send two strings via a bluetooth connection. From one android device to another.
I found this guide http://developer.android.com/guide/topics/connectivity/bluetooth.html
but it talks alot about setting up the connection. So i went straight down to the chapter about Managing a Connection. The reason i do this is that in the apps i create i plan to setup the bluetooth connection before opening the apps (via the phones usual bluetooth setup) and then open the apps and send when it is necessary.
So my question is how do i find the bluetooth socket that should be setup? Since that should be what im searching for to create the sending and recieving threads?
Hope this is enough information, else tell what more you need and ill try and answer to the best of my ability.
Best Regards Drakthal
The usual bluetooth setup only pairs between devices, it doesn't create a data connection between them (And even if it would, you wouldn't be able to access this Socket object because it's not created in your process).
After Bluetooth is turned on, you can call BluetoothAdapter.getBondedDevices() to get a set of the paired devices. You can then iterate over them, and initiate a connection to the one you want. You can't avoid the connection creation :( If you want a simplified example, you can look here (An answer I posted a while ago, regarding the whole pairing/connecting/sending/receiving subject with bluetooth).
Once you acquired an open connection, sending the 2 string is easy.
String s1 = "A", s2 = "B";
byte[] buf1 = s1.getBytes(), buf2 = s2.getBytes();
OutputStream os = connection.getOutputStream();
os.write(buf1);
os.write(buf2);
os.flush();
connection.close();
I am stuck at a place where my Android Phone(Samsung Galaxy) has to open 2(or more) connections to my PC(server) which is a paired device. For this purpose, I start SDP server on PC with UUID : 00001101-0000-1000-8000-00805F9B34FB and channel 2, after the first connection I unregister the SDP service on the PC and register it again on channel 3(and same UUID) and expect my android phone to 'connect' to it for establishing the second bluetooth connection. The second one fails.
The problem as I have figured out is that because of the pairing, the channel num and UUID of the device for the 'BluetoothSocket.connect' are serviced from cache and are not being updated, so my SDP change in channel num on the server side is not visible here as it still tries to connect to channel 2(old one).
A workaround of this problem(found after a lot of frustrating attempts) might be to change the android.server.BluetoothService.java file, by introducing
updateDeviceServiceChannelCache(addr) before returning in the getRemoteServiceChannel() and fetchRemoteUuids() so as to update the channel number at each func call.
The above solution may well be wrong, please pardon me for that. If it is correct please suggest any further changes and how to make this change permanent in eclipse, or in other case(wrong solution), the right way of doing this.
Note: I have android 2.3.5 and the requests from the device are actually from the browser that I forward to the PC. I want them on separate channels.
thanks.
Is there any way for Android to connect to a Bluetooth device using a specific port instead of using service UUID?
I know this option is available in other platforms which provide Bluetooth support (Java ME for example by specifying a "btspp://" style URL).
Thanks!
Ok, it's been a while, but I found a solution to the problem. I actually intended to give up and use UUID, but I kept getting a Service Discovery Failed (IO)exception, and when I tried to find a solution to the service discovery issue, I found the solution to my original question... Ain't life something?:)
Anyways, this is the link I stumbled upon, though you should note there is a mistake in the answer (they actually simply connected to port 1, instead of using a service UUID).
And after this short history lesson, here is the solution:
Using reflection, it is possible to create the Rfcomm socket connecting to a port number instead of UUID:
int bt_port_to_connect = 5; // just an example, could be any port number you wish
BluetoothDevice device = ... ; // get the bluetooth device (e.g., using bt discovery)
BluetoothSocket deviceSocket = null;
...
// IMPORTANT: we create a reference to the 'createInsecureRfcommSocket' method
// and not(!) to the 'createInsecureRfcommSocketToServiceRecord' (which is what the
// android SDK documentation publishes
Method m = device.getClass().getMethod("createInsecureRfcommSocket", new Class[] {int.class});
deviceSocket = (BluetoothSocket) m.invoke(device,bt_port_to_connect);
A few things to notice:
since we're using Invoke, the first parameter is the object we're invoking the method on, the second parameter of invoke is actually the first function parameter)
There is also a secure version available ('createRfcommSocket'), which accepts a bluetooth channel number as a single parameter (again, since this is invoke style, you'll need to pass the object to invoke the method on, as mentioned in -1- )
I found what appears to be a link to these functions' prototypes
Good luck to all.
Bluetooth Android connections are exclusively done via UUID. Each Bluetooth device has a UUID for every service it runs (see Bluetooth SDP).
You just give Android the UUID to watch for and, in client mode, it will find a socket to connect to automatically (including port). In server mode, it will wait for the specified device to initiate a connection using the specified UUID.
The BluetoothSocket object is also valid when connection is established (use getInput/Output Stream)
See Server Socket documentation and Client Socket documentation.
If you really want to check everything, you can see what Android decodes from the other device's SDP and the UUID you provided.
Use this tutorial to get the Bluetooth interface (very easy to do).
Then the code should look something like this:
IBluetooth ib =getIBluetooth();
Int otherDevicePort = ib.getRemoteServiceChannel(otherDeviceAddress, UUID);
I'm using bluecove which allow me to do so with the function Connector.open().
I use the following url:
btspp://" + phoneID + ":" + phonePort
N.b.: Some options can be added (e.g.: authenticate=false; or encrypt=false;).
With phoneID being the the being the Bluetooth address and phonePort the port number.
How to find the Bluetooth address?
From this link:
From the Home screen, open the app drawer, then open “Settings“.
Select “System“. (Skip this step on some models)
Scroll down to the bottom and tap “About Phone“, “About device“, or “About tablet“.
Scroll down to the bottom and tap “Status“.
Scroll down and the “Bluetooth address” will be shown in the list.
How to find the port number?
I haven't been able to find which port is supposed to be used yet...
I used 5 and it works but I need to research why and if I want to change the phone I will need to know if I also need to change the port.