IOException: odczyt nieudany, gniazdo mogło zostać zamknięte - Bluetooth w systemie Android 4.3

100

Obecnie próbuję poradzić sobie z dziwnym Wyjątkiem podczas otwierania BluetoothSocket na moim Nexusie 7 (2012), z Androidem 4.3 (kompilacja JWR66Y, myślę, że druga aktualizacja 4.3). Widziałem kilka pokrewnych postów (np. Https://stackoverflow.com/questions/13648373/bluetoothsocket-connect-throwing-exception-read-failed ), ale żadna z nich nie zapewnia obejścia tego problemu. Ponadto, jak sugerowano w tych wątkach, ponowne parowanie nie pomaga, a ciągłe próby połączenia (przez głupią pętlę) również nie przynoszą efektu.

Mam do czynienia z urządzeniem wbudowanym (adapter samochodowy noname OBD-II, podobny do http://images04.olx.com/ui/15/53/76/1316534072_254254776_2-OBD-II-BLUTOOTH-ADAPTERSCLEAR-CHECK-ENGINE- ŚWIATŁA-Z-TWOIM-TELEFONEM-Oceanside.jpg ). Mój telefon z Androidem 2.3.7 nie ma żadnych problemów z połączeniem, działa też Xperia kolegi (Android 4.1.2). Inny Google Nexus (nie wiem, czy „Jeden” czy „S”, ale nie „4”) również nie działa z Androidem 4.3.

Oto fragment zestawienia połączenia. Działa we własnym wątku utworzonym w ramach usługi.

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

}

Kod nie działa pod adresem bluetoothSocket.connect(). Dostaję java.io.IOException: read failed, socket might closed, read ret: -1. To jest odpowiednie źródło w GitHub: https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothSocket.java#L504 Jest wywoływany przez readInt (), wywoływany z https : //github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothSocket.java#L319

Niektóre zrzuty metadanych używanego gniazda zwróciły następujące informacje. Są dokładnie takie same na Nexusie 7 i moim telefonie 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

Mam kilka innych adapterów OBD-II (więcej ekspansji) i wszystkie działają. Czy jest szansa, że ​​czegoś mi brakuje lub może to być błąd w Androidzie?

Matthes
źródło
Wypróbowałem powyższe rozwiązanie, mogę pomóc w tej sprawie stackoverflow.com/q/52105647/1559331
dileepVikram

Odpowiedzi:

129

W końcu znalazłem obejście. Magia jest ukryta pod maską BluetoothDeviceklasy (patrz https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothDevice.java#L1037 ).

Teraz, kiedy otrzymam ten wyjątek, tworzę instancję rezerwową BluetoothSocket, podobną do kodu źródłowego poniżej. Jak widać, przywołanie metody ukrytej createRfcommSocketza pomocą odbić. Nie mam pojęcia, dlaczego ta metoda jest ukryta. Kod źródłowy definiuje to tak, publicjakby ...

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()to już nie zawodzi. Wciąż napotkałem kilka problemów. Zasadniczo to czasami blokuje i kończy się niepowodzeniem. W takich przypadkach pomaga ponowne uruchomienie urządzenia SPP (podłączenie / wyłączenie). Czasami otrzymuję również kolejną prośbę o parowanie, connect()nawet gdy urządzenie jest już połączone.

AKTUALIZACJA:

tutaj jest pełna klasa, zawierająca kilka zagnieżdżonych klas. dla rzeczywistej implementacji mogłyby się one odbywać jako oddzielne klasy.

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
źródło
3
Łał! świetne rozwiązanie!
Bobs
2
@MD Nie wiem jak. Właśnie przetestowałem i stwierdziłem, że działa.
Bobs
1
To nie działa na Nexusie 4. Czy możesz powiedzieć, jak rozwiązać ten problem. Prawie próbowałem wszystkiego. Dzięki.
Shah
3
@matthes Przykro mi to mówić, ale nawet twoje rozwiązanie polegające na użyciu rozwiązania zastępczego nie rozwiązało mojego problemu. Zejście poniżej Błąd. Fallback failed. Cancelling. java.io.IOException: Connection refused Proszę pomóż.
Tushar Banne
2
@matthes "SPP-Device (podłącz / podłącz) pomaga w takich przypadkach." Deklaracja włączania / wyłączania jest najbardziej niedoceniana na świecie. Właśnie zmarnowałem 3 godziny i wszystko, co musiałem zrobić, to włączyć i wyłączyć -_-
Adz
98

cóż, miałem ten sam problem z moim kodem, a to dlatego, że od czasu zmiany stosu bluetooth Androida 4.2. więc mój kod działał poprawnie na urządzeniach z Androidem <4.2, na innych urządzeniach występował słynny wyjątek „błąd odczytu, gniazdo mogło zostać zamknięte lub przekroczono limit czasu, odczyt ret: -1”

Problem dotyczy socket.mPortparametru. Kiedy tworzysz swoje gniazdo za pomocą socket = device.createRfcommSocketToServiceRecord(SERIAL_UUID);, mPortotrzymujesz wartość całkowitą " -1 ", a ta wartość wydaje się nie działać dla Androida> = 4.2, więc musisz ustawić ją na " 1 ". Zła wiadomość jest taka, że createRfcommSocketToServiceRecordakceptuje tylko UUID jako parametr, a mPortwięc nie musimy używać innego podejścia. Odpowiedź wysłane przez @matthes również pracował dla mnie, ale uproszczone go: socket =(BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device,1);. Musimy użyć obu atrybutów gniazda, drugiego jako rezerwy.

Więc kod to (do połączenia z SPP na urządzeniu ELM327):

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
źródło
57
dla moderatorów: ponieważ bez powodu usunąłeś moją poprzednią odpowiedź, zamieszczam ją ponownie.
George Dima,
2
dzięki George za wgląd w mPortparametr! imho przepływ pracy pozostaje taki sam, po prostu opakowałem rzeczy niektórymi klasami implementującymi interfejs.
Matthes
5
tak, już powiedziałem, że twoje rozwiązanie jest dobre, ale chciałem, aby ludzie zrozumieli, dlaczego musi korzystać z tego podejścia, począwszy od Androida 4.2
George Dima
2
SPP = profil portu szeregowego (który emuluje port szeregowy przez bluetooth), a ELM327 to urządzenie samochodowe bluetooth <-> obd, wygoogluj to.
George Dima
10
U mnie zadziałało po zmianie wartości portu z 1 na 2, spójrz na ten kod. gniazdo = (BluetoothSocket) urządzenie.getClass (). getMethod ("createRfcommSocket", nowa klasa [] {int.class}). invoke (urządzenie, 2); socket.connect ();
Dinesh IT,
16

Po pierwsze, jeśli chcesz rozmawiać z urządzeniem Bluetooth 2.x, w tej dokumentacji stwierdza się, że:

Wskazówka: Jeśli łączysz się z kartą szeregową Bluetooth, spróbuj użyć dobrze znanego SPP UUID 00001101-0000-1000-8000-00805F9B34FB . Jeśli jednak łączysz się z partnerem z systemem Android, wygeneruj swój własny unikalny identyfikator UUID.

Nie sądziłem, że to zadziała, ale działa tylko zastępując UUID 00001101-0000-1000-8000-00805F9B34FB. Jednak ten kod zdaje sobie z problemem wersji SDK, i można po prostu zastąpić funkcję device.createRfcommSocketToServiceRecord(mMyUuid);z tmp = createBluetoothSocket(mmDevice);po zdefiniowaniu w następujący sposób:

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

Kod źródłowy nie jest mój, ale pochodzi z tej witryny .

tobiasBora
źródło
1
To rozwiązało prawie 2 dni pracy ... wdzięczne westchnienie ... Bez tego UUID gniazdo zamknęłoby się natychmiast i uległo awarii bez dalszych wyjaśnień.
David Sinclair
bardzo dziękuję za pomoc MÓJ UUID jest alfanumeryczny i próbuję połączyć HC-5 i mój bluetooth, pokazując, że połączenie nie powiodło się, ale kiedy skorzystałem z tego rozwiązania, mój problem został rozwiązany. jeszcze raz dziękuję
Nikhil Shende
8

Miałem takie same objawy, jak opisane tutaj. Mogłem raz połączyć się z drukarką bluetooth, ale kolejne połączenia kończyły się niepowodzeniem z powodu „zamkniętego gniazda” bez względu na to, co zrobiłem.

Wydało mi się trochę dziwne, że opisane tutaj obejścia byłyby konieczne. Po przejrzeniu mojego kodu stwierdziłem, że zapomniałem zamknąć InputStream i OutputSteram gniazda i nie zakończyłem poprawnie ConnectedThreads.

ConnectedThread, którego używam, jest taki sam jak w przykładzie tutaj:

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

Zwróć uwagę, że ConnectThread i ConnectedThread to dwie różne klasy.

Każda klasa, która uruchamia ConnectedThread, musi wywołać przerwanie () i anulowanie () w wątku. Dodałem mmInStream.close () i mmOutStream.close () w metodzie ConnectedTread.cancel ().

Po poprawnym zamknięciu wątków / strumieni / gniazd mogłem bez problemu tworzyć nowe gniazda.

Daniel T
źródło
Dzięki, miałem ten sam problem, właśnie teraz pomyślałem, że nie zamknąłem strumienia i połączenia ...
Rafael
wypróbowałem wszystkie powyższe scenariusze (z wyjątkiem usunięcia innych sparowanych urządzeń powoduje, że to naprawdę nie jest rozwiązanie, które okazało się, że tak ... dzieje się to zwykle tylko wtedy, gdy strumienie wejściowe i gniazda nie zamykają się prawidłowo ..... !!
Aman Satija
7

Cóż, faktycznie znalazłem problem.

Większość osób, które próbują nawiązać połączenie za pomocą protokołu, socket.Connect();otrzymuje wyjątek o nazwie Java.IO.IOException: read failed, socket might closed, read ret: -1.

W niektórych przypadkach zależy to również od urządzenia Bluetooth, ponieważ istnieją dwa różne typy Bluetooth, a mianowicie BLE (low energy) i Classic.

Jeśli chcesz sprawdzić typ swojego urządzenia Bluetooth, oto kod:

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

Od dni próbuję rozwiązać ten problem, ale od dzisiaj go znalazłem. Rozwiązanie z @matthes ma niestety wciąż kilka problemów, jak już powiedział, ale oto moje rozwiązanie.

W tej chwili pracuję w Xamarin Android, ale powinno to zadziałać również na innych platformach.

ROZWIĄZANIE

Jeśli jest więcej niż jedno sparowane urządzenie, należy usunąć pozostałe sparowane urządzenia. Więc zachowaj tylko ten, który chcesz podłączyć (patrz prawy obraz).

wprowadź opis obrazu tutaj wprowadź opis obrazu tutaj

Na lewym obrazku widać, że mam dwa sparowane urządzenia, a mianowicie „MOCUTE-032_B52-CA7E” i „Blue Easy”. To jest problem, ale nie mam pojęcia, dlaczego ten problem występuje. Może protokół Bluetooth próbuje uzyskać jakieś informacje z innego urządzenia Bluetooth.

Jednak teraz socket.Connect();działa świetnie, bez żadnych problemów. Chciałem się tym tylko podzielić, ponieważ ten błąd jest naprawdę irytujący.

Powodzenia!

Jamie
źródło
To działało na jednym urządzeniu, na którym napotykam ten problem, który występuje tylko u mnie na telefonie 5.0, a nawet wtedy tylko wtedy, gdy najpierw włączam BT, a następnie otwieram połączenie. Jeśli BT było już włączone, nie ma problemu z połączeniem! Nie występuje na urządzeniach z nowszą wersją Androida, co sugeruje, że twórcy zauważyli błąd i naprawili go, ale strona Android BT nie mówi o tym ani słowa. Ale twoje rozwiązanie działa, dzięki!
John Perry,
7

W nowszych wersjach Androida otrzymywałem ten błąd, ponieważ adapter nadal wykrywał, gdy próbowałem połączyć się z gniazdem. Mimo że wywołałem metodę cancelDiscovery na karcie Bluetooth, musiałem poczekać, aż wywołanie zwrotne metody onReceive () BroadcastReceiver z akcją BluetoothAdapter.ACTION_DISCOVERY_FINISHED.

Gdy czekałem, aż adapter zakończy wykrywanie, wywołanie connect w gnieździe powiodło się.

kmac.mcfarlane
źródło
6

Można umieścić registerReceiver(receiver, new IntentFilter("android.bleutooth.device.action.UUID")); z „Bluetooth” pisane „bleutooth”.

Fizz Binn
źródło
4

W przypadku, gdy ktoś ma problemy z Kotlinem, musiałem postępować zgodnie z zaakceptowaną odpowiedzią z kilkoma wariacjami:

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

Mam nadzieję, że to pomoże

Santiago Martí Olbrich
źródło
3

Urządzenia Bluetooth mogą działać jednocześnie w trybie klasycznym i LE. Czasami używają innego adresu MAC w zależności od tego, w jaki sposób się łączysz. Dzwonienie socket.connect()odbywa się za pomocą Bluetooth Classic, więc musisz upewnić się, że urządzenie, które otrzymałeś podczas skanowania, było naprawdę klasycznym urządzeniem.

Łatwo jest jednak filtrować tylko dla urządzeń Classic, jednak:

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

Bez tego sprawdzenia jest to warunek wyścigu, czy skanowanie hybrydowe da najpierw urządzenie Classic, czy BLE. Może się to wydawać jako sporadyczna niemożność połączenia lub jako że niektóre urządzenia są w stanie połączyć się niezawodnie, podczas gdy inne pozornie nigdy nie mogą.

kmac.mcfarlane
źródło
1

też napotkałem ten problem, możesz go rozwiązać na dwa sposoby, jak wspomniano wcześniej użyj refleksji do stworzenia gniazda Po drugie, klient szuka serwera o podanym UUID i jeśli twój serwer nie działa równolegle do klienta, to dzieje się. Stwórz serwer o podanym UUID klienta, a następnie nasłuchuj i zaakceptuj klienta od strony serwera.

ireshika piyumalie
źródło
1

Natknąłem się na ten problem i naprawiłem go, zamykając strumienie wejściowe i wyjściowe przed zamknięciem gniazda. Teraz mogę się rozłączyć i połączyć ponownie bez żadnych problemów.

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

W Kotlinie:

fun disconnect() {
    bluetoothSocket.inputStream.close()
    bluetoothSocket.outputStream.close()
    bluetoothSocket.close()
}
TheoKanning
źródło
1

Jeśli inna część twojego kodu już nawiązała połączenie z tym samym gniazdem i UUID, pojawi się ten błąd.

user1725145
źródło
0

Nawet ja miałem ten sam problem, w końcu zrozumiałem mój problem, próbowałem połączyć się z (poza zasięgiem) zasięgu zasięgu Bluetooth.

krishnamurthy
źródło
0

Miałem ten problem i rozwiązaniem było użycie specjalnego magicznego identyfikatora 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.

Podejrzewam, że są to UUID, które działają:

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

Jednak nie wypróbowałem ich wszystkich.

Richard Barraclough
źródło
Nawet ja używam tego samego UUID. Ale mi nie pomogło. Czy obsługuje to tylko łączenie się z klasycznymi urządzeniami Bluetooth (nie BLE)? Co muszę zrobić, aby połączyć się z urządzeniami BLE za pomocą platformy Xamarin.Forms. Opublikowane tutaj [ stackoverflow.com/questions/62371859/…
Shailesh Bhat
Używam klasycznego Bluetooth; BLE to śmieci.
Richard Barraclough
-1

Po dodaniu akcji filtrowania mój problem został rozwiązany

 // 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
źródło
-3

Otrzymałem również to samo IOException , ale znalazłem demo systemu Android: projekt "BluetoothChat" działa. Ustaliłem, że problemem jest UUID.

Więc zamieniam UUID.fromString("00001001-0000-1000-8000-00805F9B34FB")na UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66")i działało w większości scen, tylko czasami trzeba ponownie uruchomić urządzenie Bluetooth;

taotao
źródło