Prevent NPE on BluetoothAdapter's onScanResult - android

While running on Nexus 7 with Android 4.4, the onScanResult throws a NullPointerExceptionas as seen in the log below:
03-18 17:59:34.170: D/BluetoothAdapter(5092): onScanResult() - Device=78:4B:08:02:7C:91 RSSI=-77
03-18 17:59:34.170: W/BluetoothAdapter(5092): Unhandled exception: java.lang.NullPointerException
However, other devices have no such problems.
I've found this on googlesource
public void onScanResult(String address, int rssi, byte[] advData) {
if (DBG) Log.d(TAG, "onScanResult() - Device=" + address + " RSSI=" +rssi);
// Check null in case the scan has been stopped
synchronized(this) {
if (mLeHandle <= 0) return;
}
try {
BluetoothAdapter adapter = mBluetoothAdapter.get();
if (adapter == null) {
Log.d(TAG, "onScanResult, BluetoothAdapter null");
return;
}
mLeScanCb.onLeScan(adapter.getRemoteDevice(address), rssi, advData);
} catch (Exception ex) {
Log.w(TAG, "Unhandled exception: " + ex);
}
}
Which has a number of potential culprits, but of course I cannot set values for these variables.
Any why does this fail for Nexus 7 but not for other devices? Any ideas for workarounds?

Android 4.3, 4.4: BLE filtering in startLeScan(UUIDs, callback) doesn't work for 128-bit UUIDs
It works on Samsung s5, tested with Android 4.4.2 but for some reason, it fails on Nexus. Waiting for this fix from Google stack.
In case you want to search for specific address only, you could use this solution. Basically, you would have to use scanRec[], take an extra effort to parse it and then add device with matching address into a list adapter.
[I know, .... wish that simple API would work ! :P ]

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.

Related

How to make Android connect after the Bluetooth Adapter has been disabled & reenabled?

I have written an app that connects to a BLE device. The app works OK on most devices; but some devices (most noticeably Huawei P8 Lite and Nexus 6P) refuse to connect after the Bluetooth adapter has been disabled.
This is the test sequence:
Make sure the app is NOT running.
Slide down from the top, disable BT for a couple of seconds, then re-enable bluetooth.
Start the app. The app automatically connects to a bluetooth address stored in the preferences.
Wait for connect. This is where nothing happens on Huawei phones, but other phones, such as Samsung, works like a charm.
Verify from another phone the device is advertising and you can
connect to it.
This is the code I use to connect:
private final Runnable mBeginConnectRunnable = new Runnable() {
#Override
public void run() {
synchronized (GattConnection.this) {
if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) {
try {
mBluetoothAdapter.cancelDiscovery();
mBluetoothDevice = mBluetoothAdapter.getRemoteDevice(mAddress);
mGatt = mBluetoothDevice.connectGatt(mContext, mBackgroundConnect, mGattCallback);
final boolean connectSuccess = mGatt.connect();
Log.d(TAG, String.format(Locale.ENGLISH, "mGatt.connect(%s, %s) %s",
mAddress,
mBackgroundConnect ? "background[slow]" : "foreground[fast]",
connectSuccess ? "success" : "failed"));
refreshDeviceCache(mGatt);
} catch (Exception ex) {
Log.e(TAG, "Create connection failed: " + ex.getMessage());
setState(State.Closed);
}
} else {
Log.d(TAG, "Can't create connection. Adapter is disabled");
setState(State.Closed);
}
}
}
};
All calls are posted via a Handler to the main thread. I can see it waits for a connect, gives up after 30 seconds at which I call BluetoothGatt.close() on the object and nulls it. It's like nothing is out there.
After some time, later in the day, it works again.
Help is highly appreciated :-)
Update September 14, 2018: After great explanation from Emil I've updated our app and as such don't have this problem on the Nexus. I've noticed the Huawei P8 Lite continues to scan in the background and it seems there is nothing you can do to stop it.
To demonstrate the problems I've made a very simple and clean app that exercise the Bluetooth LE functionality on a phone and used it to demonstrate this problem and also the P8 is broken. The app is available here: https://play.google.com/store/apps/details?id=eu.millibit.bluetootherror
Source is available here: https://bitbucket.org/millibit/eu.millibit.bluetootherror/src/master/
I hope I over time can extend this app to make it a test vehicle for Android documenting all the stange behavior from Android and collect it in a database. In case you are interested in contributing, don't hesitate to drop me a mail on bt.error#millibit.dk
The Android Bluetooth stack has a design flaw in its API. When you connect to a specific device by Bluetooth Device Address, there is no way to tell if you mean a Public address or Random address.
If you start to connect to a device with autoConnect=false which is not bonded and has not recently been seen in a Scan, it will assume you mean a Public address. So if you try to connect to a device having a static random address, it will fail.
To be sure you connect with the correct address type if the device is not bonded, you MUST perform a scan first, find the device and THEN start the connection attempt.

