Atualmente estou tentando lidar com uma estranha exceção ao abrir um BluetoothSocket no meu Nexus 7 (2012), com Android 4.3 (Build JWR66Y, acho que a segunda atualização 4.3). Eu vi algumas postagens relacionadas (por exemplo, /programming/13648373/bluetoothsocket-connect-throwing-exception-read-failed ), mas nenhuma parece fornecer uma solução alternativa para esse problema. Além disso, como sugerido nesses tópicos, o reemparelhamento não ajuda e tentar conectar-se constantemente (por meio de um loop estúpido) também não tem efeito.
Estou lidando com um dispositivo incorporado (um adaptador de carro OBD-II sem nome, semelhante a http://images04.olx.com/ui/15/53/76/1316534072_254254776_2-OBD-II-BLUTOOTH-ADAPTERSCLEAR-CHECK-ENGINE- LUZES-COM-SEU-TELEFONE-Oceanside.jpg ). Meu telefone Android 2.3.7 não tem problemas de conexão e o Xperia de um colega (Android 4.1.2) também funciona. Outro Google Nexus (não sei se 'One' ou 'S', mas não '4') também falha com o Android 4.3.
Aqui está o snippet do estabelecimento da conexão. Ele está rodando em seu próprio Thread, criado dentro de um 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;
}
}
O código está falhando bluetoothSocket.connect()
. Estou recebendo um java.io.IOException: read failed, socket might closed, read ret: -1
. Esta é a fonte correspondente no GitHub: https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothSocket.java#L504
É chamado por readInt (), chamado de https : //github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothSocket.java#L319
Algum despejo de metadados do soquete usado resultou nas seguintes informações. Eles são exatamente iguais no Nexus 7 e no meu telefone 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
Eu tenho alguns outros adaptadores OBD-II (mais expansivos) e todos eles funcionam. Existe alguma chance de estar faltando alguma coisa ou pode ser um bug no Android?
Respostas:
Finalmente encontrei uma solução alternativa. A magia está escondida sob o capô da
BluetoothDevice
classe (consulte https://github.com/android/platform_frameworks_base/blob/android-4.3_r2/core/java/android/bluetooth/BluetoothDevice.java#L1037 ).Agora, quando recebo essa exceção, instanciamos um fallback
BluetoothSocket
, semelhante ao código-fonte abaixo. Como você pode ver, invocando o método ocultocreateRfcommSocket
por meio de reflexões. Não tenho ideia de por que esse método está oculto. O código-fonte define comopublic
se ...connect()
então não falha mais. Eu ainda tive alguns problemas. Basicamente, isso às vezes bloqueia e falha. Reinicializar o dispositivo SPP (plug off / plug in) ajuda nesses casos. Às vezes, também recebo outra solicitação de emparelhamentoconnect()
mesmo quando o dispositivo já está vinculado.ATUALIZAR:
aqui está uma classe completa, contendo algumas classes aninhadas. para uma implementação real, eles podem ser mantidos como classes separadas.
fonte
Fallback failed. Cancelling. java.io.IOException: Connection refused
Por favor ajude.bem, eu tive o mesmo problema com meu código, e é porque desde o Android 4.2 pilha bluetooth mudou. então meu código estava funcionando bem em dispositivos com android <4.2, nos outros dispositivos eu estava recebendo a famosa exceção "falha de leitura, soquete pode ser fechado ou tempo limite, leia ret: -1"
O problema é com o
socket.mPort
parâmetro. Quando você cria seu soquete usandosocket = device.createRfcommSocketToServiceRecord(SERIAL_UUID);
,mPort
obtém o valor inteiro " -1 ", e esse valor parece não funcionar para android> = 4.2, então você precisa defini-lo como " 1 ". A má notícia é quecreateRfcommSocketToServiceRecord
só aceita UUID como parâmetro e nãomPort
por isso temos que usar outra abordagem. A resposta postado por @matthes também trabalharam para mim, mas eu simplificado-lo:socket =(BluetoothSocket) device.getClass().getMethod("createRfcommSocket", new Class[] {int.class}).invoke(device,1);
. Precisamos usar ambos os atributos de socket, o segundo como fallback.Portanto, o código é (para conectar a um SPP em um dispositivo ELM327):
fonte
mPort
parâmetro! imho o fluxo de trabalho permanece o mesmo, acabei de embrulhar as coisas com algumas classes implementando uma interface.Primeiro, se você precisar falar com um dispositivo bluetooth 2.x, esta documentação afirma que:
Não achei que fosse funcionar, mas apenas substituindo o UUID por
00001101-0000-1000-8000-00805F9B34FB
ele funciona. No entanto, este código parece lidar com o problema da versão do SDK, e você pode simplesmente substituir a funçãodevice.createRfcommSocketToServiceRecord(mMyUuid);
portmp = createBluetoothSocket(mmDevice);
após definir o seguinte método:O código-fonte não é meu, mas vem deste site .
fonte
Tive os mesmos sintomas descritos aqui. Pude conectar uma vez a uma impressora bluetooth, mas as conexões subsequentes falharam com "soquete fechado", não importa o que eu fiz.
Achei um pouco estranho que as soluções alternativas descritas aqui fossem necessárias. Depois de examinar meu código, descobri que havia esquecido de fechar o InputStream e o OutputSteram do soquete e não finalizei o ConnectedThreads corretamente.
O ConnectedThread que uso é o mesmo do exemplo aqui:
http://developer.android.com/guide/topics/connectivity/bluetooth.html
Observe que ConnectThread e ConnectedThread são duas classes diferentes.
Qualquer classe que iniciar o ConnectedThread deve chamar interrupt () e cancel () no thread. Adicionei mmInStream.close () e mmOutStream.close () ao método ConnectedTread.cancel ().
Depois de fechar os threads / streams / sockets corretamente eu poderia criar novos sockets sem nenhum problema.
fonte
Bem, eu realmente encontrei o problema.
A maioria das pessoas que tenta fazer uma conexão usando
socket.Connect();
uma exceção é chamadaJava.IO.IOException: read failed, socket might closed, read ret: -1
.Em alguns casos, também depende do seu dispositivo Bluetooth, porque existem dois tipos diferentes de Bluetooth, nomeadamente BLE (baixa energia) e Classic.
Se você deseja verificar o tipo de seu dispositivo Bluetooth, este é o código:
Há dias tento resolver o problema, mas desde hoje encontrei o problema. A solução de @matthes infelizmente ainda tem alguns problemas como ele já disse, mas aqui está minha solução.
No momento eu trabalho no Xamarin Android, mas também deve funcionar para outras plataformas.
SOLUÇÃO
Se houver mais de um dispositivo emparelhado, você deve remover os outros dispositivos emparelhados. Portanto, mantenha apenas aquele que deseja conectar (veja a imagem certa).
Na imagem à esquerda, você vê que tenho dois dispositivos emparelhados, ou seja, "MOCUTE-032_B52-CA7E" e "Blue Easy". Esse é o problema, mas não tenho ideia de por que esse problema ocorre. Talvez o protocolo Bluetooth esteja tentando obter algumas informações de outro dispositivo Bluetooth.
No entanto,
socket.Connect();
funciona muito bem agora, sem problemas. Então, eu só queria compartilhar isso, porque esse erro é realmente irritante.Boa sorte!
fonte
Em versões mais recentes do Android, recebia este erro porque o adaptador ainda estava descobrindo quando tentei conectar ao soquete. Embora eu tenha chamado o método cancelDiscovery no adaptador Bluetooth, tive que esperar até que o retorno de chamada para o método onReceive () do BroadcastReceiver fosse chamado com a ação BluetoothAdapter.ACTION_DISCOVERY_FINISHED.
Assim que esperei que o adaptador parasse de detectar, a chamada de conexão no soquete foi bem-sucedida.
fonte
Você coloca
registerReceiver(receiver, new IntentFilter("android.bleutooth.device.action.UUID"));
com "bluetooth" escrito "bleutooth".fonte
Caso alguém esteja tendo problemas com o Kotlin, tive que seguir a resposta aceita com algumas variações:
Espero que ajude
fonte
Os dispositivos Bluetooth podem operar nos modos clássico e LE ao mesmo tempo. Às vezes, eles usam um endereço MAC diferente, dependendo de como você está se conectando. Chamar
socket.connect()
é usar o Bluetooth Classic, então você deve se certificar de que o dispositivo que você recebeu ao fazer a varredura é realmente um dispositivo clássico.É fácil filtrar apenas para dispositivos clássicos, no entanto:
if(BluetoothDevice.DEVICE_TYPE_LE == device.getType()){ //socket.connect() }
Sem essa verificação, é uma condição de corrida se uma varredura híbrida fornecerá primeiro o dispositivo Classic ou o dispositivo BLE. Pode parecer uma incapacidade intermitente de se conectar, ou alguns dispositivos sendo capazes de se conectar de forma confiável, enquanto outros aparentemente nunca conseguem.
fonte
Eu também enfrentei esse problema, você poderia resolvê-lo de 2 maneiras, como mencionei anteriormente, use reflexão para criar o soquete. A segunda é, o cliente está procurando por um servidor com o UUID fornecido e se o seu servidor não estiver rodando paralelo ao cliente, então isso acontece. Crie um servidor com o UUID do cliente fornecido e, em seguida, ouça e aceite o cliente do lado do servidor. Isso funcionará.
fonte
Corri para esse problema e corrigi-lo fechando os fluxos de entrada e saída antes de fechar o soquete. Agora posso desconectar e conectar novamente sem problemas.
https://stackoverflow.com/a/3039807/5688612
Em Kotlin:
fonte
Se outra parte do seu código já fez uma conexão com o mesmo soquete e UUID, você receberá este erro.
fonte
Mesmo eu tendo o mesmo problema, finalmente entendi meu problema, eu estava tentando conectar de (fora do alcance) cobertura de Bluetooth.
fonte
Eu tive esse problema e a solução foi usar o GUID mágico especial.
Suspeito que estes são os UUIDs que funcionam:
No entanto, não experimentei todos eles.
fonte
Ao adicionar ação de filtro, meu problema foi resolvido
fonte
Também recebi o mesmo
IOException
, mas acho que a demo do sistema Android: projeto "BluetoothChat" funcionou. Eu determinei que o problema é o UUID.Então eu troquei o meu
UUID.fromString("00001001-0000-1000-8000-00805F9B34FB")
paraUUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66")
e funcionou na maioria das cenas, só às vezes preciso reiniciar o dispositivo Bluetooth;fonte