rosu alin
rosu alin

Reputation: 5830

Setting the UUID of a bluetooth beacon device does not work correctly

I pair with my device, I connect to it, using this code

   //this will try  to connect to our bluetooth device
public void connectGatt(Context context, BluetoothDevice btDevice) {
    this.btDevice = btDevice;
    mBluetoothGatt = btDevice.connectGatt(context, false, mGattCallback);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && mBluetoothGatt != null) {
        mBluetoothGatt.requestMtu(512);
    }
}

This is my GATT Callback:

 private final BluetoothGattCallback mGattCallback =
        new BluetoothGattCallback() {
            @Override
            public void onConnectionStateChange(BluetoothGatt gatt, int status,
                                                int newState) {
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    tryToConnect = false;
                    Log.i("BluetoothService", "BluetoothService onConnectionStateChange CONNECTED");
                    if (back != null)
                        back.onResponse(REMOVE_CALLBACKS);
                    Log.i("", "Connected to GATT server.");
                    boolean discover = mBluetoothGatt.discoverServices();
                    Log.i("", "Attempting to start service discovery:" + discover);
                } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                    Log.i("BluetoothService", "BluetoothService onConnectionStateChange STATE_DISCONNECTED");
                    if (tryToConnect) {
                        if (back != null)
                            back.onResponse(REMOVE_CALLBACKS);
                        stopMonitoringBeacons();
                        stopListeningForBeacons();
                        mBluetoothAdapter.disable();
                        setSleep(1500);
                        mBluetoothAdapter.enable();
                        setSleep(1500);
                        Log.i("BluetoothService", "BluetoothService onConnectionStateChange WILL TRY CONNECT");
                        if (back != null)
                            back.onResponse(CONNECT);
                        tryToConnect = false;
                    }
                }
            }

            @Override
            // New services discovered
            public void onServicesDiscovered(BluetoothGatt gatt, int status) {
                tryToConnect = false;
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    Log.i("BluetoothService", "BluetoothService onConnectionStateChange onServicesDiscovered GATT_SUCCESS" + status);
                    Log.w("", "onServicesDiscovered GATT_SUCCESS: " + status);
                    List<BluetoothGattService> listBGS = mBluetoothGatt.getServices();
                    Log.i("", "list size: " + listBGS.size());
                    if (listBGS.size() > 0) {
                        if (back != null)
                            back.onResponse(CONFIGURE);
                        String character = "FF05";
                        if (justName)
                            character = "FF01";
                        setCharacteristic(gatt, character);
                    } else {
                        Log.i("BluetoothService", "BluetoothService onConnectionStateChange onServicesDiscovered GATT_SUCCESS but ZERO SERVICES: " + btDevice.getBondState());
                        if (btDevice.getBondState() == BluetoothDevice.BOND_BONDED) {
                            setSleep(500);
                            if (btDevice.getBondState() == BluetoothDevice.BOND_BONDED)
                                mBluetoothGatt.discoverServices();
                        } else if (btDevice.getBondState() == BluetoothDevice.BOND_NONE) {
                            askPairDevice();
                        }
                    }
                } else {
                    askPairDevice();
                    Log.w("BluetoothService", "BluetoothService onServicesDiscovered received: " + status);
                }
            }

            @Override
            // Result of a characteristic read operation
            public void onCharacteristicRead(final BluetoothGatt gatt,
                                             final BluetoothGattCharacteristic characteristic,
                                             int status) {
                handler.removeCallbacksAndMessages(null);
                Log.i("BluetoothService", "BluetoothService onCharacteristicRead");
                if (status == BluetoothGatt.GATT_SUCCESS) {
                    Log.i("BluetoothService", "BluetoothService onCharacteristicRead GATT_SUCCESS");
                    Log.w("", "BGC onCharacteristicRead GATT_SUCCESS: " + status + " / char: " + characteristic);
                    if (characteristic.getUuid().toString().toUpperCase().contains("FF05")) {
                        final String value = toHexadecimal(characteristic.getValue());
                        if (newBeacon == null || newBeacon.getName() == null) {
                            checkIfNeedsToChangeUUID(gatt, value);
                        } else if (newBeacon != null && (newBeacon.getUuid() != null || justName)) {
                            Log.i("BluetoothService", "BluetoothService new Beacon UUID is: " + value);
                            newBeacon.setUuid(Identifier.parse(value).toString());
                            if (back != null)
                                back.onResponse(CHANGED);
                        } else {
                            changeUUID(gatt, characteristic, value);
                        }
                    } else if (characteristic.getUuid().toString().toUpperCase().contains("FF01")) {
                        changeName(gatt, characteristic);
                    }
                } else {
                    Log.i("", "");
                }
            }
        };

