Platform Notes
Specific behaviors and limitations for each supported operating system.
SimpleBLE aims to provide a unified API across all platforms, but due to differences in the underlying operating system Bluetooth stacks, there are some platform-specific behaviors and limitations you should be aware of.
Windows
Architecture Mismatch
If you are running a 32-bit application on a 64-bit Windows machine, you might encounter issues where SimpleBLE::Adapter::bluetooth_enabled() returns false even if Bluetooth is enabled. This is a known limitation of the Windows API. It is recommended to build and run your application as 64-bit on 64-bit systems.
Adapter Selection
The Windows implementation currently has a limitation where initialized() might return true if any adapter is enabled, not necessarily the specific instance you are inspecting. This is a known issue being tracked.
Local Peripheral Mode
Windows hosts local services with WinRT GattServiceProvider. The Bluetooth adapter and driver must report peripheral-role support. Applications distributed as AppX/MSIX packages should declare <DeviceCapability Name="bluetooth" /> in Package.appxmanifest; ordinary unpackaged executables do not need this manifest declaration.
Windows controls the friendly name placed in a discoverable advertisement. WinRT requires one provider per primary service; SimpleBLE starts every configured provider as one peripheral operation and prioritizes providers added with add_advertised_service(). Windows owns the shared advertisement payload and may omit UUIDs that do not fit. Explicitly selected UUIDs must belong to configured services, and start() throws if the adapter or driver lacks the resources to publish every provider. Windows reports a client session after the client first interacts with or subscribes to a hosted characteristic; a bare link that performs no GATT operation may not produce a client-connected callback.
MacOS / iOS
Peripheral Identifiers (UUIDs)
On macOS and iOS, the operating system does not expose the raw MAC address of Bluetooth peripherals to applications. Instead, it assigns a unique UUID to each peripheral.
- Persistent UUIDs: These UUIDs are generally persistent for a given device on a given host, meaning if you restart your app, the UUID for a specific peripheral should remain the same.
- Not Global: The UUID for a specific peripheral will be different on different macOS/iOS devices. You cannot use this UUID to identify a peripheral across different host devices.
Connection Management
macOS and iOS manage Bluetooth connections at the OS level.
- Unpairing: Applications cannot programmatically "unpair" or "forget" a device. This must be done by the user in the system settings.
- Connection State: The OS might keep a connection alive even if your application disconnects, especially if other apps or the system itself are using the device.
Local Peripheral Mode
macOS and iOS can host a local peripheral through CoreBluetooth. Because CoreBluetooth does not expose peripheral-role connection events, SimpleBLE reports a client as connected when it first subscribes to a characteristic and disconnected when its final subscription ends. Client addresses are CoreBluetooth identifiers rather than Bluetooth MAC addresses.
CoreBluetooth supports an advertisement-specific local name distinct from the system Bluetooth and GAP device names. Set it before start() with SimpleBLE::Advanced::MacOS::set_advertisement_local_name() or SimpleBLE::Advanced::iOS::set_advertisement_local_name(). Advertising is best-effort and space-limited; iOS omits the local name while the app is in the background.
Linux (BlueZ)
Scanning Delays
When calling scan_stop(), there might be a slight delay (a few seconds) before the underlying adapter actually reports that discovery has stopped. This is a behavior of the BlueZ stack.
LE Privacy
On Linux, LE privacy is controlled by BlueZ and the Bluetooth controller, not by SimpleBLE. SimpleBLE uses BlueZ over D-Bus and follows the privacy policy configured for bluetoothd.
To enable private device addresses on BlueZ-based systems, configure /etc/bluetooth/main.conf:
[General]
Privacy = deviceRestart BlueZ after changing this setting:
sudo systemctl restart bluetoothPrivacy = device enables private addressing while still accepting peer devices that use either identity addresses or private addresses. Support depends on the BlueZ version, Linux kernel, and Bluetooth adapter/controller.
If you need to verify the address used over the air, use BlueZ tools such as btmon or an external BLE sniffer. SimpleBLE::Adapter::address() may still report the adapter identity address.
Adapter Removal
On Linux, BlueZ exposes adapters, peripherals, services, and characteristics as D-Bus object paths. If a Bluetooth adapter is disabled, unplugged, or otherwise removed, BlueZ removes the adapter object path and any child device paths below it. SimpleBLE objects retained by the application may still exist as C++ objects, but the underlying BlueZ objects they refer to are no longer present.
Applications that support adapter hot-plug or adapter removal should treat existing adapters, remote peripherals, and local peripherals as stale after the adapter disappears. Stop active work, release retained objects when possible, and call SimpleBLE::Adapter::get_adapters() again to discover the current adapter set before scanning, connecting, or advertising again.
Local Peripheral Mode
On Linux, Adapter::create_local_peripheral() registers a BlueZ GATT application and an LE advertisement through D-Bus, so bluetoothd must be running and the process must have the same D-Bus access used for scanning.
SimpleBLE permits the same adapter to scan and advertise at the same time, but availability depends on BlueZ and the Bluetooth controller supporting concurrent operation and having sufficient resources. Incoming clients of the local peripheral are reported through SimpleBLE::Local::Peripheral client callbacks, not as remote SimpleBLE::Peripheral objects to connect() to.
BlueZ supports a per-advertisement local name independent of the adapter alias. Set it before start() with SimpleBLE::Advanced::Linux::set_advertisement_local_name(). BlueZ may shorten the name to fit the advertising data.
Android
API Level Limitations
Android's Bluetooth API has evolved significantly over time, leading to some version-specific limitations:
- Address Type: The
address_type()method is only available on devices running Android API level 35 or newer. On older devices, this may return an unspecified or default value. - Bluetooth Enable/Disable: Programmatically enabling or disabling Bluetooth (e.g.,
power_on(),power_off()) uses APIs that were deprecated in API level 33. These functions may not work or might throw exceptions on newer Android versions.
Bond Management
Similar to iOS, Android (especially newer versions) restricts the ability of applications to remove bonds (unpair) programmatically. While SimpleBLE attempts to use hidden APIs to achieve this where possible, it is not guaranteed to work on all devices or future Android versions.
Local Peripheral Mode
Android API 31 and newer can host local GATT services and legacy BLE advertisements. Add BLUETOOTH_CONNECT and BLUETOOTH_ADVERTISE to the application manifest and request both at runtime. Hardware support still depends on BluetoothAdapter.isMultipleAdvertisementSupported() and the vendor Bluetooth stack.
Native C++ applications must call SimpleBLE::Advanced::Android::set_context(application_context) before Adapter::create_local_peripheral(). Pass an application context, not an Activity; SimpleBLE retains a global JNI reference while the process is alive. Adapter.createLocalPeripheral(context) in SimpleDroidBLE extracts and configures the application context automatically.
Android's public advertising API can include the adapter's system Bluetooth name but cannot set an arbitrary per-advertisement name. SimpleBLE includes the system name in the scan response.
The backend uses legacy advertising, so the normal 31-byte advertising and scan-response limits apply. Prepared writes are rejected; clients should use regular write requests or write commands within the negotiated GATT payload size.
Android reports real GATT server connection and disconnection events. Client addresses are the addresses exposed by BluetoothDevice; privacy-enabled centrals may use a rotating address.
