Le rappel du GATT ne parvient pas à s’inscrire

J’essaie d’écrire une application pour envoyer des messages via Bluetooth Low Energy, qui sera ensuite transmise par UART dans mon périphérique. J’ai suivi les étapes ici et l’application parsing et trouve le périphérique avec succès. Cependant, la connexion utilisant la méthode BluetoothGatt = BluetoothDevice.connectGatt (context, autoconnect, callback) échoue, avec logcat indiquant “Impossible d’enregistrer le rappel”.

Appel à partir de:

//device scan callback private BluetoothAdapter.LeScanCallback btScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanRecord) { some stuff currBtGatt = device.connectGatt(parentActivity, false, btGattCallback); } }; 

Et le rappel Gatt:

 //GATT callback private BluetoothGattCallback btGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { // if connected successfully if(newState == BluetoothProfile.STATE_CONNECTED) { //discover services updateStatus("Connected"); gatt.discoverServices(); } else if(newState == BluetoothProfile.STATE_DISCONNECTED) { updateStatus("Disconnected"); } } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { if(status == BluetoothGatt.GATT_SUCCESS) { //pick out the (app side) transmit channel currBtService = gatt.getService(uartUuids[0]); currBtCharacteristic = currBtService.getCharacteristic(uartUuids[1]); } else { updateStatus("Service discovery failed"); } } }; 

Logcat dit:

 11-19 10:40:39.363: D/BluetoothAdapter(11717): stopLeScan() 11-19 10:40:39.373: D/BluetoothGatt(11717): connect() - device: DC:6D:75:0C:0F:F9, auto: false 11-19 10:40:39.373: D/BluetoothGatt(11717): registerApp() 11-19 10:40:39.373: D/BluetoothGatt(11717): registerApp() - UUID=3ba20989-5026-4715-add3-a5e31684009a 11-19 10:40:39.373: I/BluetoothGatt(11717): Client registered, waiting for callback 11-19 10:40:49.373: E/BluetoothGatt(11717): Failed to register callback 11-19 10:40:49.533: D/BluetoothGatt(11717): onClientRegistered() - status=0 clientIf=5 11-19 10:40:49.533: E/BluetoothGatt(11717): Bad connection state: 0 11-19 10:40:49.593: D/BluetoothGatt(11717): onClientConnectionState() - status=0 clientIf=5 device=DC:6D:75:0C:0F:F9 11-19 10:40:49.593: W/BluetoothGatt(11717): Unhandled exception: java.lang.NullPointerException 

Fait intéressant, mon périphérique passe à un état “connecté” (j’ai des LED d’indication) et je peux me connecter à partir du même téléphone avec une application de démonstration ou avec un dongle PC BLE. Toutes les idées sont appréciées.

[EDIT] la méthode connectGatt renvoie null, ce qui, je suppose, est attendu.

[EDIT] Lors de l’inspection du code source de l’API 18, il apparaît que le message «Echec de l’enregistrement du rappel» est remis car la méthode registerApp () renvoie false car la méthode registerClient () de IBluetoothGatt «mService» génère probablement une exception distante à la ligne:

 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 

parce que le message de journal à la ligne suivante est jamais vu. Cela pourrait donc être une autorisation, sauf que l’application dispose des permissions Bluetooth et bluetooth_admin.

J’ai finalement compris ce problème. Le périphérique que j’utilise est un Samsung Galaxy S4 et le problème réel (merci Wibble pour obtenir des conseils dans votre réponse, mais vous êtes légèrement dans votre conclusion) semble être un problème de threading.

Dans sa réponse, Wibble a déclaré que l’ajout d’un bouton pour se connecter corrigeait son problème. J’ai commencé à me demander pourquoi cela importait, et je peux également me connecter et me déconnecter correctement pendant toute une session sans bouton d’interface graphique utilisant des threads de travail en arrière-plan. Dès que je force à fermer mon application, redémarrez-le et essayez de vous connecter, je commence à recevoir l’erreur “Impossible d’enregistrer le rappel”. et rien ne fonctionne plus. J’ai presque arraché mes cheveux sur celui-ci 🙂

