112

Currently I am trying to deal with a strange Exception when opening a BluetoothSocket on my Nexus 7 (2012), with Android 4.3 (Build JWR66Y, I guess the second 4.3 update). I have seen some related postings (e.g. https://stackoverflow.com/questions/13648373/bluetoothsocket-connect-throwing-exception-read-failed), but none seems to provide a workaround for this issue. Also, as suggested in these threads, re-pairing does not help, and constantly trying to connect (through a stupid loop) also has no effect.

I am dealing with an embedded device (a noname OBD-II car adapter, similar to http://images04.olx.com/ui/15/53/76/1316534072_254254776_2-OBD-II-BLUTOOTH-ADAPTERSCLEAR-CHECK-ENGINE-LIGHTS-WITH-YOUR-PHONE-Oceanside.jpg). My Android 2.3.7 phone does not have any issues connecting, and the Xperia of a colleague (Android 4.1.2) also works. Another Google Nexus (I dont know if 'One' or 'S', but not '4') also fails with Android 4.3.

Here is the Snippet of the connection establishment. It is running in its own Thread, created within a Service.

private class ConnectThread extends Thread {

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

    private BluetoothAdapter adapter;
    private boolean secure;
    private BluetoothDevice device;
    private List<UUID> uuidCandidates;
    private int candidate;
    protected boolean started;

    public ConnectThread(BluetoothDevice device, boolean secure) {
        logger.info("initiliasing connection to device "+device.getName() +" / "+ device.getAddress());
        adapter = BluetoothAdapter.getDefaultAdapter();
        this.secure = secure;
        this.device = device;

        setName("BluetoothConnectThread");

        if (!startQueryingForUUIDs()) {
            this.uuidCandidates = Collections.singletonList(EMBEDDED_BOARD_SPP);
            this.start();
        } else{
            logger.info("Using UUID discovery mechanism.");
        }
        /*
         * it will start upon the broadcast receive otherwise
         */
    }

    private boolean startQueryingForUUIDs() {
        Class<?> cl = BluetoothDevice.class;

        Class<?>[] par = {};
        Method fetchUuidsWithSdpMethod;
        try {
            fetchUuidsWithSdpMethod = cl.getMethod("fetchUuidsWithSdp", par);
        } catch (NoSuchMethodException e) {
            logger.warn(e.getMessage());
            return false;
        }

        Object[] args = {};
        try {
            BroadcastReceiver receiver = new BroadcastReceiver() {
                @Override
                public void onReceive(Context context, Intent intent) {
                    BluetoothDevice deviceExtra = intent.getParcelableExtra("android.bluetooth.device.extra.DEVICE");
                    Parcelable[] uuidExtra = intent.getParcelableArrayExtra("android.bluetooth.device.extra.UUID");

                    uuidCandidates = new ArrayList<UUID>();
                    for (Parcelable uuid : uuidExtra) {
                        uuidCandidates.add(UUID.fromString(uuid.toString()));
                    }

                    synchronized (ConnectThread.this) {
                        if (!ConnectThread.this.started) {
                            ConnectThread.this.start();
                            ConnectThread.this.started = true;
                            unregisterReceiver(this);
                        }

                    }
                }

            };
            registerReceiver(receiver, new IntentFilter("android.bleutooth.device.action.UUID"));
            registerReceiver(receiver, new IntentFilter("android.bluetooth.device.action.UUID"));

            fetchUuidsWithSdpMethod.invoke(device, args);
        } catch (IllegalArgumentException e) {
            logger.warn(e.getMessage());
            return false;
        } catch (IllegalAccessException e) {
            logger.warn(e.getMessage());
            return false;
        } catch (InvocationTargetException e) {
            logger.warn(e.getMessage());
            return false;
        }           

        return true;
    }

    public void run() {
        boolean success = false;
        while (selectSocket()) {

            if (bluetoothSocket == null) {
                logger.warn("Socket is null! Cancelling!");
                deviceDisconnected();
                openTroubleshootingActivity(TroubleshootingActivity.BLUETOOTH_EXCEPTION);
            }

            // Always cancel discovery because it will slow down a connection
            adapter.cancelDiscovery();

            // Make a connection to the BluetoothSocket
            try {
                // This is a blocking call and will only return on a
                // successful connection or an exception
                bluetoothSocket.connect();
                success = true;
                break;

            } catch (IOException e) {
                // Close the socket
                try {
                    shutdownSocket();
                } catch (IOException e2) {
                    logger.warn(e2.getMessage(), e2);
                }
            }
        }

        if (success) {
            deviceConnected();
        } else {
            deviceDisconnected();
            openTroubleshootingActivity(TroubleshootingActivity.BLUETOOTH_EXCEPTION);
        }
    }

    private boolean selectSocket() {
        if (candidate >= uuidCandidates.size()) {
            return false;
        }

        BluetoothSocket tmp;
        UUID uuid = uuidCandidates.get(candidate++);
        logger.info("Attempting to connect to SDP "+ uuid);
        try {
            if (secure) {
                tmp = device.createRfcommSocketToServiceRecord(
                        uuid);
            } else {
                tmp = device.createInsecureRfcommSocketToServiceRecord(
                        uuid);
            }
            bluetoothSocket = tmp;
            return true;
        } catch (IOException e) {
            logger.warn(e.getMessage() ,e);
        }

        return false;
    }

}

The code is failing at bluetoothSocket.connect(). I am getting a java.io.IOException: read failed, socket might closed, read ret: -1. This is the corresponding source at GitHub: https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothSocket.java#L504 Its called through readInt(), called from https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothSocket.java#L319

Some metadata dump of the used socket resulted in the following information. These are exactly the same on Nexus 7 and my 2.3.7 phone.

Bluetooth Device 'OBDII'
Address: 11:22:33:DD:EE:FF
Bond state: 12 (bonded)
Type: 1
Class major version: 7936
Class minor version: 7936
Class Contents: 0
Contents: 0

I have some other OBD-II adapters (more expansives) and they all work. Is there any chance, that I am missing something or might this be a bug in Android?

B.Letz
  • 136
  • 1
  • 16
matthes
  • 2,972
  • 3
  • 15
  • 18

16 Answers16

139

I have finally found a workaround. The magic is hidden under the hood of the BluetoothDevice class (see https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothDevice.java#L1037).

Now, when I receive that exception, I instantiate a fallback BluetoothSocket, similar to the source code below. As you can see, invoking the hidden method createRfcommSocket via reflections. I have no clue why this method is hidden. The source code defines it as public though...

Class<?> clazz = tmp.getRemoteDevice().getClass();
Class<?>[] paramTypes = new Class<?>[] {Integer.TYPE};

Method m = clazz.getMethod("createRfcommSocket", paramTypes);
Object[] params = new Object[] {Integer.valueOf(1)};

fallbackSocket = (BluetoothSocket) m.invoke(tmp.getRemoteDevice(), params);
fallbackSocket.connect();

connect() then does not fail any longer. I have experienced a few issues still. Basically, this sometimes blocks and fails. Rebooting the SPP-Device (plug off / plug in) helps in such cases. Sometimes I also get another Pairing request after connect() even when the device is already bonded.

UPDATE:

here is a complete class, containing some nested classes. for a real implementation these could be held as seperate classes.

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.util.List;
import java.util.UUID;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.util.Log;

public class BluetoothConnector {

    private BluetoothSocketWrapper bluetoothSocket;
    private BluetoothDevice device;
    private boolean secure;
    private BluetoothAdapter adapter;
    private List<UUID> uuidCandidates;
    private int candidate;


    /**
     * @param device the device
     * @param secure if connection should be done via a secure socket
     * @param adapter the Android BT adapter
     * @param uuidCandidates a list of UUIDs. if null or empty, the Serial PP id is used
     */
    public BluetoothConnector(BluetoothDevice device, boolean secure, BluetoothAdapter adapter,
            List<UUID> uuidCandidates) {
        this.device = device;
        this.secure = secure;
        this.adapter = adapter;
        this.uuidCandidates = uuidCandidates;

        if (this.uuidCandidates == null || this.uuidCandidates.isEmpty()) {
            this.uuidCandidates = new ArrayList<UUID>();
            this.uuidCandidates.add(UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"));
        }
    }

    public BluetoothSocketWrapper connect() throws IOException {
        boolean success = false;
        while (selectSocket()) {
            adapter.cancelDiscovery();

            try {
                bluetoothSocket.connect();
                success = true;
                break;
            } catch (IOException e) {
                //try the fallback
                try {
                    bluetoothSocket = new FallbackBluetoothSocket(bluetoothSocket.getUnderlyingSocket());
                    Thread.sleep(500);                  
                    bluetoothSocket.connect();
                    success = true;
                    break;  
                } catch (FallbackException e1) {
                    Log.w("BT", "Could not initialize FallbackBluetoothSocket classes.", e);
                } catch (InterruptedException e1) {
                    Log.w("BT", e1.getMessage(), e1);
                } catch (IOException e1) {
                    Log.w("BT", "Fallback failed. Cancelling.", e1);
                }
            }
        }

        if (!success) {
            throw new IOException("Could not connect to device: "+ device.getAddress());
        }

        return bluetoothSocket;
    }

    private boolean selectSocket() throws IOException {
        if (candidate >= uuidCandidates.size()) {
            return false;
        }

        BluetoothSocket tmp;
        UUID uuid = uuidCandidates.get(candidate++);

        Log.i("BT", "Attempting to connect to Protocol: "+ uuid);
        if (secure) {
            tmp = device.createRfcommSocketToServiceRecord(uuid);
        } else {
            tmp = device.createInsecureRfcommSocketToServiceRecord(uuid);
        }
        bluetoothSocket = new NativeBluetoothSocket(tmp);

        return true;
    }

    public static interface BluetoothSocketWrapper {

        InputStream getInputStream() throws IOException;

        OutputStream getOutputStream() throws IOException;

        String getRemoteDeviceName();

        void connect() throws IOException;

        String getRemoteDeviceAddress();

        void close() throws IOException;

        BluetoothSocket getUnderlyingSocket();

    }


    public static class NativeBluetoothSocket implements BluetoothSocketWrapper {

        private BluetoothSocket socket;

        public NativeBluetoothSocket(BluetoothSocket tmp) {
            this.socket = tmp;
        }

        @Override
        public InputStream getInputStream() throws IOException {
            return socket.getInputStream();
        }

        @Override
        public OutputStream getOutputStream() throws IOException {
            return socket.getOutputStream();
        }

        @Override
        public String getRemoteDeviceName() {
            return socket.getRemoteDevice().getName();
        }

        @Override
        public void connect() throws IOException {
            socket.connect();
        }

        @Override
        public String getRemoteDeviceAddress() {
            return socket.getRemoteDevice().getAddress();
        }

        @Override
        public void close() throws IOException {
            socket.close();
        }

        @Override
        public BluetoothSocket getUnderlyingSocket() {
            return socket;
        }

    }

    public class FallbackBluetoothSocket extends NativeBluetoothSocket {

        private BluetoothSocket fallbackSocket;

        public FallbackBluetoothSocket(BluetoothSocket tmp) throws FallbackException {
            super(tmp);
            try
            {
              Class<?> clazz = tmp.getRemoteDevice().getClass();
              Class<?>[] paramTypes = new Class<?>[] {Integer.TYPE};
              Method m = clazz.getMethod("createRfcommSocket", paramTypes);
              Object[] params = new Object[] {Integer.valueOf(1)};
              fallbackSocket = (BluetoothSocket) m.invoke(tmp.getRemoteDevice(), params);
            }
            catch (Exception e)
            {
                throw new FallbackException(e);
            }
        }

        @Override
        public InputStream getInputStream() throws IOException {
            return fallbackSocket.getInputStream();
        }

        @Override
        public OutputStream getOutputStream() throws IOException {
            return fallbackSocket.getOutputStream();
        }


        @Override
        public void connect() throws IOException {
            fallbackSocket.connect();
        }


        @Override
        public void close() throws IOException {
            fallbackSocket.close();
        }

    }

    public static class FallbackException extends Exception {

        /**
         * 
         */
        private static final long serialVersionUID = 1L;

        public FallbackException(Exception e) {
            super(e);
        }

    }
}
matthes
  • 2,972
  • 3
  • 15
  • 18
  • If you want to connect to multiple bluetooth devices, remember that you need to increase the number of the channel. Valid channels are 1-30 – Jonno_FTW Oct 27 '14 at 05:37
  • Hi, How did you use this code for connection, Can you please tell me – bindal Feb 26 '15 at 06:33
  • 1
    Thank you @matthes.. you solved my question http://stackoverflow.com/q/29143695/1994950 I have mentioned your answer there – Kushal Apr 04 '15 at 09:58
  • @matthes, How to use the BluetoothConnector class? Does it replace the BluetoothSocket class? Thanks. – The Original Android Jan 13 '16 at 08:54
  • Thanks for Explanation, I am very new to bluetooth as i am trying this in my code, but its showing IOException: read failed, socket might closed at @fallbackSocket.connect(); Please help me out. If possible can you send me code for sender and receiver end. – Mukesh Garg Jan 20 '16 at 02:43
  • 3
    @matthes Sorry to say but even your solution of using the fallback has not Solved my problem. Getting below Error. `Fallback failed. Cancelling. java.io.IOException: Connection refused` Please help. – Tushar Banne Feb 17 '16 at 18:48
  • 2
    @matthes "SPP-Device (plug off / plug in) helps in such cases. " . The on/off statement is the most underrated in the world. I just wasted 3 hours and all I had to do was turn it on and off -_- – Adz Feb 25 '16 at 00:48
  • 1
    When I try to write int the BluetoothSocketWrapper getOutputStream(), I get the java.io.IOException: socket closed exception. – Madhan Oct 12 '16 at 12:44
  • @matthes : How to use this BluetoothConnector class ? – Pragya Mendiratta Jan 31 '18 at 07:15
  • 2
    @matthes, big thanks for this idea (and the source), I don't know how you came up with this idea but you are awsome. Working as it should :) By the way - "normal" way of setting up bluetooth was working unitll I upgraded project from API 21 to 24. Maybe this hint shines some light on what exactly is going on here... – michelson Jun 13 '18 at 17:57
  • This also works for me but I would like to ask, if I want to close the connection from another method, should I call the constructor again? – John Mar 27 '20 at 20:56
  • For those if this solution is not working try to change 1 with 2. I replaced Object[] params = new Object[] {Integer.valueOf(1)}; with: Object[] params = new Object[] {Integer.valueOf(2)}; and it is working now. – Umama Khalid Feb 22 '21 at 11:05
  • 1
    Thankx @UmamaKhalid changing value from 1 with 2 is working now. Saved my lot of time. – Hussnain Hashmi May 10 '23 at 21:48
109

well, i had the same problem with my code, and it's because since android 4.2 bluetooth stack has changed. so my code was running fine on devices with android < 4.2 , on the other devices i was getting the famous exception "read failed, socket might closed or timeout, read ret: -1"

The problem is with the socket.mPort parameter. When you create your socket using socket = device.createRfcommSocketToServiceRecord(SERIAL_UUID); , the mPort gets integer value "-1", and this value seems doesn't work for android >=4.2 , so you need to set it to "1". The bad news is that createRfcommSocketToServiceRecord only accepts UUID as parameter and not mPort so we have to use other aproach. The answer posted by @matthes also worked for me, but i simplified it: socket =(BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device,1);. We need to use both socket attribs , the second one as a fallback.

So the code is (for connecting to a SPP on an ELM327 device):

BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();

    if (btAdapter.isEnabled()) {
        SharedPreferences prefs_btdev = getSharedPreferences("btdev", 0);
        String btdevaddr=prefs_btdev.getString("btdevaddr","?");

        if (btdevaddr != "?")
        {
            BluetoothDevice device = btAdapter.getRemoteDevice(btdevaddr);

            UUID SERIAL_UUID = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb"); // bluetooth serial port service
            //UUID SERIAL_UUID = device.getUuids()[0].getUuid(); //if you don't know the UUID of the bluetooth device service, you can get it like this from android cache

            BluetoothSocket socket = null;

            try {
                socket = device.createRfcommSocketToServiceRecord(SERIAL_UUID);
            } catch (Exception e) {Log.e("","Error creating socket");}

            try {
                socket.connect();
                Log.e("","Connected");
            } catch (IOException e) {
                Log.e("",e.getMessage());
                try {
                    Log.e("","trying fallback...");

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

                    Log.e("","Connected");
                }
             catch (Exception e2) {
                 Log.e("", "Couldn't establish Bluetooth connection!");
              }
            }
        }
        else
        {
            Log.e("","BT device not selected");
        }
    }
George Dima
  • 2,707
  • 3
  • 17
  • 19
  • 70
    for moderators: since you deleted my previous answer for no reason, i post it again. – George Dima Sep 03 '14 at 14:40
  • 2
    thanks George for the insights on the `mPort` parameter! imho the workflow stays the same, I just wrapped things with some classes implementing an interface. – matthes Sep 04 '14 at 08:32
  • 7
    yes, i already said your solution was good, but i wanted to make people understand why it needs to use this approach starting with android 4.2 – George Dima Sep 04 '14 at 16:37
  • 1
    It did not work. :( I used it in a Samsung Galaxy S2 (Android 4.1.2) acting as server, and a Samsung Galaxy S3 (Android 4.3) as client, having previously discovered each other. Changing roles does not work either. – jsanmarb Sep 11 '14 at 17:25
  • well, be more precise , what error do you have? first of all you don't need this fallback sollution since you are in android 4.1 , what bluetooth service are you using, my example is for SPP – George Dima Sep 11 '14 at 21:31
  • 2
    SPP = Serial Port Profile (that emulates a serial port over bluetooth) and ELM327 is a bluetooth <-> obd car device , google it. – George Dima Feb 06 '15 at 18:35
  • @GeorgeDima.. Can you give an insight on why this approach works? Also why the socket.connect() fails, I wish I know how the person who thought and found the first solution would have thought and reached this answer. – Sreekanth Karumanaghat Mar 25 '15 at 11:55
  • 1
    i know it's because since android 4.2 bluetooth stack has changed, i'm not an android os creator to know more... – George Dima Mar 25 '15 at 15:40
  • @GeorgeDima... On fallback why doses the reflection method work? Please provide me with more information if possible. – Sreekanth Karumanaghat Mar 26 '15 at 13:08
  • 1
    i already explained in my post! (the mPort gets integer value "-1", and this value seems doesn't work for android >=4.2 , so you need to set it to "1") , so the fallback methods enables you to set mport to 1 vs the default method with sets it to -1. – George Dima Mar 26 '15 at 17:49
  • 11
    Its worked for me after changed the port value from 1 to 2.please look at this code. socket =(BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device,2); socket.connect(); – Dinesh IT Jul 09 '15 at 06:39
  • 1
    Thanks for All comments above and posted answer. 1 was not working for me.. socket =(BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device,2); socket.connect();.. Its showing connected now. Can you please help me with the receiver end means how i will receive data. – Mukesh Garg Jan 20 '16 at 02:56
  • how do you do the fallback if you originally got the socket via a serversocket? – Fuad May 19 '16 at 16:28
  • 2
    Hi george , i have tried with the both values 1 and 2 but still getting the same exception "read failed, socket might closed or timeout, read ret: -1" . . .plz help me out – Nirmal Prajapat Feb 01 '18 at 10:51
  • 2
    The above solution still gives the same error. And as @NirmalPrajapat tried both values I Still see same behavior. Even in Android 7.0. Is this indication that Android is just broken? Or is it that the two dozen or so different device I have tried are all broken? – Logic1 Mar 04 '18 at 00:13
  • I get `NullPointerException` on `socket.connect()` while trying to implement this – ChumiestBucket Sep 28 '18 at 16:38
  • This works like charm. Thanks!! – Fuad Reza Aug 18 '23 at 09:25
19

First, if you need to talk to a bluetooth 2.x device, this documentation states that :

Hint: If you are connecting to a Bluetooth serial board then try using the well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. However if you are connecting to an Android peer then please generate your own unique UUID.

I didn't think that it would work, but only by replacing the UUID with 00001101-0000-1000-8000-00805F9B34FB it works. However, this code seems to handle the problem of SDK version, and you can just replace the function device.createRfcommSocketToServiceRecord(mMyUuid); with tmp = createBluetoothSocket(mmDevice); after defining the following method :

private BluetoothSocket createBluetoothSocket(BluetoothDevice device)
    throws IOException {
    if(Build.VERSION.SDK_INT >= 10){
        try {
            final Method m = device.getClass().getMethod("createInsecureRfcommSocketToServiceRecord", new Class[] { UUID.class });
            return (BluetoothSocket) m.invoke(device, mMyUuid);
        } catch (Exception e) {
            Log.e(TAG, "Could not create Insecure RFComm Connection",e);
        }
    }
    return  device.createRfcommSocketToServiceRecord(mMyUuid);
}

The source code isn't mine, but comes from this website.

tobiasBora
  • 1,542
  • 14
  • 23
  • 2
    That solved almost 2 days of work... *thankful sigh* ...Without this UUID the socket would close immediately and fail without further explanation. – David Sinclair Jul 17 '17 at 19:07
  • thank you very much for help MY UUID is alphanumeric and i am trying to connect HC-5 and my bluetooth showing connection fail but when i used this solution my problem solve. thank you again – Nikhil Shende Feb 04 '20 at 10:45
9

I had the same symptoms as described here. I could connect once to a bluetooth printer but subsequent connects failed with "socket closed" no matter what I did.

I found it a bit strange that the workarounds described here would be necessary. After going through my code I found that I had forgot to close the socket's InputStream and OutputSteram and not terminated the ConnectedThreads properly.

The ConnectedThread I use is the same as in the example here:

http://developer.android.com/guide/topics/connectivity/bluetooth.html

Note that ConnectThread and ConnectedThread are two different classes.

Whatever class that starts the ConnectedThread must call interrupt() and cancel() on the thread. I added mmInStream.close() and mmOutStream.close() in the ConnectedTread.cancel() method.

After closing the threads/streams/sockets properly I could create new sockets without any problem.

Daniel T
  • 91
  • 1
  • 3
  • Thanks, was having the same issue, just now I figured that I hadn't closed the stream and the connection... – Rafael May 19 '17 at 11:29
  • tried all of the above scenarios , (except for removing other paired devices causes well that really isn't a solution found that yes ... this normally only happens when the input streams and sockets don't get closed properly.....!! – Aman Satija May 14 '19 at 14:35
9

On newer versions of Android, I was receiving this error because the adapter was still discovering when I attempted to connect to the socket. Even though I called the cancelDiscovery method on the Bluetooth adapter, I had to wait until the callback to the BroadcastReceiver's onReceive() method was called with the action BluetoothAdapter.ACTION_DISCOVERY_FINISHED.

Once I waited for the adapter to stop discovery, then the connect call on the socket succeeded.

kmac.mcfarlane
  • 307
  • 2
  • 6
8

Well, I have actually found the problem.

The most people who try to make a connection using socket.Connect(); get an exception called Java.IO.IOException: read failed, socket might closed, read ret: -1.

In some cases it also depends on your Bluetooth device, because there are two different types of Bluetooth, namely BLE (low energy) and Classic.

If you want to check the type of your Bluetooth device is, here's the code:

        String checkType;
        var listDevices = BluetoothAdapter.BondedDevices;
        if (listDevices.Count > 0)
        {
            foreach (var btDevice in listDevices)
            {
                if(btDevice.Name == "MOCUTE-032_B52-CA7E")
                {
                    checkType = btDevice.Type.ToString();
                    Console.WriteLine(checkType);
                }
            }
        }

I've been trying for days to solve the problem, but since today I have found the problem. The solution from @matthes has unfortunately still a few issues as he said already, but here's my solution.

At the moment I work in Xamarin Android, but this should also work for other platforms.

SOLUTION

If there is more than one paired device, then you should remove the other paired devices. So keep only the one that you want to connect (see the right image).

enter image description here enter image description here

In the left image you see that I have two paired devices, namely "MOCUTE-032_B52-CA7E" and "Blue Easy". That's the issue, but I have no idea why that problem occurs. Maybe the Bluetooth protocol is trying to get some information from another Bluetooth device.

However, the socket.Connect(); works great right now, without any problems. So I just wanted to share this, because that error is really annoying.

Good luck!

Jamie
  • 363
  • 7
  • 19
  • This worked on the one device where I encounter this problem, which only occurs for me on a 5.0 phone, and even then only when I'm first enabling BT, then opening a connection. If BT were already on, no connection problem! It doesn't occur on devices with a later version of Android, suggesting the devs noticed the bug & fixed it, but the Android BT page breathes not a word about it. But your solution works, thanks! – John Perry Sep 08 '17 at 05:17
8

In case somebody is having issues with Kotlin, I had to follow the accepted answer with some variations:

fun print(view: View, text: String) {
    var adapter = BluetoothAdapter.getDefaultAdapter();
    var pairedDevices = adapter.getBondedDevices()
    var uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
    if (pairedDevices.size > 0) {
        for (device in pairedDevices) {
            var s = device.name
            if (device.getName().equals(printerName, ignoreCase = true)) {
                Thread {
                    var socket = device.createInsecureRfcommSocketToServiceRecord(uuid)
                    var clazz = socket.remoteDevice.javaClass
                    var paramTypes = arrayOf<Class<*>>(Integer.TYPE)
                    var m = clazz.getMethod("createRfcommSocket", *paramTypes)
                    var fallbackSocket = m.invoke(socket.remoteDevice, Integer.valueOf(1)) as BluetoothSocket
                    try {
                        fallbackSocket.connect()
                        var stream = fallbackSocket.outputStream
                        stream.write(text.toByteArray(Charset.forName("UTF-8")))
                    } catch (e: Exception) {
                        e.printStackTrace()
                        Snackbar.make(view, "An error occurred", Snackbar.LENGTH_SHORT).show()
                    }
                }.start()
            }
        }
    }
}

Hope it helps

6

You put registerReceiver(receiver, new IntentFilter("android.bleutooth.device.action.UUID")); with "bluetooth" spelled "bleutooth".

Fizz Binn
  • 115
  • 1
  • 8
4

Bluetooth devices can operate in both classic and LE mode at the same time. Sometimes they use a different MAC address depending on which way you are connecting. Calling socket.connect() is using Bluetooth Classic, so you have to make sure the device you got when you scanned was really a classic device.

It's easy to filter for only Classic devices, however:

if(BluetoothDevice.DEVICE_TYPE_LE == device.getType()){ //socket.connect() }

Without this check, it's a race condition as to whether a hybrid scan will give you the Classic device or the BLE device first. It may appear as intermittent inability to connect, or as certain devices being able to connect reliably while others seemingly never can.

kmac.mcfarlane
  • 307
  • 2
  • 6
1

i also faced with this problem,you could solve it in 2 ways , as mentioned earlier use reflection to create the socket Second one is, client is looking for a server with given UUID and if your server isn't running parallel to client then this happens. Create a server with given client UUID and then listen and accept the client from server side.It will work.

ireshika piyumalie
  • 2,226
  • 22
  • 22
1

Even i had the same problem ,finally understand my issue , i was trying to connect from (out of range) Bluetooth coverage range.

krishnamurthy
  • 1,574
  • 14
  • 14
1

I ran into this problem and fixed it by closing the input and output streams before closing the socket. Now I can disconnect and connect again with no issues.

https://stackoverflow.com/a/3039807/5688612

In Kotlin:

fun disconnect() {
    bluetoothSocket.inputStream.close()
    bluetoothSocket.outputStream.close()
    bluetoothSocket.close()
}
TheoKanning
  • 1,631
  • 11
  • 20
1

If another part of your code has already made a connection with the same socket and UUID, you get this error.

user1725145
  • 3,993
  • 2
  • 37
  • 58
0

I've had this problem and the solution was to use the special magic GUID.

            val id: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB") // Any other GUID doesn't work.
            val device: BluetoothDevice = bta!!.bondedDevices.first { z -> z.name == deviceName }

            bts = device.createRfcommSocketToServiceRecord(id) // mPort is -1
            bts?.connect()
            // Start processing thread.

I suspect that these are the UUIDs that work:

var did: Array<ParcelUuid?> = device.uuids

However, I have not tried them all.

Richard Barraclough
  • 2,625
  • 3
  • 36
  • 54
  • Even I'm using same UUID. But didn't helped me. Does this supports only for connecting to Classic bluetooth devices(not BLE)? What I have to do to connect to BLE devices using Xamarin.Forms. Posted here [https://stackoverflow.com/questions/62371859/how-to-connect-to-another-device-through-bluetooth-and-send-some-data-and-receiv] – Shailesh Bhat Jun 18 '20 at 08:56
  • I'm using classic Bluetooth; BLE is rubbish. – Richard Barraclough Jun 18 '20 at 09:00
-2

By adding filter action my problem resolved

 // Register for broadcasts when a device is discovered
    IntentFilter intentFilter = new IntentFilter();
    intentFilter.addAction(BluetoothDevice.ACTION_FOUND);
    intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED);
    intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
    registerReceiver(mReceiver, intentFilter);
Vinod Ranga
  • 511
  • 8
  • 15
-4

I have also receive the same IOException, but I find the Android system demo: "BluetoothChat" project is worked. I determined the problem is the UUID.

So i replace my UUID.fromString("00001001-0000-1000-8000-00805F9B34FB") to UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66") and it worked most scene,only sometimes need to restart the Bluetooth device;

taotao
  • 747
  • 9
  • 11