Open brinata opened 6 months ago
After some tests, the permission request function implemented inside the scanner for android is not compatibile with the api version = 33, most probably because the p4a is changed.
the api version 33 doesn't like the request ACCESS_BACKGROUND_LOCATION.
after removing it for API version >= 30, the scanner is working, but the client give out the followind error:
if not descriptor:
raise BleakError(f"Descriptor {desc_specifier} was not found!")
after connecting, when calling :
await client.start_notify(UART_TX_CHAR_UUID, self.handle_rx)
Please find the logs:
04-22 18:07:23.649 26795 26958 I python : Start Scanning 04-22 18:07:23.968 26795 26958 I python : [DEBUG ] Starting BTLE scan 04-22 18:07:25.110 26795 26795 I python : [True, True] 04-22 18:07:27.559 26795 26795 I python : [True, True, True, True] 04-22 18:07:27.609 26795 26958 I python : [DEBUG ] Waiting for android api onScan 04-22 18:07:27.710 26795 26958 I python : [DEBUG ] Java state transfer onScan error=None data=(<android.bluetooth.le.ScanResult at 0x7434cafcb0 jclass=android/bluetooth/le/ScanResult jself=<LocalRef obj=0xab0e at 0x7434cab090>>,) 04-22 18:07:27.710 26795 26958 I python : [DEBUG ] onScan succeeded (<android.bluetooth.le.ScanResult at 0x7434cafcb0 jclass=android/bluetooth/le/ScanResult jself=<LocalRef obj=0xab0e at 0x7434cab090>>,) 04-22 18:07:27.826 26795 26958 I python : Nulla !!! 04-22 18:07:27.875 26795 26958 I python : trovato! 04-22 18:07:27.875 26795 26958 I python : [DEBUG ] Stopping BTLE scan 04-22 18:07:27.881 26795 26958 I python : Device found 04-22 18:07:27.884 26795 26958 I python : Attesa connessione 04-22 18:07:27.887 26795 26958 I python : [DEBUG ] [Connecting to BLE device @ BB]3D:7A:F6:4F:78 04-22 18:07:27.888 26795 26958 I python : [DEBUG ] Waiting for android api onConnectionStateChange 04-22 18:07:28.588 26795 26958 I python : [DEBUG ] Java state transfer onConnectionStateChange error=None data=(2,) 04-22 18:07:28.588 26795 26958 I python : [DEBUG ] onConnectionStateChange succeeded () 04-22 18:07:28.589 26795 26958 I python : [DEBUG ] Connection successful. 04-22 18:07:28.590 26795 26958 I python : [DEBUG ] requesting mtu... 04-22 18:07:28.590 26795 26958 I python : [DEBUG ] Waiting for android api onMtuChanged 04-22 18:07:29.316 26795 26958 I python : [DEBUG ] Java state transfer onMtuChanged error=None data=(247,) 04-22 18:07:29.318 26795 26958 I python : [DEBUG ] onMtuChanged succeeded (247,) 04-22 18:07:29.318 26795 26958 I python : [DEBUG ] discovering services... 04-22 18:07:29.319 26795 26958 I python : [DEBUG ] Waiting for android api onServicesDiscovered 04-22 18:07:29.338 26795 26958 I python : [DEBUG ] Java state transfer onServicesDiscovered error=None data=() 04-22 18:07:29.340 26795 26958 I python : [DEBUG ] onServicesDiscovered succeeded () 04-22 18:07:29.341 26795 26958 I python : [DEBUG ] Get Services... 04-22 18:07:29.371 26795 26958 I python : Connected, start typing and press ENTER... 04-22 18:07:29.375 26795 26958 I python : ERROR Descriptor None was not found! 04-22 18:07:29.400 26795 26958 I python : ERROR Descriptor None was not found! 04-22 18:07:29.415 26795 26958 I python : [DEBUG ] BTLE scan already stopped
and the implemented python code:
async def ble_service(self):
await asyncio.sleep(1)
print("Start Scanning")
device = None
while True:
if self.scanning :
try:
#cancello il device se già esistente
if device is not None:
del device
device = await BleakScanner.find_device_by_filter(self.match_nus_uuid)
if device is None:
print("Device not present")
else:
print("Device found")
try:
client = BleakClient(device, disconnected_callback=self.handle_disconnect, timeout=5)
print("Attesa connessione")
self.connected = await client.connect()
if self.connected is True:
self.topbar_status_color = 'green'
print("Connected, start typing and press ENTER...")
await client.start_notify(UART_TX_CHAR_UUID, self.handle_rx)
# ottengo il servizio di interesse
print("uno")
nus = client.services.get_service(UART_SERVICE_UUID)
# ottengo la caratteristica di interesse
rx_char = nus.get_characteristic(UART_RX_CHAR_UUID)
print("due")
await asyncio.sleep(0.5)
self.invia_comando(BLE.BLE_ACT_READ)
# Clock.schedule_once(self.invia_comando, 0.5)
print("entro nel loop")
while True:
await asyncio.sleep(0.25)
if self.end_app is True:
return
if self.connected is False:
# if client.is_connected() is False:
print("dispositivo disconnesso, esco dal loop")
while not self.tx_queue.empty():
# Depending on your program, you may want to
# catch QueueEmpty
self.tx_queue.get_nowait()
self.tx_queue.task_done()
await client.disconnect()
break
if self.tx_queue.empty() is False:
data = self.tx_queue.get_nowait()
print(data)
data = bytes([STX]) + data + bytes([ETX])
minimo = min(len(data), 20, rx_char.max_write_without_response_size)
self.ms = time.time()
for s in sliced(data, minimo):
await client.write_gatt_char(rx_char, s)
print("sent:", data)
except BleakError as e:
print(f"ERROR {e}")
Clock.schedule_once(lambda dt: self.scanning_dialog(f"ERROR {e}"), 0.05)
await asyncio.sleep(2)
except BleakError as e:
print(f"ERROR {e}")
Clock.schedule_once(lambda dt: self.scanning_dialog(f"ERROR {e}"), 0.05)
await asyncio.sleep(2)
except jnius.jnius.JavaException as e:
print(f"ERROR {e}")
Clock.schedule_once(lambda dt: self.scanning_dialog(f"ERROR {e}"), 0.05)
await asyncio.sleep(2)
else:
await asyncio.sleep(2)
Permission issue is possible duplicate of #1363.
Descriptor issues is possible duplicate of/related to #883
the BLE define:
UART_SERVICE_UUID = "6E400001-B5A3-F393-E0A9-E50E24DCCA9E" UART_RX_CHAR_UUID = "6E400002-B5A3-F393-E0A9-E50E24DCCA9E" UART_TX_CHAR_UUID = "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
the print result inside the start_notify:
print(characteristic) --> 6e400003-b5a3-f393-e0a9-e50e24dcca9e (Handle: 14): Nordic UART TX
before calling start_noify:
print(client.services.characteristics) -->
04-22 18:44:38.587 32277 32401 I python : {3: <bleak.backends.p4android.characteristic.BleakGATTCharacteristicP4Android object at 0x7434bf7010>, 5: <bleak.backends.p4android.characteristic.BleakGATTCharacteristicP4Android object at 0x7434bf7050>, 8: <bleak.backends.p4android.characteristic.BleakGATTCharacteristicP4Android object at 0x7434bbbb10>, 12: <bleak.backends.p4android.characteristic.BleakGATTCharacteristicP4Android object at 0x7434bbbad0>, 14: <bleak.backends.p4android.characteristic.BleakGATTCharacteristicP4Android object at 0x7434bb9250>}
Can you suggest any workaround ?
Descriptor issue:
After some test i discovered that the issue is in the add_descriptor function ( \bleak\backends\p4android\characteristic.py)
i added some debug print and modification as follows:
def add_descriptor(self, descriptor: BleakGATTDescriptor):
"""Add a :py:class:`~BleakGATTDescriptor` to the characteristic.
Should not be used by end user, but rather by `bleak` itself.
"""
self.__descriptors.append(descriptor)
print("descriptor.uuid...", descriptor.uuid)
print("defs.CLIENT_CHARACTERISTIC_CONFIGURATION_UUID...", defs.CLIENT_CHARACTERISTIC_CONFIGURATION_UUID)
# if descriptor.uuid == defs.CLIENT_CHARACTERISTIC_CONFIGURATION_UUID:
if defs.CLIENT_CHARACTERISTIC_CONFIGURATION_UUID in descriptor.uuid:
self.__notification_descriptor = descriptor
print(self.__notification_descriptor)
it works.
the problem is that the fileds "descriptor.uuid" and "defs.CLIENT_CHARACTERISTIC_CONFIGURATION_UUID" are different, so the operation "self.__notification_descriptor = descriptor" never happens
Permission issue:
i modified the "start" function of the scanner as follow:
async def start(self) -> None:
if BleakScannerP4Android.__scanner is not None:
raise BleakError("A BleakScanner is already scanning on this adapter.")
logger.debug("Starting BTLE scan")
loop = asyncio.get_running_loop()
if self.__javascanner is None:
if self.__callback is None:
self.__callback = _PythonScanCallback(self, loop)
permission_acknowledged = loop.create_future()
def handle_permissions(permissions, grantResults):
if any(grantResults):
print(grantResults)
loop.call_soon_threadsafe(
permission_acknowledged.set_result, grantResults
)
else:
loop.call_soon_threadsafe(
permission_acknowledged.set_exception(
BleakError("User denied access to " + str(permissions))
)
)
request_permissions(
[
Permission.ACCESS_FINE_LOCATION,
Permission.ACCESS_COARSE_LOCATION
# "android.permission.ACCESS_BACKGROUND_LOCATION",
],
handle_permissions,
)
await permission_acknowledged
VERSION = autoclass('android.os.Build$VERSION')
if VERSION.SDK_INT < 30:
permission_acknowledged.cancel()
permission_acknowledged = loop.create_future()
request_permissions(
[
# Permission.ACCESS_FINE_LOCATION,
# Permission.ACCESS_COARSE_LOCATION,
"android.permission.ACCESS_BACKGROUND_LOCATION"
],
handle_permissions,
)
await permission_acknowledged
permission_acknowledged.cancel()
permission_acknowledged = loop.create_future()
request_permissions(
[
Permission.BLUETOOTH_SCAN,
Permission.BLUETOOTH_CONNECT,
Permission.BLUETOOTH_ADMIN,
Permission.BLUETOOTH
],
handle_permissions,
)
await permission_acknowledged
self.__adapter = defs.BluetoothAdapter.getDefaultAdapter()
if self.__adapter is None:
raise BleakError("Bluetooth is not supported on this hardware platform")
if self.__adapter.getState() != defs.BluetoothAdapter.STATE_ON:
raise BleakError("Bluetooth is not turned on")
self.__javascanner = self.__adapter.getBluetoothLeScanner()
BleakScannerP4Android.__scanner = self
filters = cast("java.util.List", defs.List())
if self._service_uuids:
for uuid in self._service_uuids:
filters.add(
defs.ScanFilterBuilder()
.setServiceUuid(defs.ParcelUuid.fromString(uuid))
.build()
)
scanfuture = self.__callback.perform_and_wait(
dispatchApi=self.__javascanner.startScan,
dispatchParams=(
filters,
defs.ScanSettingsBuilder()
.setScanMode(self.__scan_mode)
.setReportDelay(0)
.setPhy(defs.ScanSettings.PHY_LE_ALL_SUPPORTED)
.setNumOfMatches(defs.ScanSettings.MATCH_NUM_MAX_ADVERTISEMENT)
.setMatchMode(defs.ScanSettings.MATCH_MODE_AGGRESSIVE)
.setCallbackType(defs.ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.build(),
self.__callback.java,
),
resultApi="onScan",
return_indicates_status=False,
)
self.__javascanner.flushPendingScanResults(self.__callback.java)
try:
async with async_timeout(0.2):
await scanfuture
except asyncio.exceptions.TimeoutError:
pass
except BleakError as bleakerror:
await self.stop()
if bleakerror.args != (
"onScan",
"SCAN_FAILED_APPLICATION_REGISTRATION_FAILED",
):
raise bleakerror
else:
# there might be a clearer solution to this if android source and vendor
# documentation are reviewed for the meaning of the error
# https://stackoverflow.com/questions/27516399/solution-for-ble-scans-scan-failed-application-registration-failed
warnings.warn(
"BT API gave SCAN_FAILED_APPLICATION_REGISTRATION_FAILED. Resetting adapter."
)
def handlerWaitingForState(state, stateFuture):
def handleAdapterStateChanged(context, intent):
adapter_state = intent.getIntExtra(
defs.BluetoothAdapter.EXTRA_STATE,
defs.BluetoothAdapter.STATE_ERROR,
)
if adapter_state == defs.BluetoothAdapter.STATE_ERROR:
loop.call_soon_threadsafe(
stateOffFuture.set_exception,
BleakError(f"Unexpected adapter state {adapter_state}"),
)
elif adapter_state == state:
loop.call_soon_threadsafe(
stateFuture.set_result, adapter_state
)
return handleAdapterStateChanged
logger.info(
"disabling bluetooth adapter to handle SCAN_FAILED_APPLICATION_REGSTRATION_FAILED ..."
)
stateOffFuture = loop.create_future()
receiver = BroadcastReceiver(
handlerWaitingForState(
defs.BluetoothAdapter.STATE_OFF, stateOffFuture
),
actions=[defs.BluetoothAdapter.ACTION_STATE_CHANGED],
)
receiver.start()
try:
self.__adapter.disable()
await stateOffFuture
finally:
receiver.stop()
logger.info("re-enabling bluetooth adapter ...")
stateOnFuture = loop.create_future()
receiver = BroadcastReceiver(
handlerWaitingForState(
defs.BluetoothAdapter.STATE_ON, stateOnFuture
),
actions=[defs.BluetoothAdapter.ACTION_STATE_CHANGED],
)
receiver.start()
try:
self.__adapter.enable()
await stateOnFuture
finally:
receiver.stop()
logger.debug("restarting scan ...")
return await self.start()
it seems it works. the issue is in the api version : if you ask for all the postion permissions simultaneously the OS always replys immediatly with a negative answare. If yoy ask for the ACCESS_BACKGROUND_LOCATION later it works
Trying to run the basic example given for android https://github.com/hbldh/bleak/tree/develop/examples/kivy
Added Java files from https://github.com/hbldh/bleak/tree/develop/bleak/backends/p4android/java/com/github/hbldh/bleak to local folder java and in buildozer.spec modifed android.add_src = java
log:
04-22 10:53:06.613 12555 12647 I python : [INFO ] [example ]scanning 04-22 10:53:06.614 12555 12647 I python : [DEBUG ] Starting BTLE scan 04-22 10:53:06.710 12555 12647 I python : [INFO ] [example ]ERROR User denied access to ['android.permission.ACCESS_FINE_LOCATION', 'android.permission.ACCESS_COARSE_LOCATION', 'android.permission.ACCESS_BACKGROUND_LOCATION'] 04-22 10:53:06.710 12555 12647 I python : [ERROR ] [Exception in callback None() 04-22 10:53:06.710 12555 12647 I python : handle]
04-22 10:53:06.710 12555 12647 I python : Traceback (most recent call last):
04-22 10:53:06.710 12555 12647 I python : File "/home/ronni/progetti/app_bleak/.buildozer/android/platform/build-arm64-v8a/build/other_builds/python3/arm64-v8a__ndk_target_21/python3/Lib/asyncio/events.py", line 80, in _run
04-22 10:53:06.710 12555 12647 I python : TypeError: 'NoneType' object is not callable