IOException: ошибка чтения, сокет может быть закрыт-Bluetooth на Android 4.3

В настоящее время я пытаюсь справиться со странным исключением при открытии BluetoothSocket на моем Nexus 7 (2012), с Android 4.3 (Build JWR66Y, я думаю, второе обновление 4.3). Я видел некоторые связанные публикации (например,https://stackoverflow.com/questions/13648373/bluetoothsocket-connect-throwing-exception-read-failed), но ни один, похоже, не обеспечивает обходной путь для этой проблемы. Кроме того, как предлагается в этих потоках, повторное сопряжение не помогает и постоянно пытается подключиться (через глупую петлю) также не имеет никакого эффекта.

Я имею дело со встроенным устройством (автомобильный адаптер noname OBD-II, похожий на http://images04.olx.com/ui/15/53/76/1316534072_254254776_2-OBD-II-BLUTOOTH-ADAPTERSCLEAR-CHECK-ENGINE-LIGHTS-WITH-YOUR-PHONE-Oceanside.jpg) - ... Мой телефон Android 2.3.7 не имеет никаких проблем с подключением, и Xperia коллеги (Android 4.1.2) также работает. Другой Google Nexus (я не знаю, "один" или "S", но не "4") также терпит неудачу Android 4.3.

вот фрагмент установки соединения. Он работает в своем собственном потоке, созданном внутри службы.

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;
    }

}

код не работает на bluetoothSocket.connect(). Я получаю java.io.IOException: read failed, socket might closed, read ret: -1. Это соответствующий источник в GitHub: https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothSocket.java#L504 Его называют через readInt(), вызывается из https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothSocket.java#L319

некоторый дамп метаданных используемого сокета привел к следующей информации. Они точно такие же на Nexus 7 и моем телефоне 2.3.7.

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

у меня есть несколько других БД-II адаптеров (более expansives) и все они работают. Есть ли шанс, что я что-то упускаю или это может быть ошибка в Android?

12 ответов


я, наконец, нашел обходной путь. Магия скрыта под капюшоном BluetoothDevice класс (см. https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothDevice.java#L1037).

теперь, когда я получаю это исключение, я создаю запасной вариант BluetoothSocket, похож на исходный код ниже. Как вы можете видеть, вызывая скрытый метод createRfcommSocket через размышления. Я понятия не имею, почему этот метод скрыт. Источник код определяет его как public хотя...

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() тогда больше не терпит неудачу. У меня все еще есть несколько проблем. В основном, это иногда блокирует и терпит неудачу. В таких случаях помогает перезагрузка SPP-устройства (plug off / plug in). Иногда я также получаю еще один запрос на сопряжение после connect() даже когда прибор уже скреплен.

обновление:

вот полный класс, содержащий некоторые вложенные классы. для реальной реализации это может быть проводятся как отдельные занятия.

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);
        }

    }
}

ну, у меня была такая же проблема с моим кодом, и это потому, что с android 4.2 Bluetooth stack изменился. таким образом, мой код работал нормально на устройствах с android "чтение не удалось, сокет может быть закрыт или тайм-аут, читать ret: -1"

проблема заключается в


во-первых, если вам нужно поговорить с Bluetooth 2.устройство X, документация гласит :

подсказка: если вы подключаетесь к последовательной плате Bluetooth, попробуйте использовать the известный SPP UUID 00001101-0000-1000-8000-00805F9B34FB. Однако если вы подключаетесь к Android peer, пожалуйста, создайте свой собственный уникальный идентификатор UUID.

Я не думал, что это сработает, но только путем замены UUID на 00001101-0000-1000-8000-00805F9B34FB это работает. Однако этот код, похоже, справляется с проблемой версии SDK, и вы можете просто заменить функцию device.createRfcommSocketToServiceRecord(mMyUuid); С tmp = createBluetoothSocket(mmDevice); после определения следующего метода:

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);
}

исходный код не мой, но этот сайт.


вы положили registerReceiver(receiver, new IntentFilter("android.bleutooth.device.action.UUID")); с " bluetooth "пишется"bleutooth".


Ну, я действительно нашел проблему.

большинство людей, которые пытаются установить соединение с использованием socket.Connect(); сделать исключение по имени Java.IO.IOException: read failed, socket might closed, read ret: -1.

