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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
TxAg
  • 1,485
  • 2
  • 13
  • 14

5 Answers5

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!

AgentKnopf
  • 4,295
  • 7
  • 45
  • 81
TxAg
  • 1,485
  • 2
  • 13
  • 14
  • 1
    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. – Brad Hein Aug 03 '10 at 17:13
  • 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. – TxAg Aug 12 '10 at 18:50
  • 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? – GobiasKoffi Sep 27 '10 at 19:39
  • 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. – TxAg Oct 25 '10 at 19:56
  • 5
    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. – TxAg Oct 25 '10 at 19:58
  • how would the listentoRfcommSocket (server side) look like? Because I cant get the device from it. – Miky Nov 18 '11 at 19:14
  • 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? – chesterbr Dec 31 '11 at 01:12
  • 1
    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. – Mister Smith Oct 09 '12 at 16:44
  • 1
    "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? – ericn Aug 16 '13 at 09:02
  • 1
    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? – gnychis Mar 20 '14 at 22:08
  • How do i find the channel number. Why should we always use 1 as the channel number ? – Sagar Waghmare Apr 24 '14 at 11:51
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...

Muzikant
  • 8,070
  • 5
  • 54
  • 88
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.

Community
  • 1
  • 1
Rup
  • 33,765
  • 9
  • 83
  • 112
  • And now [another anonymous user comments](http://stackoverflow.com/suggested-edits/335727): 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. – Rup Aug 10 '12 at 01:27
  • Got the same error as above when attempting to write an android application that connects to a Bluetooth scanner. This solution worked for me. – zarazan Jan 14 '13 at 20:09
  • @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. – Rup Mar 17 '13 at 11:17
  • 8
    @Rup 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. – Monsignor Mar 18 '13 at 00:42
  • @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. – Steve Moseley Mar 11 '15 at 12:37
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.

Devgrapher
  • 87
  • 6
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);
animalito maquina
  • 2,264
  • 3
  • 20
  • 35