capacitor-community/bluetooth-le

Bluetooth Scan not working (Working cordova-plugin-ble, not capacitor-bluetooth)

corentin35000 opened this issue · 11 comments

Good morning,

I have a problem, we are working on a connected watch project, a multi-year project, we want to migrate everything to capacitor. But since capacitor, we no longer detect our watches during the scan (requestLEScan). I receive callbacks from other approximate devices, but never our watches. Whereas before that work very well with cordova. We pass the service on cordova to sort the bluetooth devices, we do the same we find nothing, suddenly we scan the whole thing but even for that we find nothing. Do you have any idea what was changed? Knowing that from a telephone we do not detect watches connected in basic bluetooth.

Code cordova is working :

public scan(deviceId: number): Promise {
return new Promise((resolve, reject) => {
// Stop BLE scan after SCAN_DURATION seconds
const timeout = setTimeout(() => this.ble.stopScan().then(() => {
this.logger.info('BLEService: BLE scan timeout. BLE scan has been stopped.');
reject("BLE scan timeout. BLE scan has been stopped.");
}), SCAN_DURATION * 1000);

		this.logger.info('BLEService: Start scanning');
		this.ble
			.startScan([this.bleUtils.EDDYSTONE_SERVICE_UUID_SHORT])
			.pipe(
				tap(d => this.logger.info(`BLEService: Scanning found : ${JSON.stringify(d)}`)),
				map(d => this.bleUtils.parseAdvertisingData(d)),
				tap(d => this.logger.info(`BLEService: Scanning for device ${deviceId} found : ${d.id}`)),
				filter((d: BoraDevice) => d.id === deviceId),
				take(1)
			)
			.subscribe(
				device =>
					this.ble
						.stopScan()
						.then(() => {
							clearTimeout(timeout);
							setTimeout(() => {
								resolve(device);
							}, 1000);
						})
						.catch(error => this.logger.error(`BLEService: Stopscan error : ${error}`)),
				error => {
					this.logger.error(`BLEService: Startscan error : ${error}`);
					reject(`Startscan error : ${error}`);
				}
			);
	});
}
pwespi commented

A few reasons why a peripheral device might not show up in the scan:

  • it is already connected to a central device
  • it is not a Bluetooth Low Energy device (this plugin does not support Bluetooth classic)
  • the scan filters are wrong
  • the location permission has not been granted on Android (then no device would show)

You mentioned already most of these points, so I don't know what could be the problem in your case.

Just driving by with a thought...

I wonder how ParcelUUID.fromString transforms a short UUID?

This plugin uses that method directly: https://github.com/capacitor-community/bluetooth-le/blob/main/android/src/main/java/com/capacitorjs/community/plugins/bluetoothle/BluetoothLe.kt#L756

Compared the cordova-plugin-ble-central which has it's own helper method to transform the short UUID into a full 128-bit one: https://github.com/don/cordova-plugin-ble-central/blob/2349ef07b54afe2d4d30dadfbfd43e307a147490/src/android/UUIDHelper.java#L29

This would hint at your 3rd point there @pwespi that the filter doesn't apply properly.

@corentin35000 what happens if you try this Service UUID instead (this is the long form of the EDDYSTONE UUID)?

"0000FEAA-0000-1000-8000-00805F9B34FB"

See https://stackoverflow.com/a/32838954/156169

I'm assuming you're on the standard Eddystone Service UUID of 0xFEAA?

pwespi commented

Thank you @peitschie for you thoughts. It's a good idea to look into the scan filters with more detail.

However, this plugin already expects a 128-bit UUID and checks it in the JavaScript layer of the plugin, see https://github.com/capacitor-community/bluetooth-le/blob/main/src/validators.ts

@peitschie @pwespi
I work with @corentin35000 on this.
We call numberToUUID(0xfeaa) to generate the long UUID and we saw that the generated UUID was "0000FEAA-0000-1000-8000-00805F9B34FB"
But no bands with this service was detected by the scan althought other devices are detected if we don't filter the scan

@corentin35000 has forgotten to tell that the same code works under Android 11. It doesn't work under Android 12+

@pwespi to answer to your questions:

  • it is already connected to a central device
    

We have double checked this and there was no connection to a central device

  • it is not a Bluetooth Low Energy device (this plugin does not support Bluetooth classic)
    

Eddystone is a BLE beacon

  • the scan filters are wrong
    

Without filter, the eddystone beacon are not detected

  • the location permission has not been granted on Android (then no device would show)
    

other devices are detected so i don't think there is a probleme with the location permission and it works with cordova which need the same permissions

@CodeTroopers do you have any references to android:usesPermissionFlags="neverForLocation" in any of the Android manifests in your project? And to confirm, are you asking for ACCESS_FINE_LOCATION on Android 12 at runtime when the scan is run?

The reason I ask is that Android 12+ has some documentation suggesting BLE beacons are filtered from scan results if one of these are missing: https://developer.android.com/guide/topics/connectivity/bluetooth/permissions#assert-never-for-location

@peitschie Yes we have any reference to neverForLocation

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" android:maxSdkVersion="30" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" android:usesPermissionFlags="neverForLocation">

I'm not sure if we have tried without neverForLocation. I check that and tell you

pwespi commented

If it works with Android 11 but not Android 12 and you're using neverForLocation then this is probably the reason.

This is from the link peitschie posted above:

Note: If you include neverForLocation in your android:usesPermissionFlags, some BLE beacons are filtered from the scan results.

Hello guys,

I come back to you regarding Android 12 or +.

It still doesn't work despite your remarks, here are the Android permissions in the Manifest:

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
    <uses-permission android:name="android.permission.INTERNET" />

Init BleClient :

private async initBleClient(): Promise<void> {
  try {
	  await BleClient.initialize();
  } catch(error) {
	  this.logger.error('error init ble client : ' + error);
  }
}

Thank you all for your feedback !

pwespi commented

Hm strange. Just to make sure, have you tried completely uninstalling the app and reinstalling it when you made the permission changes?