IOException:读取失败,套接字可能closures – Android 4.3上的蓝牙

目前我正在尝试处理一个奇怪的exception,当我的Nexus 7(2012),与Android 4.3(构buildJWR66Y,我猜是第二个4.3更新)打开一个BluetoothSocket。 我见过一些相关的post(例如https://stackoverflow.com/questions/13648373/bluetoothsocket-connect-throwing-exception-read-failed ),但似乎没有提供解决此问题的方法。 此外,正如在这些线程中所build议的,重新配对没有帮助,并且不断尝试连接(通过一个愚蠢的循环)也没有效果。

我正在处理一个embedded式设备(一个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手机没有任何连接问题,同事(Android 4.1.2)的Xperia也可以。 Android的另一个谷歌Nexus(我不知道是“一个”还是“S”,但不是“4”)也失败了。

这是连接build立的片段。 它在自己的线程中运行,在服务中创build。

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的错误?

我终于find了一个解决方法。 魔法隐藏在BluetoothDevice类的引擎之下(请参阅https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothDevice.java#L1037 )。

现在,当我收到这个exception时,我实例化了一个类似于下面源代码的后备式BluetoothSocket套接字。 正如你所看到的,通过reflection调用隐藏的方法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设备(插拔/插入)有帮助。 有时候我也会在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蓝牙堆栈已经改变。 所以我的代码运行良好的设备与Android <4.2,在其他设备上,我得到着名的exception“读取失败,套接字可能closures或超时,读取ret:-1”

问题是与socket.mPort参数。 当你使用socket = device.createRfcommSocketToServiceRecord(SERIAL_UUID);创build套接字时socket = device.createRfcommSocketToServiceRecord(SERIAL_UUID);mPort获得整数值“ -1 ”,这个值似乎不适用于android> = 4.2,所以你需要将它设置为“ 1 ”。 坏消息是, createRfcommSocketToServiceRecord只接受UUID作为参数,而不是mPort所以我们必须使用其他的方法。 这个@matthes发布的答案也适用于我,但我简化了它: socket =(BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device,1); 。 我们需要使用套接字attribs,第二个作为后备。

所以代码是(用于连接到ELM327设备上的SPP):

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

首先,如果您需要与蓝牙2.x设备通话,则此文档指出:

提示:如果要连接到蓝牙串行板,请尝试使用着名的SPP UUID 00001101-0000-1000-8000-00805F9B34FB 。 但是,如果您连接到Android对等,那么请生成您自己的唯一UUID。

我不认为这是可行的,但只有通过replaceUUID 00001101-0000-1000-8000-00805F9B34FB它的工作。 但是,这段代码似乎处理了SDK版本的问题,您可以直接replace函数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")); 用“蓝牙”拼写“bleutooth”。

我有这里描述的相同的症状。 我可以连接一次到蓝牙打印机,但随后连接失败,“套接字closures”无论我做了什么。

我发现这里描述的解决方法是必要的,这有点奇怪。 通过我的代码后,我发现我忘了closures套接字的InputStream和OutputSteram,并没有正确地终止ConnectedThreads。

我使用的ConnectedThread和这里的例子是一样的:

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

请注意,ConnectThread和ConnectedThread是两个不同的类。

无论启动ConnectedThread的任何类必须调用线程上的interrupt()和cancel()。 我在ConnectedTread.cancel()方法中添加了mmInStream.close()和mmOutStream.close()。

正确closures线程/stream/套接字后,我可以创build新的套接字,没有任何问题。

那么,我已经发现了这个问题。

大多数尝试使用socket.Connect();进行连接的人socket.Connect(); 得到一个名为Java.IO.IOException: read failed, socket might closed, read ret: -1的exceptionJava.IO.IOException: read failed, socket might closed, read ret: -1

在某些情况下,它也取决于您的蓝牙设备,因为有两种不同types的蓝牙,即BLE(低能耗)和Classic。

如果你想检查你的蓝牙设备的types是,这里是代码:

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

我一直在努力解决这个问题,但从今天起,我发现了这个问题。 从@matthes的解决scheme,不幸的是,他已经说了几个问题,但这是我的解决scheme。

目前我在Xamarin Android工作,但这也应该为其他平台工作。

如果有多个配对设备,则应删除其他配对设备。 所以只保留一个你想连接(见右图)。

在这里输入图像描述 在这里输入图像描述

在左图中,您看到我有两个配对设备,即“MOCUTE-032_B52-CA7E”和“Blue Easy”。 这是问题,但我不知道为什么会出现这个问题。 也许蓝牙协议试图从另一个蓝牙设备获得一些信息。

但是, socket.Connect(); 现在工作很好,没有任何问题。 所以我只是想分享一下,因为那个错误真的很烦人。

祝你好运!

在较新版本的Android上,我收到了这个错误,因为适配器在我尝试连接到套接字时仍然在发现。 尽pipe我在蓝牙适配器上调用了cancelDiscovery方法,但我必须等到BroadcastReceiver的onReceive()方法的callback调用BluetoothAdapter.ACTION_DISCOVERY_FINISHED。

一旦我等待适配器停止发现,则套接字上的连接调用成功。

我也面临这个问题,你可以用两种方法解决它,如前面提到的使用reflection来创build套接字第二个是,客户端正在寻找一个给定的UUID服务器,如果你的服务器没有与客户端并行运行,那么这个发生。 用给定的客户端UUID创build一个服务器,然后从服务器端监听并接受客户端,它将工作。

我也收到相同的IOException ,但我find了Android系统演示:“BluetoothChat”项目工作。 我确定问题是UUID。

所以我把我的UUID.fromString("00001001-0000-1000-8000-00805F9B34FB")replace成UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66") ,它工作在大多数情况下,只有时需要重新启动蓝牙设备;