BlueLib¶
Bluetooth for Android 5.0 (API 21) through Android 17 (API 37). Coroutines first, typed errors, and a compatibility story that is verified against the Android SDK database rather than remembered.
val blueLib = BlueLib.create(context)
blueLib.scan(ScanRequest(timeoutMillis = 10_000))
.filterIsInstance<ScanEvent.Observed>()
.collect { println(it.observation.deviceId) }
What is in the box¶
| Capability | Platform support handled by BlueLib |
|---|---|
| LE scanning | Android 5.0+, PendingIntent background scans on 8.0+, legacy and extended scan settings, Android 13 AD-type filters, scan quota (5 starts / 30 s) |
| Advertising | Legacy advertising on 5.0+, extended advertising sets on 8.0+, discoverable sets on 14+, payload budgets validated per mode |
| GATT client | GATT over LE, BluetoothGattConnectionSettings on Android 17, MTU negotiation, PHY requests (2M and coded on 8.0+, high data throughput on 17), connection priority including DCK on 14+, notifications with automatic re-arming |
| GATT server | Local services, automatic request answering, Client Characteristic Configuration tracking, long writes, notifications and indications |
| Bluetooth Classic & Pairing | BR/EDR discovery, bonding (including Android 16.1 bond-loss reasons and Android 17 autonomous re-pairing), dynamic autoPair with AutoPairFilter, RFCOMM and L2CAP sockets, BluetoothSocketSettings on Android 16+ |
| Permissions | The complete matrix from Android 5 to 17, including "denied" vs "never declared" |
| Diagnostics | Every platform operation, state transition and typed error on one flow, with status codes preserved |
The design in one page¶
bluelib BlueLib facade: one object, one lifecycle
└── bluelib-android Platform adapters (android.bluetooth) → ports
└── bluelib-core Domain: models, validation, policies, state machines (no Android imports)
bluelib-testing Fakes for every port
- The domain never imports
android.*. A Gradle check (verifyNoAndroidImports) fails the build if it ever does, which is what makes the interesting logic unit-testable on the JVM. - The platform layer owns every version check.
ApiLevelis the only placeBuild.VERSION.SDK_INTis compared, and its helpers carry@ChecksSdkIntAtLeastso Android Lint can prove each guarded call. - Errors are data.
BlueLibResult<T>carries aBlueLibErrorwith a stablecode, anisRetryableflag, structuredcontextand adocsAnchorpointing at the matching section of Troubleshooting.
Where to go next¶
- Getting started — install, permissions, first scan.
- Architecture — why the modules are split this way.
- Compatibility matrix — every platform API BlueLib relies on and the
release that introduced it, generated from
api-versions.xml. - Troubleshooting — start from the error code you saw.