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

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

12 77

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 стек изменился. так что мой код работал нормально на устройствах с android "чтение не удалось, сокет может быть закрыт или тайм-аут, чтение ret: -1"

проблема с

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

подсказка: если вы подключаетесь к последовательной плате Bluetooth, попробуйте использовать элемент известный 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 (low energy) и 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 клиента, а затем прослушайте и примите клиент с сервера side.It будет работать.

устройства 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;