Service discovery failed exception using Bluetooth on Android
Asked Answered
P

5

49

I'm currently working on an Android application that connects to an instrument via Bluetooth and need to write string commands and receive string responses back. Currently I have the connect/read/write working for TCP/IP over Wi-Fi and now trying to implement Bluetooth. But I am running into some roadblocks. I have been searching the web trying to find examples of something similar and haven't had any luck. I have been using the Android developer resource example: Bluetooth Chat as my main reference point.

My current code seems to work.. Then it throws a Service Discovery Failed exception at the point of the connection. I am using the DeviceListActivity class to do the discovery and selecting of the device I want to connect to. It returns anActivityResult and then my Bluetooth class waits for it to handle that and then does the connect to it. The code beneath is almost identical to the Bluetooth Chat App.

public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if(!m_BluetoothAdapter.isEnabled())
    {
        m_BluetoothAdapter.enable();
    }
    switch (requestCode) {
        case REQUEST_CONNECT_DEVICE:
            // When DeviceListActivity returns with a device to connect
            if (resultCode == Activity.RESULT_OK) {
                // Get the device MAC address
                String address = data.getExtras()
                                     .getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
                // Get the BLuetoothDevice object
                BluetoothDevice device = m_BluetoothAdapter.getRemoteDevice(address);
                // Attempt to connect to the device
                connect(device);
            }
            break;

        case REQUEST_ENABLE_BT:
            // When the request to enable Bluetooth returns
            if (resultCode == Activity.RESULT_OK) {
                // Bluetooth is now enabled, so set up a chat session
            }
            else {
                // User did not enable Bluetooth or an error occured

                Toast.makeText(this, "Bluetooth not enabled", Toast.LENGTH_SHORT).show();
                finish();
            }
    }
}

This is my connect function:

private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");

private void connect(BluetoothDevice device) {
    m_Device = device;
    BluetoothSocket tmp = null;

    // Get a BluetoothSocket for a connection with the
    // given BluetoothDevice
    try {
        tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
    }
    catch (IOException e) {

    }
    m_Socket = tmp;

    m_BluetoothAdapter.cancelDiscovery();

    try {
        // This is a blocking call and will only return on a
        // successful connection or an exception
        m_Socket.connect();
    }
    catch (IOException e) {
        try {
            m_Socket.close();
        }
        catch (IOException e2) {
        }
        return;
    }
}

Hopefully, whatever I am doing wrong is simple, but I'm afraid it's never that easy. This is my first time doing any Bluetooth development, and maybe I'm doing something blatantly wrong... But I'm not sure why I get the service discovery failed exception.

You can pair/find the device at all times manually on the phone... It does require a passcode, but I don't think that is the problem that I am having.

Paulus answered 3/8, 2010 at 13:44 Comment(0)
P
93

After three days I got it figured out thanks to some very helpful posts.

I had to replace:

tmp = device.createRfcommSocketToServiceRecord(MY_UUID);

with:

Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
         tmp = (BluetoothSocket) m.invoke(device, 1);

and voilà it works!

