NordicSemiconductor / Android-nRF-Toolbox

The nRF Toolbox is a container app that stores your Nordic Semiconductor apps for Bluetooth Low Energy in one location.
https://www.nordicsemi.com/Software-and-tools/Development-Tools/nRF-Toolbox
BSD 3-Clause "New" or "Revised" License
1.07k stars 461 forks source link

Getting error on descriptor write status 133 with bonded device #9

Open talarari opened 8 years ago

talarari commented 8 years ago

hey, i'm getting some weird behavior with enabling notifications. i'm developing an app that needs to connect to a ble peripheral automatically, i scan, find the device then bond with - device.createBond(). then i enable notifications from the device on the rx characteristic for the uart service. this works fine for the first time, but when i use the bonded device to do the same the second time i get a callback to descriptor write with status 133 sometimes followed by a connection state change callback with state 0 and status 22.

i could not find any info online for what status 133 or 22 mean.

any idea why this is happening? removing the bond makes it work again, but again, only for the first time, i cant complete the flow from a bonded device

philips77 commented 8 years ago

Hi, regarding those errors, you may find some very, very short info here: https://android.googlesource.com/platform/external/bluetooth/bluedroid/+/android-5.1.1_r13/stack/include/gatt_api.h

Error 133, defined as: #define GATT_ERROR 0x85 is a general BLE error. It usually appears when a device is not reachable or... from any other occasion. The error 22 is a connection error, which is defined there as #define GATT_CONN_TERMINATE_LOCAL_HOST HCI_ERR_CONN_CAUSE_LOCAL_HOST /* 0x16 connectionterminated by local host */. You'll find other error codes "described" there as well.

Regarding the bonding problem, I'd say it depends very much which Android version or which device are you using. You may try to add some delays here and there to give the stack some more time. In the toolbox, in BLEManager, I wait 600ms after I get onConnectionStateChange(..) callback before calling gatt.discoverServices(). Also clearing the device cache usually helps, but it's a hack and should not be used normally. To refresh the cache you have to call gatt.refresh() method using reflections. You'll find it implemented in the DfuBaseService class in the DFU Library, here.

Did you try using nRF Toolbox or nRF Master Control Panel apps to connect to your service after you bond? Do you get similar results?

talarari commented 8 years ago

just tried the nrf toolbox app with the bonded device, got the exact same problem. error on writing descriptor 133, error:0x16 gatt conn terminate local host

i added a 2 second delay after connecting gatt before doing anything and now it works. thanks!

talarari commented 8 years ago

ok, was wrong, the delay did not fix it. not sure what i can do

talarari commented 8 years ago

this usually doesnt happen when i initiate the disconnection from the device myself. when the device disconnects becuase it was turned off and back on or when i walk away from it and come back then i always get this problems. when i get the onConnectionStateChange event with disconnect state i still call gatt.disconnect and gatt.close but maybe something is not released properly.

any suggestions?

talarari commented 8 years ago

some more details, if i only save the ble device address and connect using bluetoothAdapter.getRemoteDevice(address) then i never have this problem. it is only when bonding, maybe the bond information is no longer valid after my ble device restarts? what info does the bond keep?

philips77 commented 8 years ago

Without bonding reconnecting is much more stable and reliable. Even if you get 133 you may repeat it and usually it works. But in come cases you must have bond (for example if you have secure informations, proximity etc.). Then you have a problem... :( From my experience if any error occurs the bond information is lost on Android. The bond information contains the LTK - Long Term Key - a key used to encrypt the connection. This key is known by both sides and used to encrypt and decrypt packets. If one of sides does not have it, the other either refuses to connect (as it thinks it is not a valid device), tries to bond again or just connects without bonding, depending on the implementation. You also may not rely on the getBondState() method from the device. If the device was bonded, but due to an error the LTK has been removed, the method still returns state bonded. The only reliable way I know to check if a device is still bonded after connection is using notifications/indications. A bonded devices should save values of CCCDs for each characteristic that have them and after reconnection you should start getting packets. You must remember to enable notifications or indications locally on the Android gatt.setCharacteristicNotification(characteristic, true); after you reconnect and do service discovery. If notifications were enabled (CCCD=0x0100) before, you should start receiving them. If you lost bond the CCCD value will be 0 (you may also read it just after you reconnect).

amibition521 commented 8 years ago

I now face a situation on the following: the premise is that I will be connected with the equipment are stored locally, when off the device on automatic connection device, but now the callback connection for a long time, about 35S,I get the error 133, any good solution can shorten the feedback time and solution this 133 problem

philips77 commented 8 years ago

In this case the 133 means that the device is not reachable. It doesn't advertise or is out of range. Also, I think you can't just connect to a device that you created in the code with the Mac address, you have to scan for it, or obtain from Bluetooth Adapter if it's bonded.

This code may not work:

BluetoothDevice d = new BluetoothDevice("AA:33:66:44:66:77");
d.connectGatt(...);
philips77 commented 7 years ago

Regarding my previous comment. The code snippet will work in 2 conditions:

  1. The device has Public address
  2. The device with given address has been scanned at least once before the connectGatt method is called.

Explanation: The BLE address is in fact 49-bit long. The additional bit says whether the device is private or public. It is present in all advertising packets as well as in Connect Req packet. http://j2abro.blogspot.no/2014/06/understanding-bluetooth-advertising.html If the device has never been scanned, the Android does not know what type of address it is. The API allows only to specify the 48-bit long address. However, as private addresses are usually randomly generated, it's highly unlikely that someone would know the address in real life scenario (debugging your device on you desk is not one of them). Therefore in that case they assume that the address is public and even if all 48-bits of your device are equal to those given in the parameter above, the connection attempt will fail. However, if a device with such address was scanned at least once, its type was saved by the system. Then, when you create a BluetoothDevice object that way, or obtained it from a scanner, the Android knows what address type put in the Connect Request packet.

mjohn123 commented 7 years ago

I know the issue may be closed but I have the same issue. I did not find any good solution. My issue only happen in some device such as Samsung Note 3. My error code is

onClientConnectionState() - status=22 clientIf=7

To solve that error, I used a delay before calling the readCharacteristic () function on the

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                try {
                    Thread.sleep(500);
                    readCustomCharacteristic();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            } else {
                Log.w(TAG, "onServicesDiscovered received: " + status);
            }
        }

