Android backend (BeeWare)

This backend is intended for Android apps using the BeeWare toolchain:

Briefcase is used to build the application. Under the hood, it uses Chaquopy to package the Python application into an Android app and to bind to the Android Java APIs. Additionally, a GUI using the Toga library is required for requesting Bluetooth permissions.

The BeeWare backend classes are located in the bleak.backends.android package and are automatically selected when the application is built with Briefcase.

This backend requires Python 3.13 or later, as Android is only officially supported since that version (see PEP 738) and only since then can an Android environment be reliably detected via sys.platform == "android" at runtime or via environment markers (sys_platform == "android") for dependency resolution.

Briefcase Configuration

To use Bluetooth functionality in an application built with Briefcase, some settings must be configured in the pyproject.toml file.

Static proxies must be defined so that Java callbacks from Android can be forwarded to the Python implementations in Bleak:

build_gradle_extra_content = """
android.defaultConfig.python.staticProxy(
    'bleak.backends.android.scanner_callback',
    'bleak.backends.android.client_callback',
    'bleak.backends.android.broadcast'
)
"""

Additionally, the required Bluetooth permissions must be added. Briefcase has an option to add Bluetooth permissions to the app (available since Briefcase v0.3.26):

permission.bluetooth = "This app uses Bluetooth to communicate with nearby Bluetooth devices."

This will automatically add the Bluetooth permissions to the application’s AndroidManifest.xml. But this only means that the app can request Bluetooth permissions, not that the app automatically has them. The app must still check for and request the permissions at runtime from the user. This is done automatically by Bleak when the BleakScanner or BleakClient is used for the first time. If the app does not yet have Bluetooth permission, it will be automatically requested via a popup dialog by the Android OS:

Android Bluetooth permission request dialog

For an example of building an Android Bluetooth app using BeeWare, see the briefcase testbed.

Backend Specific Quirks

On Android, no more than 5 start/stop scanning operations are allowed per 30 seconds! See also this issue comment or this PR in the Android OS. Before Android 13 (API level 33), if this limit is exceeded, scanning simply won’t work without producing an error. Therefore, Bleak tracks the start times of scans itself and raises a BleakError when starting a scan would exceed the limit. The error message includes the time to wait before scanning is possible again.

BleakAdapter.get_connected_devices() can only filter by service UUIDs of devices for which Android has already performed GATT service discovery (e.g. devices that were connected with BleakClient before). Devices connected by other apps may not be found.

API

Scanner

class bleak.backends.android.scanner.BleakScannerAndroid(detection_callback: Callable[[BLEDevice, AdvertisementData], Coroutine[Any, Any, None] | None] | None, service_uuids: list[str] | None, scanning_mode: Literal['active', 'passive'], **kwargs: Any)[source]

Android Bleak BLE Scanner using Chaquopy/BeeWare.

Parameters:
  • detection_callback – Optional function that will be called each time a device is discovered or advertising data has changed.

  • service_uuids – Optional list of service UUIDs to filter on. Only advertisements containing this advertising data will be received. Specifying this also enables scanning while the screen is off on Android.

  • scanning_mode – Set to "passive" to avoid the "active" scanning mode.

async start() None[source]

Start scanning for devices

async stop() None[source]

Stop scanning for devices

class bleak.backends.android.scanner.ExcessiveUsageChecker[source]

On Android, no more than 5 start/stop scanning operations are allowed per 30 seconds!

Before API level 33, Android does not report an error when this limit is exceeded, scanning just silently doesn’t work. This is a helper class to track scan start times so that we can raise an error instead.

See this commit: https://android-review.googlesource.com/c/platform/packages/apps/Bluetooth/+/215844 Or this comment: https://github.com/NordicSemiconductor/Android-Scanner-Compat-Library/issues/18#issuecomment-402412139

add_new_scan() None[source]

Record a new scan start time.

check() None[source]

Raise BleakError if starting a scan now would exceed the limit.

