BluetoothGattServer всегда отключается через 30 секунд

Я пытался выяснить, как заставить эти телефоны общаться друг с другом через Bluetooth. У меня есть телефон Android, настроенный как периферийное устройство, и на моем iPhone работает приложение nrf connect. Я могу рекламировать и подключаться к телефону Android с iPhone, и я могу подписаться на уведомления и видеть обновление характеристик. Проблема в том, что если я не отправлю характерное уведомление, то примерно через 7-10 секунд связь будет потеряна. Я получаю обратный вызов обработчика обратного вызова connectionStateChanged и не могу понять, в чем причина. Я не думаю, что это приложение nrf, потому что я подключался к другим вещам, и оно просто остается на связи навсегда. Есть что-то, что мне не хватает?

Вот код:

private BluetoothManager bluetoothManager;
private BluetoothGattServer gattServer;
private BluetoothGattCharacteristic test_characteristic;
private BluetoothDevice connected_device;


private BluetoothGattServerCallback gattServerCallback = new BluetoothGattServerCallback() {
    @Override
    public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
        super.onConnectionStateChange(device, status, newState);
        if (newState == 2){ //CONNECTED
            connected_device = device;
        }
        Log.i("chase", "Connection state changed: "+newState);
    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    BluetoothRegulator.sharedInstance().appContext = getApplicationContext();
    bluetoothManager = (android.bluetooth.BluetoothManager) this.getSystemService(Context.BLUETOOTH_SERVICE);
    BluetoothRegulator.sharedInstance().initialize(bluetoothManager);

    if( ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, new String[]{android.Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
    }


    gattServer = bluetoothManager.openGattServer(this, gattServerCallback);
    String uuid_string = "92D9D153-9BE6-43FF-9672-3E2904628B9D";
    BluetoothGattService service = new BluetoothGattService(UUID.fromString(uuid_string), BluetoothGattService.SERVICE_TYPE_PRIMARY);

    uuid_string = "43FF0001-9BE6-43FF-9672-3E2904628B9D";
    test_characteristic = new BluetoothGattCharacteristic(
            UUID.fromString(uuid_string),
            BluetoothGattCharacteristic.PROPERTY_BROADCAST | BluetoothGattCharacteristic.PROPERTY_NOTIFY | BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PROPERTY_WRITE,
            BluetoothGattCharacteristic.PERMISSION_READ | BluetoothGattCharacteristic.PERMISSION_WRITE);
    test_characteristic.setValue( ""+System.currentTimeMillis() );
    service.addCharacteristic(test_characteristic);
    gattServer.addService(service);

    findViewById(R.id.send_data_btn).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            shot_detected_characteristic.setValue( ""+System.currentTimeMillis() );
            gattServer.notifyCharacteristicChanged(connected_device, shot_detected_characteristic, false);
        }
    });

    findViewById(R.id.advertise).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            advertise();
        }
    });

}
private void advertise() {
    BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
    BluetoothLeAdvertiser advertiser = adapter.getBluetoothLeAdvertiser();
    AdvertiseSettings settings = new AdvertiseSettings.Builder()
            .setAdvertiseMode( AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY )
            .setTxPowerLevel( AdvertiseSettings.ADVERTISE_TX_POWER_HIGH )
            .setConnectable( true )
            .build();
    String uuid_string = "92C9D164-9BE6-43FF-9672-3E2804618B9C";
    ParcelUuid pUuid = new ParcelUuid( UUID.fromString( uuid_string ) );

    AdvertiseData data = new AdvertiseData.Builder()
            .setIncludeDeviceName( true )
            .addServiceData( pUuid, "TEST".getBytes( Charset.forName( "UTF-8" ) ) )
            .build();

    AdvertiseCallback advertisingCallback = new AdvertiseCallback() {
        @Override
        public void onStartSuccess(AdvertiseSettings settingsInEffect) {
            Log.e( "BLE", "Advertising Started");
            super.onStartSuccess(settingsInEffect);
        }

        @Override
        public void onStartFailure(int errorCode) {
            Log.e( "BLE", "Advertising onStartFailure: " + errorCode );
            super.onStartFailure(errorCode);
        }
    };
    advertiser.startAdvertising( settings, data, advertisingCallback );
}

Редактировать: На самом деле, я заметил, что это не имеет ничего общего с бездействием. На самом деле он каждый раз отключается примерно на 30-й секунде.

EDIT: поэтому я добавил обратные вызовы onCharacteristicRead и onCharacteristicWrite. Но это, похоже, ничего не меняет, и для меня это имеет смысл, потому что я в любом случае ничего не делаю в них, кроме вызова super.onCharacteristicRead(). Кроме того, они не будут вызываться, если я не буду читать и писать. И кажется, что Bluetooth должен оставаться на связи более 30 секунд, даже если нет запроса на чтение или запись.

private BluetoothGattServerCallback gattServerCallback = new BluetoothGattServerCallback() {
    @Override
    public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
        super.onConnectionStateChange(device, status, newState);
        if (newState == 2){
            connected_device = device;
            advertiser.stopAdvertising(new AdvertiseCallback() {});
        }
    }

    @Override
    public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
        super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
    }

    @Override
    public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
        super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite, responseNeeded, offset, value);
    }

};

РЕШЕНИЕ

Итак, проблема заключается в обратном вызове onCharacteristicReadRequest. Согласно документации:

Приложение должно вызвать BluetoothGattServer#sendResponse для выполнения запроса.

Итак, как только я обновил свой обратный вызов, чтобы он выглядел так:

@Override
public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
    super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
    gattServer.sendResponse(device, requestId, 1, offset, characteristic.getValue());
}

а затем читал каждые несколько секунд, тогда время ожидания не истекло и не отключилось.


person Chase Roberts    schedule 27.02.2019    source источник
comment
Вы можете удалить вызов super.onCharacteristicReadRequest, поскольку базовый класс объекта обратного вызова следует рассматривать как интерфейс.   -  person Emil    schedule 19.03.2019


Ответы (1)


Согласно спецификации ядра ble, для ATT

Транзакция, не завершенная в течение 30 секунд, истечет по тайм-ауту.

На практике это обычно проявляется в виде отключения одной стороны соединения.

Некоторые типы запросов GATT требуют ответов (например, индикации, запросы на чтение, запросы на запись). Не похоже, что вы реализуете обратные вызовы, такие как onCharacteristicReadRequest и onCharacteristicWriteRequest, в своей реализации сервера Gatt, что может привести к этой проблеме.

person chrisc11    schedule 28.02.2019
comment
Я попробовал это, и это, похоже, ничего не изменило. Я также отредактировал вопрос выше. - person Chase Roberts; 19.03.2019
comment
А, я понял. - person Chase Roberts; 19.03.2019