We have a BLE device we are working on that outputs data via the device's name. The device is functioning properly and can be seen changing names properly using an app like nRF Connect. However, we are having a difficult time doing the same in our own Android app. We can detect the devices fine, but they will almost never move past the original names they were given.
The code I started with has a loop that is started in onResume() that scans using a BluetoothLeScanner and the startScan() function.
public void BLEScan(final boolean enable){
final int SCAN_PERIOD = 12000;
final BluetoothLeScanner bluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
if (enable) {
Log.d(TAG, "Starting Scan");
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
BLEScan(false);
invalidateOptionsMenu();
}
}, SCAN_PERIOD);
bluetoothLeScanner.flushPendingScanResults(mLeScanCallback);
bluetoothLeScanner.startScan(mLeScanCallback);
} else {
Log.d(TAG, "Stopping Scan");
bluetoothLeScanner.flushPendingScanResults(mLeScanCallback);
bluetoothLeScanner.stopScan(mLeScanCallback);
mHandler.removeCallbacksAndMessages(null);
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
BLEScan(true);
}
}, SCAN_PERIOD);
}
}
And it is detected in mLeScanCallback that filters based on devices. Basically, if it hasn't seen a device before, it adds it to a list of Beacons. But, if it has seen it before, it updates the values with the Beacon's seen() function with the Beacon's name as seen as that's where the information will come from. In both cases, it will update its adapter to populate the new information.
private ScanCallback mLeScanCallback =
new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
final ScanResult res2 = result;
runOnUiThread(new Runnable() {
@Override
public void run() {
BluetoothDevice device = res2.getDevice();
String address = device.getAddress();
if (device.getName() != null){
if (device.getName().contains("ABT:")){
if (!mLeBeacons.containsKey(address)){
BleBeacon beacon = new BleBeacon(device.getName(), address);
bleList.add(beacon);
adapter.notifyDataSetChanged();
mLeBeacons.put(device.getAddress(), beacon);
} else {
for (int x = 0; x < bleList.size(); x++){
if (device.getAddress().equals(bleList.get(x).getMAC())){
bleList.get(x).seen(device.getName());
adapter.notifyDataSetChanged();
}
}
}
}
}
}
});
}
};
However, even after updating the name, mLeScanCallback will only ever return the original name.
After some searching through here, the thing I kept finding was to use the function fetchUuidsWithSdp() and intents such as ACTION_FOUND, ACTION_UUID, ACTION_DISCOVERY_FINISHED, and ACTION_NAME_CHANGED to see the name change properly. So, I added fetchUuidsWithSdp() to mLeScanCallback. However, while this would trigger the Intents, the name still wasn't updating. I tried calling fetchUuidsWithSdp() in the actual intents, but that didn't help either. Strange enough, ACTION_NAME_CHANGED would occasionally fire if I turned off my phone's screen or walked far away enough from the BLE device. But, all onPause() does is call super.onPause() and BLEScan(false). And, since these were things I was already doing in my loop, I wasn't sure how to bring this into my code while it was awake.
After more searching, I found that, to use the fetchUuidsWithSdq() function, you need to use the startDiscovery() function of your BluetoothAdapter. So, I changed BLEScan to use it, cutting out mLeScanCallback completely.
public void BLEScan(final boolean enable){
final int SCAN_PERIOD = 12000;
if (enable) {
Log.d(TAG, "Starting Scan");
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
BLEScan(false);
invalidateOptionsMenu();
}
}, SCAN_PERIOD);
if (mBluetoothAdapter.isDiscovering()){
mBluetoothAdapter.cancelDiscovery();
}
mBluetoothAdapter.startDiscovery();
} else {
Log.d(TAG, "Stopping Scan");
mBluetoothAdapter.cancelDiscovery();
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
BLEScan(true);
}
}, SCAN_PERIOD);
}
}
However, while the Intents were firing before, they weren't now, even though they were in my intent filter. After doing some more searching, I found some people saying that startDiscovery() doesn't work with Le devices. So, I searched for what you're supposed to use instead...which brought me all the way back to startScan with BluetoothLeScanner. At that point, I'd realized I'd gone in a circle and needed help.
I went over my entire process because, somewhere along the lines, I missed something. I just don't know where it was. Should I be using startScan()? Am I not using startDiscovery() properly? Or is there something else I should be using entirely? The fact that ACTION_NAME_CHANGED fired occasionally makes me want to go back to that, but how do I get it working at all times when the device is awake?