The part that I am interested in is here:

   public void changeUUID(final BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, String value) {
    int size = value.length();
    RandomString gen = new RandomString(size, new SecureRandom());
    String uuid = gen.nextString();
    Log.i("", "BluetoothService BGC value NEW UUID: " + uuid);
    boolean change = characteristic.setValue(hexStringToByteArray(uuid));
    Log.i("", "BluetoothService BGC value after: " + toHexadecimal(characteristic.getValue()) + " / has changed: " + change);
    boolean statusWrite = gatt.writeCharacteristic(characteristic);
    Log.i("", "BluetoothService BGC value after statusWRITE: " + statusWrite);
    if (statusWrite) {
        newBeacon.setUuid(Identifier.parse(toHexadecimal(characteristic.getValue())).toString());
        new Handler(Looper.getMainLooper()).post(new Runnable() {
            @Override
            public void run() {
                setCharacteristic(gatt, "FF05");
            }
        });
    } else {
        if (back != null)
            back.onResponse(ERROR);
    }
}

Which will call this:

       try {
        Log.i("BluetoothService", "BluetoothService set characteristic: " + characteristic);
        BluetoothGattCharacteristic btChar = null;
        for (BluetoothGattService bgs : gatt.getServices()) {
            for (final BluetoothGattCharacteristic bgc : bgs.getCharacteristics()) {
                if (bgc.getUuid().toString().toUpperCase().contains(characteristic)) {
                    btChar = bgc;
                    gatt.readCharacteristic(bgc);
                    break;
                }
            }
        }
        final BluetoothGattCharacteristic btF = btChar;
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                if (btF != null)
                    gatt.readCharacteristic(btF);
            }
        }, 1000);
    } catch (Exception e) {
        Log.i("BluetoothService", "BluetoothService set characteristic ERROR: " + e.getMessage());
        setCharacteristic(gatt, characteristic);
    }
}

This works, but not 100%, I could say that it works on less than 50% of the cases. and I don't understand why. Can someone help?

I mean boolean statusWrite = gatt.writeCharacteristic(characteristic); Always returns to me "TRUE" so then if in the "changeUUID function I make after that another call, to try to log it, why is it NOT changed? Also with other apps, if I check, my UUID is not changed, which is really weird. Why get the TRUE for the write, if value is the same?

Upvotes: 1

Views: 202

Answers (1)

davidgyoung
davidgyoung

Reputation: 64916

If you look at the documentation for writeCharacteristic, you'll see this:

Returns boolean true, if the write operation was initiated successfully

The emphasis above is mine. Just because you successfully initiated the write doesn't mean it compleleted successfully, and indeed, in my apps I often see that it does not.

What you need to do is implement the callback public void onCharacteristicWrite (BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status), then check the status to see if it is BluetoothGatt.GATT_SUCCESS. If not, you will need to retry the write. I usually make code that retries up to 10 times, then gives up after that, triggering code that presents an error to the user.

Most other Android Bluetooth APIs work this way -- discovering services, discovering characteristics, etc. Each one of these async operations can fail, and you have to implement the callbacks to find out if they actually succeeded or not, and add a retry strategy.

Upvotes: 1

Related Questions