Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to programmatically force bluetooth low energy service discovery on Android without using cache

I am using Android 4.4.2 on a Nexus 7. I have a bluetooth low energy peripheral whose services change when it is rebooted. The android app calls BluetoothGatt.discoverServices(). However Android only queries the peripheral once to discover services, subsequent calls to discoverServices() result in the cached data from the first call, even between disconnections. If I disable/enable the Android bt adapter then discoverServices() refreshes the cache by querying the peripheral. Is there a programmatic way to force Android to refresh its' ble services cache without disabling/enabling the adapter?

like image 904
monzie Avatar asked Mar 23 '14 20:03

monzie


People also ask

Why is BLE low power?

How Does BLE Use Less Power? BLE uses the same radio wavebands as Bluetooth and allows two devices to exchange data in many of the same ways. The difference is that BLE devices remain asleep in between connections. They are also designed to only communicate for a few seconds when they do connect.

What is Bluetooth low energy?

Bluetooth Low Energy is a power-conserving variant of Bluetooth personal area network (PAN) technology, designed for use by Internet-connected machines and appliances. Also marketed as Bluetooth Smart, Bluetooth LE was introduced in the Bluetooth 4.0 specification as an alternative to Bluetooth Classic.

What is BLE settings Android?

Android provides built-in platform support for Bluetooth Low Energy (BLE) in the central role and provides APIs that apps can use to discover devices, query for services, and transmit information. Common use cases include the following: Transferring small amounts of data between nearby devices.


2 Answers

I just had the same problem. If you see the source code of BluetoothGatt.java you can see that there is a method called refresh()

/** * Clears the internal cache and forces a refresh of the services from the  * remote device. * @hide */ public boolean refresh() {         if (DBG) Log.d(TAG, "refresh() - device: " + mDevice.getAddress());         if (mService == null || mClientIf == 0) return false;          try {             mService.refreshDevice(mClientIf, mDevice.getAddress());         } catch (RemoteException e) {             Log.e(TAG,"",e);             return false;         }          return true; } 

This method does actually clear the cache from a bluetooth device. But the problem is that we don't have access to it. But in java we have reflection, so we can access this method. Here is my code to connect a bluetooth device refreshing the cache.

private boolean refreshDeviceCache(BluetoothGatt gatt){     try {         BluetoothGatt localBluetoothGatt = gatt;         Method localMethod = localBluetoothGatt.getClass().getMethod("refresh", new Class[0]);         if (localMethod != null) {            boolean bool = ((Boolean) localMethod.invoke(localBluetoothGatt, new Object[0])).booleanValue();             return bool;          }     }      catch (Exception localException) {         Log.e(TAG, "An exception occurred while refreshing device");     }     return false; }           public boolean connect(final String address) {            if (mBluetoothAdapter == null || address == null) {             Log.w(TAG,"BluetoothAdapter not initialized or unspecified address.");                 return false;         }             // Previously connected device. Try to reconnect.             if (mBluetoothGatt != null) {                 Log.d(TAG,"Trying to use an existing mBluetoothGatt for connection.");               if (mBluetoothGatt.connect()) {                     return true;                } else {                 return false;                }         }          final BluetoothDevice device = mBluetoothAdapter                 .getRemoteDevice(address);         if (device == null) {             Log.w(TAG, "Device not found.  Unable to connect.");             return false;         }          // We want to directly connect to the device, so we are setting the         // autoConnect         // parameter to false.         mBluetoothGatt = device.connectGatt(MyApp.getContext(), false, mGattCallback));         refreshDeviceCache(mBluetoothGatt);         Log.d(TAG, "Trying to create a new connection.");         return true;     } 
like image 50
Miguel Avatar answered Oct 07 '22 20:10

Miguel


Indeed Miguel's answer works. To use refreshDeviceCache, I'm successful with this calling order:

// Attempt GATT connection public void connectGatt(MyBleDevice found) {     BluetoothDevice device = found.getDevice();     gatt = device.connectGatt(mActivity, false, mGattCallback);     refreshDeviceCache(gatt); } 

This works for OS 4.3 through 5.0 tested with Android and iPhone Peripherals.

like image 26
Thomas Avatar answered Oct 07 '22 21:10

Thomas