191

I am currently developing an application that will use Bluetooth Low Energy (testing on the Nexus 4). After getting started with the official BLE APIs in Android 4.3, I have noticed that after I connect a device for the first time I am rarely able to successfully connect to / communicate with that device or any other device again.

Following the guide here, I can successfully connect to a device, scan services and characteristics, and read/write/receive notifications without any issues. However, after disconnecting and re-connecting, I am often unable to either scan services/characteristics or unable to complete a read/write. I can't find anything in the logs to indicate why this is happening.

Once this happens I have to uninstall the application, disable Bluetooth, and restart the phone before it will start working again.

Whenever a device is disconnected I make sure to call close() on the BluetoothGatt object and set it to null. Any insights?


EDIT:
Log dumps: For these logs I rooted my phone and upped the trace levels of related items in /etc/bluetooth/bt_stack.conf

Successful connection - First attempt after rebooting the phone and installing the app. I am able to connect, discover all services/characteristics, and read/write.

Failed Attempt 1 - This is the next attempt after disconnecting from the successful connection above. It seems I was able to discover characteristics, but the first attempt to read returned a null value and disconnected soon thereafter.

Failed Attempt 2 - An example where I am not even able to discover services/characteristics.


EDIT 2:
The device to which I am trying to connect is based on TI's CC2541 chip. I obtained a TI SensorTag (also based on the CC2541) to play around with and discovered that TI released an android app for the SensorTag yesterday. However, this app has the same problem. I tested this on two other Nexus 4s with the same result: Connection to the SensorTag is successful the first or second time, but (according to the logs) fails to discover services thereafter, causing all sorts of crashes. I'm starting to wonder if it's an issue with this specific chip?