Bluetooth connection is hanged on first Bluetooth dongle (Android App)

I have a problem while working with multiple devices connected via BT LE (Bluetooth low energy).
Work flow is as below:
Step of processing:
1. Scan all bluetooth devices to get address.
BluetoothAdapter btAdapter = ((BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE)).getAdapter();
BluetoothLeScanner bluetoothLeScanner = mBtAdapter.getBluetoothLeScanner();
ScanSettings settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build();
if (btAdapter .isEnabled()) {
bluetoothLeScanner.startScan(filters, settings, mScanCallback);
}
2.Loop all devices get by step 1. For each device:
BluetoothDevice btDevice = bluetoothAdapter.getRemoteDevice(deviceAddress);
BluetoothGatt bluetoothGatt = mBluetoothDevice.connectGatt(context, false, btCallback);
on onConnectionStateChange() of btCallBack I call bluetoothGatt .discoverServices(); when connection state is STATE_CONNECTED.
After doing data exchange with device. I call
bluetoothGatt.disconnect();
Thread.sleep(500);
bluetoothGatt.close();
Process next device
The problem is when I called bluetoothGatt.disconnect() and bluetoothGatt.close() but the first Bluetooth Dongle connection always hangs and the blue led of bluetooth dongle never blink even I kill the app.
Only the last Bluetooth dongle can release connection when work is done.
My question is why I call disconnect() and close() but it does not work ?
build.gradle: minSdkVersion 21, targetSdkVersion 28
Otherwise, this problem only occurs with some kind of Android devices not all devices. For example it works well on Samsung device (Android 7), but not working on other devices.
Any help would be appreciated.
After trying a lot. I fixed this problem by adding idle time between ICUs connection.
Step as below.
1. Connect to device 1
2. Transfer data
3. Disconnect device 1
4. Thread.sleep for some seconds
5. Connect to device 2
...
Hope this helps someone has problem as me.

android BLE - automatic re-connect after spontaneous disconnect

using the android 4.4 BLE APIs on my Nexus7, i'm able to successfully interact with a peripheral BLE device -- connect, disconnect, read, write....
if however an active connection breaks for whatever reason (in this case, the peripheral is reset), i observe the following behavior....
my peripheral (by design) begins advertising after any active connection is terminated (for whatever reason); i can see this via my bluetooth packet sniffer....
i receive the onConnectionStateChanged callback as expected in my android app, at which point i invoke close() on my active BluetoothGatt instance; this is the same procedure i follow during a "normal" disconnect initiated from the client...
shortly after this, the android BLE stack tries to re-connect to the same peripheral; through the packet sniffer i can see the BLE connection request going out over the air...
my app, however, did not initiate this re-connection; indeed, i see no information from any bluetooth log suggesting this even happened!!!!
is there some "mode" in the BLE stack where it attempts to re-establish busted connections automatically???
thanks....
This happens on various Android phones whether the autoConnect flag is set to false or true.
Couldn't yet find a complete solution, it seems as the android BLE stack is spontaneously re-initiating the connection once it is getting the advertising signal again, just ignoring that it was the app that disconnected on purpose...
A partial solution may involve not using the BluetoothGatt.connect() method as explained here:
https://stackoverflow.com/a/23749770/4144487
So, a sample connect method can look like:
void connect(Context context) {
if (mGatt != null) {
mGatt.close();
}
mGatt = mDevice.connectGatt(context, false, callback);
}
To explain the importance of this issue, when it happens the peripheral thinks it is connected and my "real" app can't find it any more. At some phones like Galaxy S3 and Redmi note 3 I found that closing the bluetooth switch from the notification bar is "releasing" the peripheral and allowing me to discover the device. At others like Nexus 5x only a phone reboot will do the trick.
I've observed this happening if you use autoConnect=true when calling BluetoothGatt#connectGatt(). Generally I've found that it is best to use autoConnect=false, but with some devices you simply cannot connect unless you use true, so I usually do both. I try false first and if that fails then use true and then the behavior you're describing is something you simply have to work around.

BLE Device Bonding Remove Automatically in Android

