web-dev-qa-db-fra.com

BLE Android - onConnectionStateChange n'est pas appelé

J'ai un problème en essayant de me connecter à un périphérique. Parfois, le rappel onConnectionStateChange(...) n'est pas appelé après BluetoothDevice#connectGatt(...). Ce que j'essaie d'atteindre, c'est des connexions rapides et courtes déclenchées par l'action de l'utilisateur.

Cette situation se produit environ 1 toutes les 10 fois sans action préalable spécifique. Il dure environ 20 à 30 secondes ou jusqu'à ce que l'application soit supprimée et rouverte. La séquence normale des étapes que je suis est la suivante:

  1. Analysez les périphériques pour trouver le périphérique.
  2. Appelez BluetoothDevice#connectGatt(...). S'il faut plus de 1 seconde pour se connecter, cela signifie que la connexion est "bloquée" et donc qu'elle ne se connectera pas, donc BluetoothDevice#connectGatt(...) est appelé à nouveau. Cela se fait avec une limite de 5 tentatives.
  3. onConnectionStateChange(...) est appelée avec newState CONNECTED et commence la découverte des services.
  4. Le reste des opérations s'effectue sans problème.
  5. Après la déconnexion, BluetoothGatt#close() est appelée.

Le problème se produit au point 3. Parfois, onConnectionStateChange(...) n'est pas appelée. J'ai remarqué que la plupart du temps, le problème commence avec un comportement spécifique. Après avoir appelé BluetoothDevice#connectGatt(...), onConnectionStateChange(...) est appelée avec newState CONNECTED, mais presque immédiatement après (~ 40 millisecondes) est appelée à nouveau avec newStatus DISCONNECTED. En raison de la courte durée du changement d'état, je peux en déduire que l'appareil n'a même pas essayé d'établir la connexion et a changé l'état en DÉCONNECTÉ. Le problème se termine lorsque:

  1. 20-30 secondes se sont écoulées. Pendant ce temps, onConnectionStateChange(...) n'est jamais appelée. Une fois le problème terminé, onConnectionStateChange(...) est appelée le nombre de fois que l'application a tenté de se connecter. Par exemple, si BluetoothDevice#connectGatt(...) est appelée 15 fois, onConnectionStateChange(...) est appelée 15 fois avec newState égal à DISCONNECTED. C'est curieux car jamais dans aucune de ces tentatives de connexion l'état n'a été changé en CONNECTED.
  2. L'application est tuée et redémarrée.

Cette erreur se produit dans SDK18 et SDK 21.

@Override
public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
    String deviceName = device.getName();
    if (deviceName == null) return;
    Log.d("BLUETOOTH CONNECTION", "Device found: " + device.getName());
    if (mMode == SCAN_MODE) {
        mListener.deviceFound(device, rssi, scanRecord);
    }
    else {
        mDevices.put(device.hashCode(), device);
        stopScan();
        // Samsung devices with SDK 18 or 19 requires that connectGatt is called in main thread.
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Log.d("BLUETOOTH CONNECTION", "Executing first device.connectGatt()");
                BluetoothGatt gatt = device.connectGatt(mContext, false, mGattCallback);
                retryIfNecessary(device, gatt);
                mTryingToConnect = true;
            }
        });
    }
}
private void retryIfNecessary(final BluetoothDevice device, final BluetoothGatt gatt) {
    if (isRetryLimitReached()) {
        Log.d("BLUETOOTH CONNECTION", "Try count limit reached");
        finishConnection(gatt);
        mRetryCount = 0;
        mListener.error(TIMEOUT);
        return;
    }
    mRetryCount++;
    mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
            Log.d("BLUETOOTH CONNECTION", "Check if it is frozen.");
            if (isWorking()) {
                Log.d("BLUETOOTH CONNECTION", "Frozen, create new connection.");
                BluetoothGatt gatt = device.connectGatt(mContext, false, mGattCallback);
                retryIfNecessary(device, gatt);
            }
        }
    }, RETRY_INTERVAL_MS);
}
    @Override
    public void onConnectionStateChange(final BluetoothGatt gatt, int status, int newState) {
        Log.d("BLUETOOTH CONNECTION", "On connection state changed. Device: "+ gatt.getDevice().getAddress());
        if (!mConnected && BluetoothGatt.STATE_CONNECTED == newState) {
            Log.d("BLUETOOTH CONNECTION", "Connected");
            mTryingToConnect = false;
            mTryingToDiscoverServices = true;
            mConnected = true;
            gatt.discoverServices();
        }
        else if(BluetoothGatt.STATE_DISCONNECTED == newState) {
            Log.d("BLUETOOTH CONNECTION", "Disconnected and closing gatt.");
            mConnected = false;
            gatt.close();
            if (!mConnectionFinished && mRetryCount == 0) {
                finishConnection(gatt);
            }
        }
    }

