web-dev-qa-db-fra.com

Le rappel GATT ne parvient pas à s'enregistrer

J'essaie d'écrire une application pour envoyer des messages via Bluetooth Low Energy, qui seront ensuite transmis par UART dans mon périphérique. J'ai suivi les étapes ici = et l'application recherche et trouve le périphérique avec succès. Cependant, la connexion utilisant la méthode BluetoothGatt = BluetoothDevice.connectGatt (contexte, autoconnexion, rappel) échoue, avec logcat disant "Impossible d'enregistrer le rappel".

Appel effectué depuis:

//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 appréciées.

[EDIT] la méthode connectGatt retourne 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 émis car la méthode registerApp () renvoie false car la méthode registerClient () de l'IBluetoothGatt "mService" lève une exception distante, probablement à la ligne:

enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");

car le message du journal de la ligne suivante n'est jamais vu. Il pourrait donc s'agir d'autorisations, sauf que l'application dispose des autorisations bluetooth et bluetooth_admin.

49
Wibble

J'ai finalement compris ce problème. L'appareil que j'utilise est un Samsung Galaxy S4 et le problème réel (merci Wibble pour les conseils dans votre réponse, mais votre conclusion est légèrement décalée) semble être un problème de thread.

Dans la réponse de Wibble, il a déclaré que l'ajout d'un bouton pour se connecter a résolu 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 GUI en utilisant des threads de travail en arrière-plan. Dès que je force à fermer mon application, à la redémarrer et à essayer de me connecter, j'obtiens l'erreur "Impossible d'enregistrer le rappel". et rien ne marche plus. J'ai presque tiré 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 d'exécuter n'importe quel code d'interaction BLE (périphérique # connectGatt, connectez, déconnectez, etc.) dans l'UIThread (avec un gestionnaire, un service local ou Activity # runOnUiThread). Suivez cette règle empirique et vous pourrez, espérons-le, éviter ce terrible problème.

Au fond de notre bibliothèque, je n'avais accès qu'au contexte de l'application. Vous pouvez créer un gestionnaire à partir d'un contexte qui sera publié dans le thread principal en utilisant 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 voyez si cette application fonctionne. C'était en quelque sorte ma base de référence 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 finirais par trouver la réponse.

EDIT: Je n'ai pas vu ce problème "Échec de l'enregistrement du rappel" sur le Nexus 4, Nexus 5 ou Nexus 7 2013 lors de l'utilisation de threads d'arrière-plan pour effectuer Opérations BLE. Cela peut simplement être un problème dans la mise en œuvre de Samsung 4.3.

100
Lo-Tan

Donc, mon problème était de l'exécuter à partir d'un service récursif. connectGatt a bien fonctionné avec Lollipop mais les anciennes versions ont retourné null. l'exécution sur un thread principal a résolu le problème. Voici ma solution:

public void connectToDevice( String 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
            }
        }
    });
}
4
TacoEater

Je peux également confirmer que Lo-Tan est la réponse à vérifier en premier. J'ai testé de nombreux appareils, certains se comportent bien lorsque vous exécutez à partir d'un thread secondaire. Certains peuvent se bloquer après un certain temps, le comportement est imprévu.

Voici la liste des choses à faire:

  1. Assurez-vous que vous utilisez un nouveau gestionnaire (Looper.getMainLooper ()). Post (nouveau Runnable) sur toute opération Gatt (connexion, déconnexion, fermeture) mais aussi sur les opérations du scanner (startScan, stopScan etc.).

  2. Il y a une condition de concurrence pour la connexion directe sur Android 6 (ou peut-être 5) alors essayez de connecter gatt 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éconnecter () en premier et fermer () ensuite dans la routine GattCallback.

1
HED Tech