We are doing below process to do pair with BLE Device.
Connect() + discoverServices() + Pairing(Bonding) .
Sometimes Android OS unpaired our BT device in a weird way, that is:
without sending broadcast notification that bonding state has changed
even system Bluetooth settings app thinks that device is still paired
only bt restart (turning off and on via settings app) refreshes state and shows that device is not paired any longer
When Device is Successfully Paired the ACTION_BOND_STATE is change as below.
[6:19:28 PM] Himen Patel: 04-09 18:18:27.325: D/BluetoothGatt(8380): onCharacteristicWrite() - Device=C2:69:E9:57:93:A4 UUID=860b2c07-e3c5-11e2-a28f-0800200c9a66 Status=5
04-09 18:18:27.365: E/millisUntilFinished(8380): millisUntilFinished = 15
04-09 18:18:28.105: E/BelwithDeviceActor(8380): Bond state changed for: C2:69:E9:57:93:A4 new state: 11 previous: 10
04-09 18:18:28.105: E/millisUntilFinished(8380): millisUntilFinished = 20
04-09 18:18:29.135: E/millisUntilFinished(8380): millisUntilFinished = 18
04-09 18:18:30.135: E/millisUntilFinished(8380): millisUntilFinished = 17
04-09 18:18:31.145: E/millisUntilFinished(8380): millisUntilFinished = 16
04-09 18:18:32.145: E/millisUntilFinished(8380): millisUntilFinished = 15
04-09 18:18:33.105: D/BluetoothGatt(8380): onCharacteristicWrite() - Device=C2:69:E9:57:93:A4 UUID=032a0000-0000-0000-0000-000000000000 Status=137
04-09 18:18:33.115: E/BelwithDeviceActor(8380): Bond state changed for: C2:69:E9:57:93:A4 new state: 12 previous: 11
04-09 18:18:33.115: I/System.out(8380): unregisterReceiver true
Now when Pairing is removed by OS in weird way the ACTION_BOND_STATE is change as below.
.
.
.
.
Bond state changed for: C2:69:E9:57:93:A4 new state: 10.
we also get immediate event of act=android.bluetooth.device.action.ACL_DISCONNECTED flg=0x4000010 in our APP.
what's important here, at this point we just lost pairing with the device and protected characteristics don't work for us any longer.
if we restart bt using system settings app or BluetoothAdapter::disable() and enable() we can see that we are not paired with the device.
what's funny, without the bt restart, system settings app still thinks and shows that we are paired with the device.
tested with nexus 4 running 4.4.2, nexus 5 running 4.4.2 and even Samsung galaxy s4 running 4.3.
our expectation is that:
in case of unpairing there should be system broadcast
system preferences app should show current paring status even without bt restart
We have also Observed and get the sniffed data in which we found that our encryption is set to 0x000000 when our bonding is removed by OS in weird way.
I have no idea whether you still need help or whether you eventually solved your own problem (you know, since you did post this question back in April), but I wanted to go ahead and post the workaround I came up with because I know other people are having this problem.
Using a Nexus 7, I ran basically the same tests you did and came to the same conclusion:
If the Android tablet and the remote device were already bonded, there was a high chance that calling BluetoothGatt.discoverServices() would both disconnect and unbond the tablet from the remote device. But, certain parts of the Android OS seemed completely oblivious to the unbonding; although the Broadcast Receiver you registered to listen for bonding changes was notified that the bond between the two devices had been broken, the rest of the Android OS considered the bond to still be intact. Since the OS considered the tablet and the remote device to be bonded, the tablet could not write to any of the encrypted descriptors on the remote device, giving a write status of 15 (GATT_INSUFFICIENT_ENCRYPTION) whenever a descriptor write was attempted.
The Solution
The key is to unpair the Nexus and the remote device before they have the chance to unpair themselves in that weird way. What I do is check to see if the tablet and the remote device are bonded right before I start a Bluetooth Low Energy scan. If they are paired, I remove the bond using the function below and then start the scan. Unpairing the two devices programmatically ensures that the Android OS is aware that they are no longer bonded and, therefore, will go through the usual bonding process.
Below is the code is use to check to see if the remote device is paired with the tablet and unpair it if it is:
// Get the paired devices and put them in a Set
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// Loop through the Set of paired devices, checking to see
// if one of the devices is the device you need to unpair
// from. I use the device name, but I'm sure you can find
// another way to determine whether or not its your device
// -- if you need to. :)
for (BluetoothDevice bt : pairedDevices) {
if (bt.getName().contains("String you know has to be in device name")) {
unpairDevice(bt);
}
}
// Function to unpair from passed in device
private void unpairDevice(BluetoothDevice device) {
try {
Method m = device.getClass().getMethod("removeBond", (Class[]) null);
m.invoke(device, (Object[]) null);
} catch (Exception e) { Log.e(TAG, e.getMessage()); }
}
Why waiting for the error and then solving it by restarting the Bluetooth is a bad idea...
As you have already pointed out in your question, restarting the Bluetooth after the tablet and the remote device mysteriously unbond from each other forces the Android OS to realize that it is no longer bonded to the remote deivce. This was the original workaround I used, but it soon became clear that there were two major problems that came with this "solution":
Turning the Bluetooth on and off will disconnect all of the devices that were connected to the tablet.
Turning the Bluetooth on and off wastes a lot of time.
I would only restart the Bluetooth as a last resort. For example, if the unbonding error still miraculously occurred, your only choice would be to restart the Bluetooth.
We had the same issue and we've figured out that "connectGatt" has new "transport" argument (which defines transport protocol of connection), starting from SDK v 23.
So if you want to pair your devices you should use "TRANSPORT_BREDR", if you want to only connect to peripheral without bonding - use "TRANSPORT_LE"
Documentation: https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#connectGatt(android.content.Context, boolean, android.bluetooth.BluetoothGattCallback, int)

