On some android devices (regardless of the OS or Bluetooth version) there is a problem after BLE device connect.
The services / characteristics are not up to date. This usually happens when the peripheral changes his services. (while the app was not connected)
In this state, it is not possible to use the device. To verify this issue you can discover all characteristics and you see that there are outdated (no more existing) characteristics loaded from cache of the android device.
Current solution (not programmatically)
Reset the bluetooth enable state in the os system settings of android. (turn off and on the bluetooth state)
Also there is a feature in the nRF Connect app called Refresh services
(Ignore "Parse known characteristics" This is not the problem)
This project (nRF Connect) is not open source. I don't know how to "Refresh Services" / "Clear Cache" to avoid to load wrong services / characteristics on connect.
How to implement this in android (java)?
Background: I'm using ionic with the native ble plugin. I could implement native code directly in the plugin.
Usually Android should not cache not-bonded devices. BUT it ignores the rule.
To refresh the cache, call a hidden methode using reflections.
import java.lang.reflect.Method;
Do this in a method:
try {
// BluetoothGatt gatt
final Method refresh = gatt.getClass().getMethod("refresh");
if (refresh != null) {
refresh.invoke(gatt);
}
} catch (Exception e) {
// Log it
}
Usuage example:
If you see a poblem with the characteristic cache.
Call the method to clear the cache. (Wait a few seconds).
Reconnect (Disconnect -> Connect).
Should be fixed now.
NOTE: The refresh method has no complete callback.
Related
I am working on an application that makes multiple BLE connections in a minimum amount of time. I developed my application in Java Android. I set up a statistics-based system so that, among a group of phones, each phone makes connections on half the group and receives connections from the other half. This gives a pretty even distribution of BLE servers and clients. If a connection was made and failed or could not be launched, the other device that should have been a server will become a client and make the connection for the other in order to waste a minimum of time.
My problem arises when the number of devices present to make connections is greater than 5. When there are only two or three connections left to make, it takes a lot of tries to succeed. A lot of 133 errors or timeouts are thrown before getting a connection. The only time it is possible to speed up this is by clearing the Bluetooth cache. This is only possible by reflection and on the current connection. This operation doesn't work all the time and I'm trying to find out how to clear my own Bluetooth cache without it, faster and more reliably. Does anyone have any advice or idea to help me?
Here is the reflexive method we use to clear another's cache through a Gatt connection:
/**
* Method to refresh cache device
*/
private boolean refreshDeviceCache(BluetoothGatt gatt){
try {
Method localMethod = gatt.getClass().getMethod("refresh");
return (boolean) (Boolean) localMethod.invoke(gatt, new Object[0]);
}
catch (Exception localException) {
Log.e(TAG, "An exception occurred while refreshing device");
}
return false;
}
This method not being recommended, we had no choice since there is no other solution, and for the moment we haven't found one yet.
So my android app is behaving as a beacon, means, it will be advertising and other BLE devices will be connecting to it. Well, this is how our project is working so please don't raise questions on this as why am i using my app as a beacon and not as a scanner. Anyways, It behaves as a beacon and starts advertising and now I want to know that if a device connected to it. I cant find a way how to do this.
Of course, I am using this flutter package. beacon_broadcast 0.3.0
This is my code.
void startAdvertising() {
BeaconBroadcast beaconBroadcast = BeaconBroadcast();
beaconBroadcast
.setUUID(advertisingUUID)
.setMajorId(1)
.setMinorId(100)
.start();
}
First, Flutter is just a UI toolkit and has no support for other system APIs such as Bluetooth.
You should therefore look what the official Android APIs offer in the first place. Usually when using BluetoothLeAdvertiser for advertising, one often also adds an instance of BluetoothGattServer in order to handle connections. If you have created a BluetoothGattServer using openGattServer, you will get a onConnectionStateChange callback whenever a device connects or disconnects. So that answers your question how an Android app can get notified when a device connects. You probably also want to use the same API to add a GATT service so that the other device can communicate with your app. Other alternatives is to use the GATT client API if it's the other device that has a GATT server, or you might want to use the L2CAP CoC API.
Note that if Bluetooth is turned off/disabled/restarted, your BluetoothGattServer object will automatically die and you need to recreate it. To get notified when this happens, use a state change intent receiver for BluetoothAdapter.ACTION_STATE_CHANGED as explained in this example https://stackoverflow.com/a/9694138/556495 to recreate your BluetoothGattServer (and advertiser) when state is changed to STATE_ON.
Now, since you want to use Flutter but Flutter uses Dart, you cannot directly consume the Android APIs. Instead you need to write a bridge/plugin, to bridge your Dart code and Java code. See https://docs.flutter.dev/development/platform-integration/platform-channels for a tutorial how to do this. If you're lucky, someone else might have already created such a package that does exactly what you want. Unfortunately, the beacon_broadcast package you found, only implements BluetoothLeAdvertiser and not BluetoothGattServer, as can be seen by the source code here: https://github.com/pszklarska/beacon_broadcast/tree/master/android/src/main/kotlin/pl/pszklarska/beaconbroadcast.
I'm slightly familiar with BLE and I am facing some problem with an inherited code. So the app works like that:
With BLE enabled the app scans for devices
The app displays the devices found
The user selects the device to pair with
The app pairs with the device
The problem I'm facing is that after pairing several times (it varies) the phone is not able to discover devices, hence blocking the user to pair.
I'm using GattServer to connect with the client device, and I'm reseting the services as below:
public void resetBluetoothGattServer() {
Log.i(TAG," resetBluetoothGattServer: bluetoothGattServer: "+ bluetoothGattServer);
if (bluetoothGattServer != null) {
if(!bluetoothGattServer.getServices().isEmpty()){
Log.i(TAG," resetBluetoothGattServer: clearing services on bluetooth Gatt Server");
bluetoothGattServer.clearServices();
}
Log.i(TAG," resetBluetoothGattServer: closing bluetoothGattServer");
bluetoothGattServer.close();
}
bluetoothGattServer = openGattServer();
}
Restarting the phone, turning bluetooth off and then back on, and uninstalling and installing the app won't fix the problem. The only solution is to clear the cache from the Bluetooth Share app on the android apps manager.
This post How to programmatically force bluetooth low energy service discovery on Android without using cache adresses to a similar problem but since we are not using BluetoothGatt to connect it's no a suitable solution. Neither will be to refactor the whole inherited code.
I'm asking you if there is a way to clear the cache programmatically using BluetoothGattServer.
One solution - solve this issue using reflection.
private void refreshDeviceCache(BluetoothGatt gatt) {
try {
Method localMethod = gatt.getClass().getMethod("refresh");
if(localMethod != null) {
localMethod.invoke(gatt);
}
} catch(Exception localException) {
Log.d("Exception", localException.toString());
}
}
Note : I am not recommended this way
I am attempting to get Google Nearby API working on my handset (an s5).
I am building and running the stock project from github Google Nearby API GIT.
The app builds and runs, with no errors. Having exported the app onto two S5s (amongst other handsets I have attempted to test it with) and connecting to a WLAN from a D-Link DSL-3680. Multicasting is enabled and set to v3.
However the app refuses to connect with the neighbouring phone when corresponding 'advertise' and 'discover' instructions have been given.
Is there an effective way in which to debug this behaviour? If I can provide an effective information dump of information that might help someone identify the issue then please let me know how.
What do you mean by 'refuse to connect'?
are you getting connection status- 'Rejected'?
If you are able to advertise and discover other devices, I'm assuming all your base conditions (like connected to local network) are fulfilled
Now,
You can try logging your status in Connection call back when you try to connect
Nearby.Connections.sendConnectionRequest(mGoogleApiClient, myName,
remoteEndpointId, myPayload, new Connections.ConnectionResponseCallback() {//response conditions}
using--
inside connection callback function write
if(status.isSuccess()){
// Successful connection
} else {
// Failed connection
}
similarly, if you are not doing this, you need to accept the connection request
Nearby.Connections.acceptConnectionRequest(mGoogleApiClient, remoteEndpointId, myPayload, this)
and inside Onresult callback add-
if(status.isSuccess()){
// Successful connection
} else {
// Failed connection
}
Hope it helped
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.