sa.shadow
  • 2,328
  • 3
  • 13
  • 10
  • Please post complete logs of your phone from boot-up till you facing the issue. – AAnkit Jul 29 '13 at 11:35
  • @AAnkit I've just added related log dumps. – sa.shadow Jul 29 '13 at 15:31
  • Can you add a code snippet or pseudo code of what you're doing so that we can try our best to repro on other devices running 4.3? Thanks – dljava Jul 29 '13 at 19:17
  • I have gone through the logs completely, and there seems no reason to get disconnected. have you tried testing with other remote device? – AAnkit Jul 31 '13 at 06:22
  • Don't know how helpful this is, but I am using a Nexus 4 connecting to a CC2541 and not having any problems. – dgel Jul 31 '13 at 10:53
  • @AAnkit I recently tested connectivity with a board from another manufacturer and it seems to work perfectly. However, as soon as I switch back to the CC2541 device or the SensorTag the problem starts - I can't even connect to the other board again. – sa.shadow Jul 31 '13 at 14:37
  • It looks like some other Nexus 4 and Nexus 7 (2012) users in the [TI E2E forums](http://e2e.ti.com/support/low_power_rf/f/538/p/281081/981446.aspx) are seeing the same issue with the SensorTag. We've caught the attention of one of TI's developers and hopefully he'll be able to give us more insight. – sa.shadow Jul 31 '13 at 14:44
  • it may be a remote device issue then. – AAnkit Jul 31 '13 at 14:45
  • I've noticed the same behavior (can connect only once, then need to reboot) with Polar's H7 heart rate monitor on the Nexus 4 with Android 4.3. – ejain Aug 01 '13 at 02:32
  • I also get the 129 (GATT_INTERNAL_ERROR) and especially 133 (GATT_ERROR) a lot. The Samsung BLE SDK guys told me those codes. There are more: http://img-developer.samsung.com/onlinedocs/samsung_ble_docs_200/index.html They seem to be the same since their whole API is very similar. – OneWorld Aug 05 '13 at 13:09
  • Could you be so kind a release the source of your app to the public (or me ;) ) since I also bought a SensorKit but I have no clue where to start. I managed to get the Google example to work and I can see the SensorKit but I'm not able to read any of the sensors. – Johnnycube Aug 05 '13 at 13:43
  • And additionally: The Google Bluetooth example neves fails to connect. Maybe this can help you – Johnnycube Aug 05 '13 at 13:45
  • I spotted in your ["failed"-log](http://pastebin.com/w0XjwDMW) the line `L2CA_EnableUpdateBleConnParams - unknown BD_ADDR ` which doesn't occour in your "success"-log. Same applies to my testing experiences. However, this is just a consequence of the disconnect, which is stated some lines earlier at 124 with `GATT ATT protocol channel with BDA: 9059af043336 is disconnected` I have a BlueGiga chip by the way. – OneWorld Aug 05 '13 at 13:52
  • I also get `Unknown conn ID:` as you got in http://pastebin.com/AhZq17bU I get this soon after `registerApp()`. At the 10th time I get `Unknown conn ID: 10 D/BtGatt.btif(1077): btgattc_handle_event: Event 1000 E/bt-btif(1077): Register with GATT stack failed. failed. (about 20 times) D/BtGatt.btif(1077): btif_gattc_upstreams_evt: Event 0 E/BtGatt.btif(1077): bta_to_btif_uuid: Unknown UUID length 10! D/BtGatt.GattService(1077): onClientRegistered() - UUID=00000000-408a-6f08-ffff-fe58b0e98d19, clientIf=0 E/BtGatt.ContextMap(1077): Context not found for UUID 00000000-408a-6f08-ffff-fe58b0e98d19` – OneWorld Aug 05 '13 at 14:18
  • @Johnnycube Google Bluetooth example **also fails to connect** after several connections to several BLE devices. Exactly the same behavior and log entries. I'm talking about the project in `sdk\samples\android-18\legacy\BluetoothLeGatt` which you can [download via the Android SDK Manager](http://developer.android.com/tools/samples/index.html) – OneWorld Aug 06 '13 at 10:18
  • 3
    I am using Samsung Galaxy S4 with leaked Google edition Android 4.3 installed; after numerous time of connecting/disconnecting,when I discover services I will randomly get 129(GATT_INTERNAL_ERROR) and get a onConnectionStateChange with status 133 (GATT_ERROR),state = BluetoothProfile.DEVICE_DISCONNECTED. – reTs Aug 07 '13 at 10:13
  • 1
    For once or twice I got multiple status 129 and 133 callbacks in a short period of time and I could never receive any callback in BluetoothGattCallback until I reboot my device (but scanning is fine). – reTs Aug 07 '13 at 10:21
  • 1
    Forget to say that I am testing with around ten devices using TI chips (sorry I don't know their models) and one device with Nordic chips. The device with Nordic chips never report error.(Not enough to prove the problem is TI specific,though) – reTs Aug 07 '13 at 10:29
  • @OneWorld I worked alot on the Keyfob and the Sensorkit with TI chips and if BluetoothLeGatt fails to connect it connects the next attempt. I use a galaxy nexus. On the other hand: The official TI Sensorkit app acts like you describe. – Johnnycube Aug 08 '13 at 09:41
  • @Johnnycube I want to know how many device are you connecting? In my case if I only connect/disconnect a few devices there is no outstanding problem. However when I connect to more devices, say, 10 or 15, it is easy to get errors and sometimes the BluetoothGattCallback just respond to nothing. Of course I maybe handling multiple connection badly, just want to know if anyone can do it successfully. – reTs Aug 08 '13 at 11:35
  • 1
    I can confirm that this issue is still exist on Samsung Galaxy S5 (Both **G900VVRU2BOG5** and **G900VVRU2BOA8** build version). If I clear data from _Settings>Application Manager>>All>>Bluetooth_ , it works for a time being. – IronBlossom Oct 29 '15 at 10:45

5 Answers5

189

Important implementation hints

(Perhaps some of those hints aren't necessary anymore due to Android OS updates.)

  1. Some devices like Nexus 4 with Android 4.3 take 45+ seconds to connect using an existing gatt instance. Work around: Always close gatt instances on disconnect and create a fresh instance of gatt on each connect.
  2. Don't forget to call android.bluetooth.BluetoothGatt#close()
  3. Start a new thread inside onLeScan(..) and then connect. Reason: BluetoothDevice#connectGatt(Context context, boolean autoConnect, BluetoothGattCallback callback) always fails, if called inside LeScanCallback() {...}.onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) in the same thread on Samsung Galaxy S3 with Android 4.3 (at least for build JSS15J.I9300XXUGMK6)
  4. Most devices filter advertising
  5. Better not use android.bluetooth.BluetoothAdapter#startLeScan(UUID[] serviceUuids, LeScanCallback callback) with the parameter to filter for certain service UUIDs because this is broken completely in Samsung Galaxy S3 with Android 4.3 and doesn't work for 128bit UUIDs in general.
  6. Gatt always can process one command at a time. If several commands get called short after another, the first one gets cancelled due to the synchronous nature of the gatt implementation.
  7. I often see even on modern devices with Android 5, that Wifi interferes withs bluetooth and vice versa. As a last resort, turn off wifi to stabilize bluetooth.

Tutorial for beginners

A pretty OK entry point for newcomers could be this video tutorial: Developing Bluetooth Smart Applications for Android http://youtu.be/x1y4tEHDwk0

The issue and work around described below is probably fixed now by OS updates

Work around: I could "stabilize" my app doing that...

  1. I provide the user a setting "Restart Bluetooth". If that setting is enabled, I restart Bluetooth at some points that indicate the begin of BLE stack becoming unstable. E.g. if startScan returns false. A good point may also be if serviceDiscovery failes. I just turn Bluetooth off and on.
  2. I provide another setting "Turn off WiFi". If that setting is enabled, my app turns off Wifi when the app is running (and turns it back on afterwards)

This work around is based on follwoing experiences...

  • Restarting Bluetooth helps to fix problems with BLE in most cases
  • If you turn off Wifi, the BLE stack gets much more stable. However, it also works fine on most devices with wifi turned on.
  • If you turn off Wifi, restarting Bluetooth fully recovers the BLE stack without the need to reboot the device in most cases.
Garbit
  • 5,331
  • 6
  • 35
  • 69
OneWorld
  • 16,782
  • 18
  • 79
  • 130
  • 35
    Google, you got to fix this now. This work around (I did plus it because it does work) is ridiculous. – Chris Herbert Sep 24 '13 at 05:54
  • 5
    Sometimes, service discovery will succeed with a status 0 (assuming no problems), yet characteristic reads will yield NULL values because it's essentially not really connected or the characteristics were not discovered (I see this in the log: 11-01 18:37:32.131: WARN/BluetoothGatt(20119): Unhandled exception: java.lang.NullPointerException) – Lo-Tan Nov 01 '13 at 22:52
  • 3
    @Lo-Tan I always check after service discovery, if my expected service is included. You also can never be sure, if the service discovery gives any result. Sometimes I receive no callback. So I applied a timeout for service discovery. – OneWorld Nov 25 '13 at 12:42
  • 3
    My experience is: Samsung S3 (4.3) successfully reconnected after closing a Gatt client as described in paragraph 2 above; using Nexus 4 & 7 (4.4.2) I could not reconnect after connection dropped, even by restarting BL adapter, but it can be reconnected automatically after 2 min – Konstantin Konopko Mar 11 '14 at 08:35
  • @konopko My experience: Samsung S3 (I9300, 4.3, Build JSS15J.I9300XXUGMK6) is one of the few devices that can reconnect using an existing gatt instance with no problem and no delay. Pls share your build number because BLE behavior may differ from build to build. I had not so much issues with Nexus 4 and Nexus 7 (2013) by connecting using a new gatt instance. – OneWorld Mar 11 '14 at 09:20
  • @OneWorld my build the same as yours – Konstantin Konopko Mar 11 '14 at 10:49
  • My Problem is over the Samsung Tab Pro. – jitain sharma May 01 '14 at 16:41
  • As u mention "Always close gatt instances on disconnect and create a fresh instance of gatt on each connect." Then what will be purpose of BluetoothGatt.connect() ? Any suggestion ? – CoDe Mar 31 '15 at 11:44
  • @Shubh The API of course suggests to use BluetoothGatt.connect() as u mention it. However, in the early days of BLE android implementations `BluetoothGatt.connect()` was broken in some smartphones. OS Updates might have fixed now the issues. Back than using my implementation hints was the only way to get a pretty stable BLE enabled app. – OneWorld Mar 31 '15 at 11:49
  • true.,and it still same. After using device.connectGatt(..) if use BluetoothGatt.connect() then it's not working. Have u tried it again ? – CoDe Mar 31 '15 at 11:57
  • @Shubh I haven't been working with BLE for months. Things have changed a little bit in API and on devices since then. Bugs (if they are real bugs, not your implementation mistakes) mostly depend on devices and their OS build version. Open another question on SO or contact the manufacturer. Good luck! – OneWorld Mar 31 '15 at 12:04
  • Setting autoConnect to false in BluetoothDevice#connectGatt(...) speeds up the connect at least on the Nexus 7 as described in this answer: http://stackoverflow.com/a/23749770/1990080 – Linard Arquint Apr 17 '15 at 22:50
  • Zenfone 2, Note 3 have same problem. I think Google need fix this unstable issue. Since firmware device already turned off, why Mobile side still can scan & find out it. It totally be wrong. BLE unstable. In my case is scan beacon, so use basic BLE way work but get this unstable issue. I need change to use Android Beacon Library to do it & completed now. – Huy Tower Jun 26 '15 at 05:47
  • 2
    Can anyone confirm if android.bluetooth.BluetoothGatt can only handle one pending GATT operation **PER DEVICE**, **PER PROCESS**, or **PERIOD** (ie: across all processes). I presume it is PER DEVICE, but this problem is so mucked up that it wouldn't surprise me if it was otherwise. If the limitation is only PER DEVICE, then the OS/Device able to handle multiple simultaneous operations is smoking gun proof that this problem is purely due to some weak naive implementation in the BluetoothAdapter instance that the OS hands each process (that I had assumed was a singleton across all processes). – swooby Mar 22 '16 at 00:57
  • in 6. the second command is ignored, not the first one. The first command is in execution, so you can't execute the second command. Happens in Lollipop. – Borzh Apr 15 '16 at 20:27
  • @Borzh please add a reference to your findings, e.g. Documentation or source code. The docs I shared tell a different story. However, you have to avoid several commands at the time anyway. – OneWorld Apr 16 '16 at 10:02
  • For example source code for android-23 (version M): `public boolean readCharacteristic(BluetoothGattCharacteristic characteristic) { ... synchronized(mDeviceBusy) { if (mDeviceBusy) return false; mDeviceBusy = true; } ...` – Borzh Apr 27 '16 at 13:01
  • Nexus 4 is the first device to exclude from anything BLE related! – Saik Caskey Apr 28 '17 at 14:51
  • In my experience, with some device, wifi breaks the bluetooth connection but only when wifi is 'on' AND 'disconnected'. – fireb86 Dec 19 '19 at 09:30
19

Turning WIFI OFF:

I can confirm too, that turning WIFI OFF makes Bluetooth 4.0 more stable especially on Google Nexus (I have a Nexus 7).

The problem

is that the application I am developing needs both WIFI and continous Bluetooth LE scanning. So turning WIFI OFF was no option for me.

Moreover I have realised is that continous Bluetooth LE scanning can actually kill WIFI connection and make the WIFI adapter unable to re-connect to any WIFI network until BLE scan is ON. (I'm not sure about mobile networks and mobile internet).
This definitely happened on the following devices:

  • Nexus 7
  • Motorola Moto G

However BLE scanning with WIFI on seemed pretty stable on:

  • Samsung S4
  • HTC One

My workaround

I scan BLE for a short period of time 3-4 seconds then I turn scan OFF for 3-4 seconds. Then ON again.

  • Obviously I always turn BLE scan OFF when I'm connecting to a BLE device.
  • When I disconnect from a device I restart BLE (turn adapter OFF and then ON) to reset the stack before starting scan again.
  • I also reset BLE when discovering services or characteristics fails.
  • When I get advertisement data from a device that the app should connect to (lets say 500 times without being able to connect - thats about 5-10 seconds of advertising) I reset BLE again.
Community
  • 1
  • 1
benka
  • 4,662
  • 35
  • 44
  • 58
10

Make sure your Nexus is paired to the device. I can't verify whether or not the communication works properly, but you will be able to connect more than once without a reboot. It seems the first connect is not requiring pairing but all subsequent attempts do.

I will update this answer in a couple of days when I test service discovery and gatt read and write requests without a reboot.

EDIT: It turns out I was testing on a development firmware version (our sensor) that was causing issues if not paired. Our latest production firmware build works fine on the 2540s and 2541s.

EDIT: I did notice that on the Nexus 7 2013, connections are more stable when WiFi is turned off. I'd like to know if this helps anyone else.

EDIT: I seem to have had it backwards with pairing. Everything works fine when not paired. After pairing, I am experiencing the exact same symptoms as the OP. It's just not known yet if this is related to our firmware or the Android BLE API. Be careful if testing this because once paired, you may not be able to unpair due to a bug explained in 3b of this post.

Mikt25
  • 695
  • 1
  • 9
  • 18
  • I am consistently connecting and reconnecting to a CC2541 device without any sort of manual pairing or reboot. – dgel Aug 02 '13 at 09:17
  • For my opinion, there is no pairing necessary. The official docs don't comment pairing also. I also could perform write, read, characteristic changed notification without any pairing. However, just for a short time. Now it's shaky again... The SAMSUNG BLE SKD v2.0 also didn't require pairing and worked quite good. – OneWorld Aug 05 '13 at 12:54
  • 3
    I can confirm, **it's more stable after turning off Wifi.** Everybody should try that. – OneWorld Aug 09 '13 at 12:02
  • @OneWorld Are you using the new Nexus 7 as well? – Mikt25 Aug 09 '13 at 14:06
  • @Mikt25 I have a Nexus 4. – OneWorld Aug 09 '13 at 19:00
  • 1
    Whether pairing is required or not is dependent up the device implementation. nrf8002 devices require pairing and the Samsung 2.0 and 1.2 API's both supported this. It seems like the official api support is having problems with the pairing aspect as after I pair a ble device, it seems impossible to unpair! – Chris Herbert Aug 18 '13 at 04:24
  • 2
    I have a work around for being unable to unpair. 1) go to your bt menu, select unpair, remove the ble device from the area or depower it, select the ble device in the bt menu and it'll try to pair and fail, then reset bluetooth. Upon reset the device will be unpaired. – Chris Herbert Aug 22 '13 at 05:41
  • OK. I got a Nexus 7 (2013) today, run my app, the first impression is smooth and stable. Connect/disconnect/discover services without problem until this point. I was encountering those problems regularly on Samsung Galaxy S4, FYI. – reTs Sep 03 '13 at 05:05
8

In some models there is a defect: https://code.google.com/p/android/issues/detail?id=180440

On the other hand in my case the problem was, that my connection was not properly closed in onDestroy method. After correct closing, problem for me is not existing, not matter that wifi is turned on or off.

btGatt.disconnect();
btGatt.close();
Krystian
  • 1,961
  • 23
  • 36
  • Why is `close` necessary? – IgorGanapolsky Apr 04 '17 at 20:39
  • 3
    Correct closing procedure is Key when you want to connect Bluetooth multiple times. In my experience it works best if you run your Ble connection in a seperate UNBOUND service so you can start and stop it by hand. And that you call mConnectedGatt.disconnect(); ble_device=null; in your inDestroy(). In my case this pattern works stable without a problem. – FishingIsLife May 24 '18 at 12:07
5

I was facing a similar issue. My fix was

if (Build.VERSION.SDK_INT >= 23) {
  mBluetoothGatt = device.connectGatt(this, false, mGattCallback, BluetoothDevice.TRANSPORT_LE);
} else {
  mBluetoothGatt = device.connectGatt(this, false, mGattCallback);
}

& calling close after disconnect.

Sam Reyes
  • 299
  • 4
  • 7