time_until_next_scan_allowed() float[source]

Get the time in seconds until the next scan may be started (0 if it can be started now).

class bleak.backends.android.scanner.ScanObjects(adapter: 'BluetoothAdapter', javascanner: 'BluetoothLeScanner', callback: 'PythonScanCallback')[source]

Client

class bleak.backends.android.client.BleakClientAndroid(address_or_ble_device: BLEDevice | str, services: set[UUID] | None, *, disconnected_callback: Callable[[], None] | None, timeout: float, **kwargs: Any)[source]

Android Bleak Client using Chaquopy/BeeWare.

Parameters:
  • address_or_ble_device – The Bluetooth address of the BLE peripheral to connect to or the BLEDevice object representing it.

  • services – Optional set of services UUIDs to filter.

async connect(pair: bool, **kwargs: Any) None[source]

Connect to the specified GATT server.

async disconnect() None[source]

Disconnect from the specified GATT server.

property is_connected: bool

Check connection status between this client and the server.

Returns:

Boolean representing connection status.

property mtu_size: int

Gets the negotiated MTU.

property name: str

See bleak.BleakClient.name().

async pair(*args: Any, **kwarg: Any) None[source]

Pair with the peripheral.

async read_gatt_char(characteristic: BleakGATTCharacteristic, *, use_cached: bool = False, **kwargs: Any) bytearray[source]

Perform read operation on the specified GATT characteristic.

Parameters:

characteristic (BleakGATTCharacteristic) – The characteristic to read from.

Returns:

(bytearray) The read data.

async read_gatt_descriptor(descriptor: BleakGATTDescriptor, *, use_cached: bool = False, **kwargs: Any) bytearray[source]

Perform read operation on the specified GATT descriptor.

Parameters:
  • descriptor – The descriptor to read from.

  • use_cached – Whether to use cached value.

Returns:

The read data.

async start_notify(characteristic: BleakGATTCharacteristic, callback: Callable[[bytearray], None], **kwargs: Any) None[source]

Activate notifications/indications on a characteristic.

async stop_notify(characteristic: BleakGATTCharacteristic) None[source]

Deactivate notification/indication on a specified characteristic.

Parameters:

characteristic (BleakGATTCharacteristic) – The characteristic to deactivate notification/indication on,.

async unpair() None[source]

Unpair with the peripheral.

async write_gatt_char(characteristic: BleakGATTCharacteristic, data: SizedBuffer, response: bool) None[source]

Perform a write operation on the specified GATT characteristic.

Parameters:
  • characteristic – The characteristic to write to.

  • data – The data to send.

  • response – If write-with-response operation should be done.

async write_gatt_descriptor(descriptor: BleakGATTDescriptor, data: SizedBuffer) None[source]

Perform a write operation on the specified GATT descriptor.

Parameters:

data (bytes or bytearray) – The data to send.

class bleak.backends.android.client.ConnectObjects(adapter: 'BluetoothAdapter', device: 'BluetoothDevice', gatt: 'BluetoothGatt', callbacks: 'PythonBluetoothGattCallback', subscriptions: 'dict[int, NotifyCallback]')[source]

Adapter

class bleak.backends.android.adapter.BleakAdapterAndroid(manager: android.bluetooth.BluetoothManager)[source]

The Android Bleak BLE Adapter using Chaquopy/BeeWare.

async classmethod get(**kwargs: Any) Self[source]

Get a Bluetooth adapter for the current platform.

Returns:

A platform-specific BaseBleakAdapter instance.

Raises:

NotImplementedError – if the current backend does not support this.

async get_connected_devices(service_uuids: frozenset[str]) list[BLEDevice][source]

Retrieve BLE devices that are currently connected to the system.

Parameters:

service_uuids – Service UUIDs to filter on.

Returns:

A list of BLEDevice for each connected BLE device.

Raises:

NotImplementedError – if the current backend does not support this.