IOException: read failed, socket might closed - Bluetooth on Android 4.3

后端 未结 16 2114
天涯浪人
天涯浪人 2020-11-22 04:08

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

相关标签:
16条回答
  • 2020-11-22 04:30

    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");
            }
        }
    
    0 讨论(0)
  • 2020-11-22 04:30

    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.

    0 讨论(0)
  • 2020-11-22 04:31

    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);
            }
    
        }
    }
    
    0 讨论(0)
  • 2020-11-22 04:31

    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);
    
    0 讨论(0)
  • 2020-11-22 04:32

    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.

    0 讨论(0)
  • 2020-11-22 04:33

    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;

    0 讨论(0)
提交回复
热议问题