Paulus answered 3/8, 2010 at 14:53 Comment(11)
Wait, I have a few Bluetooth apps and I haven't found a situation where using reflection (getclass.getmethod) is necessary. Can you tell me a little more about what hardware you tried? I am very interested to know if reflection is actually necessary in certain situations.Hydrophilic
After some testing. I found that it was actually the phone that was having issues with it. The HTC Incredible did not connect using the first method, however the Samsung Galaxy S and another, can't remember which one, did connect on first try.Paulus
I was plagued with this exception and this solution seemed to fix my problems. Do you understand what is going on here and why it alleviates the problem of the Service Discovery failing?Tenpins
Honestly, I do not. From what I've read it might have something to do with the Bluetooth support that HTC is lacking in their phones and by using the reflection its a work around.Paulus
The best I found was try the first method, which connects faster. Then on the Service Discovery fail implement the reflection in the exception. Also found that the same HTC phones also had issues with UDP detection as well, while Samsung and Motorola devices had no issues.Paulus
how would the listentoRfcommSocket (server side) look like? Because I cant get the device from it.Vociferous
Correct me if I am wrong, but in that way, would you connect to the phone regardless of whether it is offering the service you wish to connect to?Toga
Reading the source code for BluetoothDevice class in JellyBean, it seems that this creates an RFCOMM socket on channel 1. The javadocs are intentionally skipped, and the method is not exposed in the API. I have no idea of why this hack works, but it allowed me to communicate with a device allegedly lacking SPP profile, where no UUID worked.Salimeter
"Permission denied" exception is thrown for me although I have requested both BLUETOOTH and BLUETOOTH_ADMIN permissions. The moment I changed this code back to the one in BluetoothChat, the "Permission denied" exception was gone but the "Service discovery failed" exception came back. What should I do?Gilder
This works for me also, but this is a hack. This assumes that the server channel is fixed. Unfortunately, I cannot guarantee that the channel will be any specific channel. Does anyone know how to solve this but avoiding this solution?Simulation
How do i find the channel number. Why should we always use 1 as the channel number ?Redheaded
L
13

As of API 15 you can use the following method:

Try replacing your UUID with the return value of getUuids() method of BluetoothDevice class. What worked for me was something like this:

UUID uuid = bluetoothDevice.getUuids()[0].getUuid();
BluetoothSocket socket = bluetoothDevice.createRfcommSocketToServiceRecord(uuid);

The reason this works is that different devices support different UUIDs and by getting the UUIDs of the device using getUuids you are supporting all features and devices.

Another interesting new method (supported since API 14) is this: BluetoothHealth.getConnectionState. Haven't tried it but looks promising...

Laidlaw answered 13/5, 2013 at 19:31 Comment(0)
T
2

This was a suggested edit from an anonymous user attempting to reply to the accepted answer.

One big difference between your before and after code is the UUID you are passing. I found my answer here: http://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createRfcommSocketToServiceRecord(java.util.UUID)

I had to replace:

tmp = device.createRfcommSocketToServiceRecord(MY_UUID);

with:

private static final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
tmp = device.createRfcommSocketToServiceRecord(SPP_UUID);

and voila it works! The original code is for a peer to peer android app. It makes no sense to use the app UUID when connecting to a simple serial bluetooth device. Thats why discovery fails.

Tengler answered 3/8, 2010 at 13:44 Comment(5)
And now another anonymous user comments: The suggested edit is using the exact same method as the code that doesnt work. The working code uses a different non-public method that doesnt require the uuid.Tengler
Got the same error as above when attempting to write an android application that connects to a Bluetooth scanner. This solution worked for me.Allisan
@Monsingor I can't comment either way, I just took the suggested edit - but please tell us why, or better yet edit and correct the answer.Tengler
@Tengler The original code uses "00001101-0000-1000-8000-00805F9B34FB" as a UUID argument for createRfcommSocketToServiceRecord() function right from the beginning. I don't understand why you added an answer that proposes to do the same thing in a slightly different manner. Obviously, this wouldn't help.Coarse
@Monsingor I believe the key differenence is the UUID that is used, even though the code is the same. The javadoc that is referred to above states: Hint: If you are connecting to a Bluetooth serial board then try using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. When I tried this, it worked for me as well.Liturgics
E
1

So as it mentioned above, the point is that you need to use the UUID that the server is waiting for.

If you are connecting to a bluetooth device, such as a headset or mouse, you need to check which UUIDs the device is listening for. You can see the UUIDs like this.

UUID[] uuids = bluetoothDevice.getUuids();

And if you want to know what these UUIDs mean, see this.

Eaves answered 10/6, 2015 at 12:28 Comment(0)
A
1

This is a realy old one question but i found that using the createInsecureRfcommSocketToServiceRecord() instead of createRfcommSocketToServiceRecord() along with the getUuids() previously mentioned do the trick for me

UUID uuid = bluetoothDevice.getUuids()[0].getUuid();
BluetoothSocket socket = bluetoothDevice.createInsecureRfcommSocketToServiceRecord(uuid);
Autocracy answered 15/9, 2017 at 18:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.