Je pense que le périphérique n'est pas pertinent, car l'application iOS peut toujours se connecter sans ce problème.

Des idées? Merci d'avance.

Modifier!

This réponse dit que:

La connexion directe a un intervalle de 60 ms et une fenêtre de 30 ms afin que les connexions se terminent beaucoup plus rapidement. De plus, il ne peut y avoir qu'une seule demande de connexion directe en attente à la fois et elle expire après 30 secondes. onConnectionStateChange () est appelé avec state = 2, status = 133 pour indiquer ce délai.

Donc, dans cet intervalle de 30 secondes, il y a une demande de connexion en attente et expire au second 30. C'est peu probable mais, est-ce que je peux faire pour raccourcir ce temps? Ou peut-être y a-t-il une explication à l'échec de la connexion que je ne vois pas. Merci.

EDIT 02/03/2016

Une nouvelle information qui peut vous aider. Lorsque le problème démarre (lorsque onConnectionStateChange(...) est appelée avec newState=DISCONNECTED Après ~ 40 ms d'être appelée avec newState=CONNECTED), L'état est 62 = 0x03E. En regardant ici ce code d'état signifie GATT_CONN_FAIL_ESTABLISH. Lorsque je détecte cet état, je ferme la connexion Gatt, mais le problème persiste. J'ai également essayé de déconnecter et de fermer. Des idées? Merci.

19
avmatte

Si quelqu'un a un problème similaire, le problème a finalement été résolu en changeant la puce BLE utilisée par le périphérique (Arduino). Avant ce changement, une solution de contournement que j'avais trouvée était d'éteindre et de rallumer le BLE après chaque connexion. La solution n'était pas parfaite, mais a beaucoup amélioré le taux de connexion.

5
avmatte

Je ne sais pas si vous cherchez toujours une réponse à cette question. Personnellement, je ne conseillerais pas de faire des "connexions rapides et courtes déclenchées par l'action de l'utilisateur" pour les appareils à faible consommation d'énergie. Au lieu de cela, vous pouvez définir l'option autoConnect sur "true" dans votre méthode connectGatt.

device.connectGatt (mContext, true, mGattCallback); [au lieu de faux]

J'espère que ça aide!

3

Le Bluetooth Android doit être recyclé de temps en temps, avez-vous essayé de redémarrer le BLE sur l'appareil lorsque vous rencontrez ce délai?

Voici un extrait que j'ai utilisé pour redémarrer le BLE lorsque des choses étranges commencent à se produire.

static Handler mHandler = new Handler();
public static void restartBle() {
    final BluetoothManager mgr = (BluetoothManager) ApplicationBase.getAppContext().getSystemService(Context.BLUETOOTH_SERVICE);
    final BluetoothAdapter adp = mgr.getAdapter();
    if (null != adp) {
        if (adp.isEnabled()) {
            adp.disable();

            // TODO: display some kind of UI about restarting BLE
            mHandler.postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (!adp.isEnabled()) {
                        adp.enable();
                    } else {
                        mHandler.postDelayed(this, 2500);
                    }
                }
            }, 2500);
        }
    }
}
3
Michael