I am scanning BLE devices which are created by iOS device. Then I connect to specific service and read specific characteristic. It works perfect when iOS app which has GATT service is in the foreground. But when hide iOS server app, Android client stops detect BLE GATT devices.
public static ScanFilter[] getFilters(UUID serviceUuid) {
...
filters.add(new ScanFilter.Builder().setServiceUuid(new ParcelUuid(serviceUuid)).build());
return filters.toArray(new ScanFilter[filters.size()]);
}
public static ScanSettings getScanSettings() {
return new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) // change if needed
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) // change if needed
.build();
}
BLE Scanner app successfully sees hidden GATT server
Updates Here is filters code part
public final class ScannerUtil {
public static final List<UUID> BEACON_UUUIDs = Arrays.asList(
...........
UUID.fromString("a8427a96-70bd-4a7e-9008-6e5c3d445a2b"));
public static ScanSettings getScanSettings() {
return new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_BALANCED) // change if needed
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) // change if needed
.build();
}
public static ScanFilter[] getFilters(UUID serviceUuid) {
List<ScanFilter> filters = Stream.of(BEACON_UUUIDs)
.map(iBeaconScanFilter::setScanFilter)
.collect(toList());
filters.add(new ScanFilter.Builder().setServiceUuid(new ParcelUuid(serviceUuid)).build());
return filters.toArray(new ScanFilter[filters.size()]);
}
}
Full scanner class code is below:
public class BLEGlobalScanner {
private final ScannerConfiguration configuration;
private final Context context;
private final RxBleClient rxBleClient;
private final Map<String, String> devicesMap = new HashMap<>();
private final Map<String, DeviceApoloBeacon> beaconsMap = new HashMap<>();
private final ScanFilter[] scanFilter;
public BLEGlobalScanner(ScannerConfiguration configuration, Context context) {
this.configuration = configuration;
this.context = context;
this.rxBleClient = RxBleClient.create(context);
this.scanFilter = getFilters(configuration.beacons(), configuration.gattServer().server());
}
public Observable<BluetoothDeviceApolo> start() {
return bluetoothEnableObservable(context).switchMap(aBoolean -> startScanner())
.filter(Optional::isPresent)
.map(Optional::get);
}
private Observable<Optional<BluetoothDeviceApolo>> startScanner() {
return rxBleClient.scanBleDevices(getScanSettings(), scanFilter)
.buffer(2, TimeUnit.SECONDS)
.flatMap(rxBleDevices -> Observable.from(rxBleDevices)
.distinct(scanResult -> scanResult.getBleDevice().getMacAddress())
.concatMap(this::handleDevices)
.map(Optional::of))
.observeOn(mainThread())
.onErrorResumeNext(throwable -> {
Timber.e(throwable, "startScanner");
return Observable.just(Optional.empty());
})
.onExceptionResumeNext(Observable.just(Optional.empty()))
.retry();
}
private Observable<BluetoothDeviceApolo> handleDevices(ScanResult scanResult) {
if (beaconsMap.containsKey(scanResult.getBleDevice().getMacAddress())) {
return Observable.fromCallable(() -> beaconsMap.get(scanResult.getBleDevice().getMacAddress()))
.map(beacon -> beacon.toBuilder()
.lastSeen(System.currentTimeMillis())
.rssi(scanResult.getRssi())
.build());
} else {
return handleBeacon(scanResult)
.map(device -> (BluetoothDeviceApolo) device)
.switchIfEmpty(
handleDevice(scanResult).map(deviceApolo -> (BluetoothDeviceApolo) deviceApolo)
);
}
}
private Observable<DeviceApoloBeacon> handleBeacon(ScanResult scanResult) {
return Observable.fromCallable(() -> scanResult.getScanRecord().getManufacturerSpecificData(COMPANY_ID_APPLE))
.filter(bytes -> bytes != null)
.filter(bytes -> DeviceApoloBeacon.requiredManufactureSize == bytes.length)
.map(bytes -> DeviceApoloBeacon.builder()
.manufacturedData(bytes)
.lastSeen(System.currentTimeMillis())
.rssi(scanResult.getRssi())
.build())
.filter(beacon -> configuration.beacons().contains(beacon.uuuid()))
.doOnNext(beacon -> beaconsMap.put(scanResult.getBleDevice().getMacAddress(), beacon));
}
private Observable<DeviceApolo> handleDevice(ScanResult scanResult) {
final RxBleDevice rxBleDevice = scanResult.getBleDevice();
if (devicesMap.containsKey(rxBleDevice.getMacAddress())) {
return Observable.fromCallable(() -> devicesMap.get(rxBleDevice.getMacAddress()))
.timestamp()
.map(deviceStr -> DeviceApolo.create(deviceStr.getValue(), deviceStr.getTimestampMillis(), scanResult.getRssi()));
} else {
return readCharacteristic(rxBleDevice, scanResult.getRssi());
}
}
private Observable<DeviceApolo> readCharacteristic(RxBleDevice rxBleDevice, final int rssi) {
return rxBleDevice.establishConnection(false)
.compose(new ConnectionSharingAdapter())
.switchMap(rxBleConnection -> rxBleConnection.readCharacteristic(configuration.gattServer().characteristic()))
.map(String::new)
.doOnNext(s -> devicesMap.put(rxBleDevice.getMacAddress(), s))
.timestamp()
.map(deviceStr -> DeviceApolo.create(deviceStr.getValue(), deviceStr.getTimestampMillis(), rssi))
.retry();
}
}
when hide iOS server app
? – Dariusz SewerynAndroid client stops detect BLE GATT devices
means that it does not emit the device when scanning—are you sure that theBLE Scanner app
does not simply cache the scan results from the time the iOS app was in foreground? – Dariusz SewerynScanFilter
s you use and what is the exact flow which you subscribe to? – Dariusz Seweryn