It can solve my issue. However, I do not think the Thread.sleep is a good solution because delay time (500ms) may be different among devices. In additions, the above error only happens on some device. It worked well with other devices. In BLE, do we have any callback function which can safely call the readCustomCharacteristic()?

This is my readCustomCharacteristic() function

public void readCustomCharacteristic() {
        /*check if the service is available on the device*/
        BluetoothGattService mCustomService = mBluetoothGatt.getService(UUID.fromString("00001c00-d102-11e1-9b23-00025b00123"));
        /*get the read characteristic from the service*/
        BluetoothGattCharacteristic mReadCharacteristic = mCustomService.getCharacteristic(UUID.fromString("00001233-d102-11e1-9b23-00025b00a5a5"));
        mBluetoothGatt.setCharacteristicNotification(mReadCharacteristic, true);
        BluetoothGattDescriptor descriptor = mReadCharacteristic.getDescriptor(
                UUID.fromString(SampleGattAttributes.CLIENT_CHARACTERISTIC_CONFIG));
        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
        mBluetoothGatt.writeDescriptor(descriptor);
    }
thejeshgn commented 7 years ago

For me writing my own onDescriptorWriteRequest solved the issue. Specially adding mGattServer.sendResponse

 @Override
        public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, 
BluetoothGattDescriptor descriptor, 
                        boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
            Log.d("HELLO", "Our gatt server descriptor was written.");
            super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded, offset, value);
            Log.d("DONE", "Our gatt server descriptor was written.");

            //NOTE: Its important to send response. It expects response else it will disconnect
            if (responseNeeded) {
                mGattServer.sendResponse(device,
                        requestId,
                        BluetoothGatt.GATT_SUCCESS,
                        0,
                        value);

            }

        }
mjohn123 commented 7 years ago

Hello thejeshgn, which issue did you solve? Status=133 or status=22 as my issue

thejeshgn commented 7 years ago

Issue of status=133 or status=2 while trying to write descriptor.

IgorGanapolsky commented 7 years ago

Any update on error 133? I am getting the same thing on Android 7.1.2.

cvergnaud commented 6 years ago

When you have too many connections opened, a 133 error can occur. Make sure you have closed all GattClient. I have a device that triggers quite often a 133 even if all connections are closed, my solution is to restart the connection process. The trick of thejeshgn is "doing the job" but it is not solving the root cause. The remote device should respond to the WriteDescriptorRequest, and android should trigger the event "OnDescriptorWrite". Does this characteristic allow notification ?

Vhaiyue commented 6 years ago

In the process of upgrade,some mobile phone can be upgraded successfully, such as XIAOMI 4S, But the connection is broken when the Implement progress is almost 10%, Such as HUAWEI 4X, the worse, sometimes it is only 3% when the connection is broken with BLE. What is wrong with it?

NivedithaKabbur commented 6 years ago

The error in my case was due to the encryption issue in the Bluetooth LE stack. Was getting the error btif_gatt_set_encryption_cb() - Encryption failed (1). Any idea about this error?

softlion commented 6 years ago

@thejeshgn onDescriptorWriteRequest is for an Android Gatt Server. Here the problem is for Android Gatt client. The client does not have any onDescriptorWriteRequest method to override.

AbhiShah29 commented 6 years ago

Hi guys,

As @philips77 mentioned the error 133 is a common BLE error and its not just only android error. When we perform multiple time connection and disconnection with the same device or with different devices the error occurs. I have solved the issue by performing the below options.

If you are facing error 133 at the time of connection.

  • Add 500ms delay before connection request.
  • If an error still persists just disable ANDROID DEVICE BT and enable again. After enabling BT, wait for 500ms and then connect to BLE device.

If you are facing an error 133 at the time of Characteristic or Description read-write.

  • Add 500ms delay and try again
  • Disable notification and enable notification and retry read-write
  • Wait for 30sec before the read-write request. // The common BLE device has advertisement timeout interval of 30sec. In this 30sec they stop advertisement and restart again.
ashishTechmosoft commented 5 years ago

when i open first time ble then it connect perfectly but after disable and i am trying to cnnect again then it showing error 133 error.plz help

acutetech commented 5 years ago

I had a problem which sounds similar (bonding information lost on second connection, only on one device) reported here:

I attributed it to both peripheral and central initiating encryption at the same time, and describe a couple of work-arounds. But I suspect there is more to it than this.

nourmed commented 5 years ago

I am getting same error "error on writing descriptor (133)" when I connect the device (esp32 as server) to an android phone only when I try to read Temperature values while on Iphone works just fine ,can't find the reason why

philips77 commented 5 years ago

The only descriptor that nRF Toolbox for Android reads is for the Service Changed characteristic. On bonded devices it ensures that SC indications are enabled. iOS API doesn't allow to read CCC descriptors, so we don't do this on iOS. iOS also deals with the SC indications correctly, so there's no need for any workarounds. Make sure the CCC descriptor under Service Changed characteristic is readable.

isgetzz commented 2 years ago

I'm trying to update it DfuBaseService: Connection state change error: 133 newState: 0 and Device not reachable. Check if the device with address D3:EF:0F:0F:EB:27 is in range, is advertising and is connectable my device is still connected