в некоторых случаях это также зависит от вашего устройства Bluetooth, потому что есть два разных типа Bluetooth, а именно BLE (низкая энергия) и Classic.

если вы хотите проверить тип вашего устройства Bluetooth, вот код:

        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);
                }
            }
        }

Я пытался в течение нескольких дней, чтобы решить эту проблему, но с сегодняшнего дня я нашел проблему. Решение от @Маттес, к сожалению, еще несколько вопросов, как он уже сказал, но вот мое решение.

на данный момент я работаю в Xamarin для Android, но это также должно работать на других платформах.

решение

если есть более одного сопряженного устройства, то вы должны удалить другие сопряженные устройства. Поэтому держите только тот, который вы хотите подключить (см. Справа изображение.)

enter image description here enter image description here

на левом изображении вы видите, что у меня есть два сопряженных устройства, а именно "MOCUTE-032_B52-CA7E" и "Blue Easy". В этом проблема, но я понятия не имею, почему эта проблема возникает. Возможно, протокол Bluetooth пытается получить некоторую информацию с другого устройства Bluetooth.

на socket.Connect(); работает отлично, без каких-либо проблем. Поэтому я просто хотел поделиться этим, потому что эта ошибка очень раздражает.

удачи!


У меня были те же симптомы, что описаны здесь. Я мог бы подключиться один раз к принтеру bluetooth, но последующие соединения не удалось с "сокетом закрыт" независимо от того, что я сделал.

Мне показалось немного странным, что описанные здесь обходные пути будут необходимы. После просмотра моего кода я обнаружил, что забыл закрыть InputStream и OutputSteram сокета и не закончил ConnectedThreads должным образом.

ConnectedThread, который я использую, такой же, как в Примере здесь:

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

обратите внимание, что ConnectThread и ConnectedThread-это два разных класса.

любой класс, который запускает ConnectedThread, должен вызвать interrupt() и cancel () в потоке. Я добавил mmInStream.закрыть () и mmOutStream.закрыть () в ConnectedTread.cancel() метод.

после закрытия потоков / потоков / сокетов должным образом я мог бы создать новые сокеты без проблем.


в более новых версиях Android я получал эту ошибку, потому что адаптер все еще обнаруживал, когда я пытался подключиться к сокету. Несмотря на то, что я вызвал метод cancelDiscovery на адаптере Bluetooth, мне пришлось ждать, пока обратный вызов метода onReceive() BroadcastReceiver был вызван с помощью действия BluetoothAdapter.ACTION_DISCOVERY_FINISHED.

Как только я ждал, что адаптер остановит обнаружение, то вызов connect на сокете завершился успешно.


в случае, если у кого-то возникли проблемы с Котлином, я должен был следовать принятому ответу с некоторыми вариациями:

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()
            }
        }
    }
}

надеюсь, что это помогает


Я также столкнулся с этой проблемой, вы можете решить ее двумя способами , как упоминалось ранее, используйте отражение для создания сокета Второй, клиент ищет сервер с заданным UUID, и если ваш сервер не работает параллельно клиенту, то это происходит. Создайте сервер с заданным UUID клиента, а затем прослушайте и примите клиент со стороны сервера.Это сработает.


устройства Bluetooth могут работать как в классическом, так и в режиме LE одновременно. Иногда они используют другой MAC-адрес в зависимости от того, каким образом вы подключаетесь. Зову socket.connect() использует Bluetooth Classic, поэтому вы должны убедиться, что устройство, которое вы получили при сканировании, было действительно классическим устройством.

легко фильтровать только для классических устройств, однако:

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

без этой проверки, это гонки, как гибридное сканирование сначала дайте вам классическое устройство или устройство BLE. Это может выглядеть как прерывистая неспособность подключиться или как некоторые устройства, способные надежно подключаться, в то время как другие, похоже, никогда не смогут.


даже у меня была такая же проблема ,наконец, понять мою проблему , я пытался подключиться с (вне диапазона) диапазон действия Bluetooth.


Я также получаю то же самое IOException, но я нахожу демо-версию системы Android: проект" BluetoothChat " работает. Я решил проблему является UUID.

поэтому я заменяю свой UUID.fromString("00001001-0000-1000-8000-00805F9B34FB") to UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66") и он работал в большинстве сцен, только иногда нужно перезапустить устройство Bluetooth;