IOException: lecture a échoué, socket peut-fermé - Bluetooth sur Android 4.3
actuellement, je suis en train d'essayer de faire face à une Exception étrange lors de l'ouverture D'un BluetoothSocket sur mon Nexus 7 (2012), avec Android 4.3 (construire JWR66Y, je suppose la deuxième mise à jour 4.3). J'ai vu quelques articles connexes (par exemple https://stackoverflow.com/questions/13648373/bluetoothsocket-connect-throwing-exception-read-failed ), mais aucune ne semble apporter de solution à ce problème. En outre, comme suggéré dans ces fils, re-appariement n'aide pas, et en essayant constamment connect (via une boucle stupide) n'a pas non plus d'effet.
Je m'occupe d'un dispositif intégré (un adaptateur de voiture OBD-II Non nom, similaire à http://images04.olx.com/ui/15/53/76/1316534072_254254776_2-OBD-II-BLUTOOTH-ADAPTERSCLEAR-CHECK-ENGINE-LIGHTS-WITH-YOUR-PHONE-Oceanside.jpg ). Mon téléphone Android 2.3.7 n'a aucun problème de connexion, et le Xperia d'un collègue (Android 4.1.2) fonctionne également. Un autre Google Nexus (Je ne sais pas si "un" ou "S", mais non '4') échoue également avec Android 4.3.
Voici l'Extrait de l'établissement de la connexion. Il tourne dans son propre fil, créé au sein d'un 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;
}
}
le code échoue à bluetoothSocket.connect()
. Je reçois un java.io.IOException: read failed, socket might closed, read ret: -1
. C'est la source correspondante à GitHub: https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothSocket.java#L504
Son appelé par readInt (), appelé de https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothSocket.java#L319
un dump de métadonnées de la socket utilisée a donné les informations suivantes. Ce sont exactement les mêmes sur Nexus 7 et mon téléphone 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
j'ai d'autres adaptateurs OBD-II (Plus expansifs) et ils fonctionnent tous. Est-ce qu'il y a une chance que je manque quelque chose ou ça pourrait être un bug dans Androïde?
12 réponses
j'ai enfin trouvé une solution. La magie est cachée sous le capot de la classe BluetoothDevice
(voir https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothDevice.java#L1037 ).
Maintenant, quand je reçois cette exception, j'instancie un repli BluetoothSocket
, similaire au code source ci-dessous. Comme vous pouvez le voir, invoquer la méthode cachée createRfcommSocket
via des réflexions. Je n'ai aucune idée pourquoi cette méthode est caché. Le code source le définit comme public
cependant...
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()
alors n'échoue plus. J'ai connu quelques problèmes encore. Fondamentalement, cela bloque parfois et échoue. Dans de tels cas, redémarrer le dispositif SPP (plug off / plug in) AIDE. Parfois, j'obtiens aussi une autre requête de couplage après connect()
même si le dispositif est déjà lié.
mise à jour:
voici un classe complète, contenant quelques classes imbriquées. pour une mise en œuvre réelle ceux-ci pourraient être tenus comme des classes séparées.
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);
}
}
}
eh bien, j'ai eu le même problème avec mon code, et c'est parce que depuis android 4.2 bluetooth stack a changé. donc mon code fonctionnait très bien sur les appareils avec android < 4.2 , sur les autres appareils j'ai obtenu la célèbre exception "lire échoué, socket pourrait fermé ou le temps mort, lire ret: -1 "
le problème est avec le paramètre socket.mPort
. Lorsque vous créez votre socket en utilisant socket = device.createRfcommSocketToServiceRecord(SERIAL_UUID);
, le mPort
obtient la valeur entière " -1 ", et cette valeur semble ne pas fonctionner pour android > = 4.2 , donc vous devez le définir à " 1 ". La mauvaise nouvelle est que createRfcommSocketToServiceRecord
n'accepte que UUID comme paramètre et pas mPort
donc nous devons utiliser d'autres aproach. La réponse Postée par @matthes a également fonctionné pour moi, mais je l'ai simplifiée: socket =(BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device,1);
. Nous devons utiliser les deux attributs de socket , le second comme un repli.
ainsi le code est (pour connexion à un SPP sur un ELM327 appareil):
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");
}
}
D'abord, si vous avez besoin de parler à un bluetooth 2.x l'appareil, 151950920" cette documentation stipule que :
indice: si vous vous connectez à une carte série Bluetooth, essayez d'utiliser le bien connu SPP UUID 00001101-0000-1000-8000-00805F9B34FB . Cependant si vous vous connectez à un pair Android alors s'il vous plaît générer votre propre unique UUID.
Je ne pensais pas qu'il fonctionnerait, mais seulement en remplaçant L'UUID par 00001101-0000-1000-8000-00805F9B34FB
il fonctionne. Cependant, ce code semble traiter le problème de la version SDK, et vous pouvez simplement remplacer la fonction device.createRfcommSocketToServiceRecord(mMyUuid);
par tmp = createBluetoothSocket(mmDevice);
après avoir défini la méthode suivante:
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);
}
le code source n'est pas le mien, mais vient de ce site web .
vous avez mis
registerReceiver(receiver, new IntentFilter("android.bleutooth.device.action.UUID"));
avec "bluetooth" épelé "bleutooth".
en fait, j'ai trouvé le problème.
la plupart des gens qui essaient de faire une connexion en utilisant socket.Connect();
obtiennent une exception appelée Java.IO.IOException: read failed, socket might closed, read ret: -1
.
dans certains cas, il dépend également de votre appareil Bluetooth, parce qu'il existe deux types différents de Bluetooth, à savoir blé (basse énergie) et classique.
Si vous voulez vérifier le type de votre appareil Bluetooth est, voici le 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);
}
}
}
j'ai essayé pendant des jours pour résoudre le problème, mais depuis aujourd'hui, j'ai trouvé le problème. La solution de @matthes a malheureusement encore quelques problèmes comme il l'a dit déjà, mais voici ma solution.
en ce moment je travaille dans Xamarin Android, mais cela devrait également fonctionner pour d'autres plates-formes.
SOLUTION
S'il y a plus d'un dispositif apparié, alors vous devez supprimer l'autre dispositif apparié appareil. Gardez donc seulement celui que vous voulez connecter (voir la bonne image).
dans l'image de gauche, vous voyez que j'ai deux appareils appariés, à savoir" MOCUTE-032_B52-CA7E "et"Blue Easy". C'est le problème, mais je ne sais pas pourquoi ce problème se produit. Peut-être que le Protocole Bluetooth essaye d'obtenir des informations d'un autre Bluetooth appareil.
cependant, le socket.Connect();
fonctionne bien maintenant, sans aucun problème. Donc je voulais juste partager ça, parce que cette erreur est vraiment ennuyante.
bonne chance!
j'ai eu les mêmes symptômes que décrit ici. J'ai pu me connecter une fois à une imprimante bluetooth mais les connexions subséquentes ont échoué avec "socket closed" peu importe ce que j'ai fait.
j'ai trouvé un peu étrange que les solutions de rechange décrites ici soient nécessaires. Après avoir passé en revue mon code, j'ai découvert que j'avais oublié de fermer L'entrée et la sortie de la socketsteram et que je n'avais pas terminé les ConnectedThreads correctement.
le fil de connexion que j'utilise est le comme dans l'exemple suivant:
http://developer.android.com/guide/topics/connectivity/bluetooth.html
Notez que ConnectThread et ConnectedThread sont deux classes différentes.
quelle que soit la classe qui démarre le ConnectedThread doit appeler interrompre() et Annuler() sur le thread. J'ai ajouté mmInStream.close () et mmOutStream.close () dans la tête ConnectedTread.la méthode cancel ().
après fermer les threads/streams/sockets correctement je pourrais créer de nouvelles sockets sans aucun problème.
sur les nouvelles versions D'Android, je recevais cette erreur parce que l'adaptateur était encore en train de découvrir quand j'ai essayé de me connecter à la socket. Même si j'ai appelé la méthode cancelDiscovery sur L'adaptateur Bluetooth, j'ai dû attendre jusqu'à ce que le rappel à la méthode Onreceive() de L'émetteur soit appelé avec L'action BluetoothAdapter.ACTION_DISCOVERY_FINISHED.
une fois que j'ai attendu que l'adaptateur arrête la découverte, puis l'appel de connexion sur la prise réussir.
dans le cas où quelqu'un a des problèmes avec Kotlin, j'ai dû suivre la réponse acceptée avec quelques 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()
}
}
}
}
j'Espère que ça aide
j'ai également fait face à ce problème, vous pourriez le résoudre de deux façons , comme mentionné précédemment utiliser la réflexion pour créer le socket Deuxième est, le client est à la recherche d'un serveur avec UUID donné et si votre serveur n'est pas parallèle au client, cela se produit. Créer un serveur avec un UUID client donné, puis écouter et accepter le client du côté du serveur.Ça va marcher.
peuvent fonctionner à la fois en mode classique et en mode LE. Parfois, ils utilisent une adresse MAC différente en fonction de la façon dont vous vous connectez. Appeler socket.connect()
utilise Bluetooth Classic, donc vous devez vous assurer que l'appareil que vous avez obtenu lorsque vous avez scanné était vraiment un appareil classique.
il est facile de filtrer pour seulement les appareils classiques, cependant:
if(BluetoothDevice.DEVICE_TYPE_LE == device.getType()){
//socket.connect()
}
sans ce chèque, c'est une course condition à savoir si un scan hybride vous donnera l'appareil classique ou L'appareil BLE en premier. Elle peut apparaître comme une incapacité intermittente à se connecter, ou comme certains appareils pouvant se connecter de manière fiable alors que d'autres apparemment ne le peuvent jamais.
même si j'ai eu le même problème ,finalement comprendre mon problème , j'ai essayé de me connecter à partir (hors de portée) gamme de couverture Bluetooth.
j'ai également reçu le même IOException
, mais je trouve la démo du système Android:" BluetoothChat " projet est travaillé. J'ai déterminé que le problème est L'UUID.
donc je remplace mon UUID.fromString("00001001-0000-1000-8000-00805F9B34FB")
par UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66")
et il a fonctionné la plupart de la scène,seulement parfois besoin de redémarrer le dispositif Bluetooth;