Подтвердить что ты не робот

BLE Android - onConnectionStateChange не вызывается

У меня проблема с подключением к периферии. Иногда callback onConnectionStateChange(...) не вызывается после BluetoothDevice#connectGatt(...). То, что я пытаюсь достичь, это быстрые и короткие соединения, вызванные действием пользователя.

Эта ситуация происходит примерно в 1 раз в 10 раз без конкретных действий. Он длится около 20-30 секунд или пока приложение не будет убито и не будет возобновлено. Нормальная последовательность шагов, которые я выполняю, следующая:

  • Сканировать устройства, чтобы найти периферийные устройства.
  • Вызов BluetoothDevice#connectGatt(...). Если для подключения требуется более 1 секунды, это означает, что соединение "застряло" и, следовательно, оно не будет подключаться, поэтому BluetoothDevice#connectGatt(...) вызывается снова. Это делается с ограничением в 5 попыток.
  • onConnectionStateChange(...) вызывается с помощью newState CONNECTED и начинает обнаружение служб.
  • Остальные операции выполняются без проблем.
  • После выключения BluetoothGatt#close().

Проблема, которая возникает, находится в точке 3. Иногда onConnectionStateChange(...) не вызывается. Я заметил, что в большинстве случаев проблема начинается с определенного поведения. После BluetoothDevice#connectGatt(...), onConnectionStateChange(...) вызывается с newState CONNECTED, но почти сразу после этого (~ 40 миллисекунд) снова вызывается с newStatus DISCONNECTED. Из-за короткого времени изменения статуса я могу сделать вывод, что устройство даже не пыталось установить соединение и изменило состояние на DISCONNECTED. Проблема заканчивается, когда:

  • Прошло 20-30 секунд. За это время onConnectionStateChange(...) никогда не вызывается. Когда проблема заканчивается, onConnectionStateChange(...) называется числом раз, которое приложение пыталось подключиться. Например, если BluetoothDevice#connectGatt(...) вызывается 15 раз, onConnectionStateChange(...) вызывается 15 раз с newState равным DISCONNECTED. Это любопытно, потому что ни в одном из этих попыток подключения статус не изменился на CONNECTED.
  • Приложение убито и снова запустилось.

Эта ошибка возникает в SDK18 и 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);
            }
        }
    }

Я думаю, что периферийное устройство не актуально, потому что приложение iOS всегда может подключаться без этой проблемы.

Любые идеи? Спасибо заранее.

Edit

Этот ответ говорит, что:

Прямое соединение имеет интервал 60 мс и окно 30 мс, поэтому соединения выполняются намного быстрее. Кроме того, может быть только один прямой запрос на соединение, ожидающий времени, и он истекает после 30 секунд. onConnectionStateChange() вызывается с состоянием = 2, status = 133, чтобы указать этот тайм-аут.

Итак, в этот интервал 30 секунд есть ожидающий запрос на соединение и время ожидания на второй 30. Это маловероятно, но есть ли что-нибудь, что я могу сделать, чтобы сделать это короче? Или, может быть, есть объяснение сбоя подключения, которого я не вижу. Спасибо.

РЕДАКТИРОВАТЬ 02/03/2016

Новое, что может помочь. Когда проблема начинается, что при onConnectionStateChange(...) вызывается с newState равным DISCONNECTED после ~ 40 мс, который вызывается с newState равным CONNECTED, статус равен 62 = 0x03E. Посмотрите здесь, что код состояния означает GATT_CONN_FAIL_ESTABLISH. Когда я обнаруживаю этот статус, я закрываю соединение gatt, но проблема сохраняется. Я также попытался отключить и закрыть. Идеи? Спасибо.

4b9b3361

Ответ 1

Если у кого-то есть аналогичная проблема, проблема была окончательно решена путем замены чипа BLE, используемого периферийным устройством (arduino). До этого изменения обходной путь, который я нашел, отключился и на BLE после каждого соединения. Решение не было совершенным, но значительно улучшило скорость соединения.

Ответ 2

Android Bluetooth необходимо периодически перерабатывать, попробовали ли вы перезапустить BLE на устройстве, когда вы столкнулись с этим часом?

Вот фрагмент, который я использовал для перезапуска BLE, когда происходят странные вещи.

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

Я не уверен, что вы все еще ищете ответ на этот вопрос. Лично я бы не советовал делать "быстрые и короткие соединения, вызванные действием пользователя" для устройств с низкой энергией. Вместо этого вы можете установить для параметра autoConnect значение "true" в вашем методе connectGatt.

device.connectGatt(mContext, true, mGattCallback); [вместо false]

Надеюсь, что это поможет!