Voir mon post dans les forums de Samsung pour plus de détails sur mes problèmes exacts.

Solution: pour contourner ce problème, assurez-vous simplement d’exécuter le code d’interaction BLE (périphérique # connectGatt, connect, disconnect, etc.) dans UIThread (avec un gestionnaire, un service local ou Activity # runOnUiThread). Suivez cette règle et vous éviterez, espérons-le, ce problème épouvantable.

Au plus profond de notre bibliothèque, je n’avais access qu’au contexte de l’application. Vous pouvez créer un gestionnaire à partir d’un contexte qui publiera sur le thread principal à l’aide du new Handler(ctx.getMainLooper());

Si vous rencontrez d’autres problèmes de connexion, déployez l’exemple d’application dans samples\android-18\legacy\BluetoothLeGatt et vérifiez si cette application fonctionne. C’était un peu mon sharepoint départ pour réaliser que BLE fonctionne réellement avec mon périphérique et m’a donné l’espoir que si je creusais suffisamment dans notre bibliothèque, je trouverais la réponse.

EDIT: Je n’ai pas vu ce problème «Impossible d’enregistrer le rappel» sur le Nexus 4, Nexus 5 ou Nexus 7 2013 lors de l’utilisation de threads d’arrière-plan pour effectuer des opérations BLE. Cela pourrait juste être un problème dans l’implémentation de Samsung 4.3.

Donc, mon problème était de l’exécuter depuis un service récursif. ConnectGatt a bien fonctionné avec Lollipop mais les anciennes versions renvoyaient null. l’exécution sur un thread principal a résolu le problème. Ceci est ma solution:

 public void connectToDevice( Ssortingng deviceAddress) { mDeviceAddress = deviceAddress; final BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(mDeviceAddress); Handler handler = new Handler(Looper.getMainLooper()); handler.post(new Runnable() { @Override public void run() { if (device != null) { mGatt = device.connectGatt(getApplicationContext(), true, mGattCallback); scanLeDevice(false);// will stop after first device detection } } }); } 

Je peux également confirmer que Lo-Tan est la solution pour vérifier en premier. J’ai testé beaucoup d’appareils, certains se comportent bien lorsque vous utilisez un thread secondaire. Certains peuvent bloquer au bout d’un moment, le comportement est imprévisible.

Voici la liste des choses à faire:

  1. Assurez-vous d’utiliser le nouveau gestionnaire (Looper.getMainLooper ()). Post (nouveau Runnable) sur toute opération de type gatt (connexion, déconnexion, fermeture) mais aussi sur les opérations du scanner (startScan, stopScan, etc.).

  2. Il y a des conditions de course pour une connexion directe sur Android 6 (ou peut-être 5), alors essayez de vous connecter comme ceci:

      new Handler(getContext().get().getMainLooper()).post(() -> { if (CommonHelper.isNOrAbove()) { connectedGatt = connectedBLEDevice.connectGatt(context.get(), true, gattCallback, BluetoothDevice.TRANSPORT_AUTO); Timber.tag("HED-BT").d("Connecting BLE after N"); } else { try { Method connectGattMethod = connectedBLEDevice.getClass().getMethod("connectGatt", Context.class, boolean.class, BluetoothGattCallback.class, int.class); connectedGatt = (BluetoothGatt) connectGattMethod.invoke(connectedBLEDevice, context.get(), false, gattCallback, BluetoothDevice.TRANSPORT_AUTO); Timber.tag("HED-BT").d("Connecting BLE before N"); } catch (Exception e) { failedConnectingBLE(); } } }); 
  3. Lors de la déconnexion du gatt, appelez d’abord disconnect () et close () dans la routine GattCallback.

Afin de se connecter automatiquement à un périphérique Bluetooth, c’est-à-dire sans saisie explicite de la part de l’utilisateur alors que j’essayais de le faire, l’autorisation BLUETOOTH_PRIVILEDGE est requirejse. Cependant, cela n’est pas disponible pour les applications tierces, donc mon code a échoué. L’ajout d’une option de menu pour se connecter et utiliser le même code fonctionne bien.

http://developer.android.com/reference/android/Manifest.permission.html#BLUETOOTH_PRIVILEGED