Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

onServicesDiscovered never called while connecting to GATT Server

I have a bluetooth headset which is paired with my Nexus 5X (running Android 7.1) and I would like to connect to a GATT Server of the headset. I tried it with the following code:

private BluetoothGattCallback btleGattCallback = new BluetoothGattCallback() {
    @Override
    public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
        Log.d(TAG, "onConnectionStateChange: " + status + ", " + newState);

        if(newState == STATE_CONNECTED) {
            Log.d(TAG, "Device connected");
            boolean ans = gatt.discoverServices();
            Log.d(TAG, "Discover Services started: " + ans);
        }
    }

    @Override
    public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            Log.d(TAG, "Number of Services: " + gatt.getServices().size());
    }
};

public void onDeviceClicked(BluetoothDevice device) {
    BluetoothGatt gatt = device.connectGatt(this, false, btleGattCallback);
    Log.d(TAG, "Connected to GATT: " + gatt.connect());
}

If I click on the headset in my UI onDeviceClicked is called and it comes to this Log output:

<!-- language: lang-none -->
Connected to GATT: true
onConnectionStateChange: 0, 2    // GATT_SUCCESS, STATE_CONNECTED
Device connected
Discover Services started: true

As you can see onServicesDiscovered is never fired. I tried to call connectGatt with TRANSPORT_LE (ref) but then I get a onConnectionStateChange: 133, 0. I also found this question which is why I added the gatt.connect() method as mentioned in answer two.

Do you have any ideas why I don't get the onServicesDiscovered callback?

like image 969
Cilenco Avatar asked Jan 02 '17 23:01

Cilenco


3 Answers

Something that has been really useful for me is to wait for about 600ms after the connection has been established and then start the service discovery.

like image 187
Roberto Betancourt Avatar answered Oct 03 '22 06:10

Roberto Betancourt


BLE on Android can be a little finicky.

Make sure you are calling mBluetoothGatt.discoverServices() on the UI thread.

if(newState == STATE_CONNECTED) {
    Log.d(TAG, "Device connected");
    new Handler(Looper.getMainLooper()).post(new Runnable() {
        @Override
        public void run() {
            boolean ans = mBluetoothGatt.discoverServices();
            Log.d(TAG, "Discover Services started: " + ans);
        }
    });
}

Also try making BluetoothGatt gatt a field variable instead of a local variable.

If you are doing any significant work, try using a library that masks all of the idiosyncrasies so you can focus on the high level logic. https://github.com/Polidea/RxAndroidBle.

Here is an example of how to read a characteristic.

        connectionObservable
                .flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUuid))
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(bytes -> {
                    readOutputView.setText(new String(bytes));
                    readHexOutputView.setText(HexString.bytesToHex(bytes));
                    writeInput.setText(HexString.bytesToHex(bytes));
                }, this::onReadFailure);

Or with Java 7 syntax

        connectionObservable
                .flatMap(new Func1<RxBleConnection, Observable<byte[]>>() {
                    @Override
                    public Observable<byte[]> call(RxBleConnection rxBleConnection) {
                        return rxBleConnection.readCharacteristic(characteristicUuid);
                    }
                })
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Subscriber<byte[]>() {
                    @Override
                    public void onCompleted() {

                    }

                    @Override
                    public void onError(Throwable e) {
                        onReadFailure(e);
                    }

                    @Override
                    public void onNext(byte[] bytes) {
                        readOutputView.setText(new String(bytes));
                        readHexOutputView.setText(HexString.bytesToHex(bytes));
                        writeInput.setText(HexString.bytesToHex(bytes));
                    }
                });
like image 22
ayvazj Avatar answered Oct 03 '22 06:10

ayvazj


Had the same problem, but waiting 600 ms wasn't enough. This is probably due to the BLE module used. I fixed the problem by calling my method

discoverServices(); 

after calling

device.connectGatt(this,false,gattCallback)

I'm basically just calling discoverServices every 5 seconds (this is arbitrarily chosen)

private void discoverServices() {
    if(!gattConnected) { //just a boolean
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                gatt.discoverServices();
            }
        });
        new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
            @Override
            public void run() {
                discoverServices();
            }
        }, 5000);
    }
}

In the onServicesDiscovered(...) method of my gattCallback I make gattConnected true. This worked for me.

like image 23
AonAbiq Avatar answered Oct 03 '22 05:10

AonAbiq