Android Bluetooth Low Energy Motorola API pairing

I am working on using the BT 4.0 API that Motorola has provided with the RAZR. In one of their documents it states to use the Android API to pair before connecting and using their framework. Per their instructions I have been pairing with OS Bluetooth settings application, but it never prompts me for a key. It will pair but doesn't appear to bond, and this is critical for me.
My question is, when they say "using the Android API" is this referring to simply using the OS Bluetooth utility to pair before hand (like I have been doing), or is there some way to do it with code in my application. They reference the "createBond()" function which, to my knowledge, is not an accessible function (at least not without some squirrely libraries or reflection).
Any advice is greatly appreciated, especially anyone who has used the API successfully, if they could give an account of their process. I'm just looking for some clarity at this point :)
Lloyd,
You are correct, follow the instructions in the link you posted.
Outside of coding, when they say use the standard android api for "non-le" operations, they mean go ahead and pair the ble device the same way you would any bluetooth classic devices inside android settings -> wireless & network -> bluetooth -> scan for devices.
If the device you are using is a motorola le compatible device the ble device will be paired but not connected.
Now, in the code, you can detect this paired device through the same method of
BluetoothAdapter.getDefaultAdapter().getBondedDevices()
To double check if your Android Phone is LE compatible, run this code:
public static boolean checkBLESupport() {
boolean deviceSupportsLE;
try {
#SuppressWarnings({ "unused", "rawtypes" })
Class object = Class.forName("android.server.BluetoothGattService");
deviceSupportsLE = true;
} catch (Exception e) {
deviceSupportsLE = false;
}
return deviceSupportsLE;
}
And to double check if the bluetooth device you paired is LE, when you are looping through the bonded devices.
Check the device with this code.
if (device.getBluetoothClass() == null) {
Log.i(TAG, "This device is BLE compatible");
b = true;
} else {
Log.i(TAG, "This device is not BLE");
b = false;
}
Now for establishing connection from your LE compatible phone to your LE compatible bluetooth device, follow the Gatt service instructions under the link you posted. http://developer.motorola.com/docs/bluetooth-low-energy-api/
Take note that under this example it is connecting to a bluetooth low energy heart rate monitor.
If you are not trying to connect to the heart rate monitor with LE heart rate profile, here is a link to another Motorola document that details creating your own LE Profile to use with the GATT framework. http://developer.motorola.com/docs/bluetooth-low-energy-gatt-framework-api/
If the instructions are not clear enough at any point in either of these documents, motorola offers sample android applications using the frameworks in those documents.
I guess motorola stack has BLE support. But what i feel is that it does not pair with the devices that require bonding though It does work some sensors. I have tried with a proximity sensor that require bonding. It never gets paired though the devices is discovered with Razr which even does not with S3.
There's a helpful video here.
Late to the game, but can confirm -
If your BLE Peripheral requires bonding, Moto X - and some other older Motorola devices - MUST be paired via Bluetooth Settings prior to programmatic connection via the Android GATT interface.
If you bond via the createBond method, or reading of an encrypted characteristic, your connection will be dropped typically in under 60 seconds, despite DDMS logs that show a good bond may be established.

Categories

Resources