Android BLE Advertising — Complete Guide
In this tutorial, you'll learn about Android BLE Advertising. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
The Problem
Your BLE advertisement doesn't appear on other devices, or the advertised service UUID is not visible to scanners.
Wrong Approach ❌
// Starting advertising without proper setup
val bluetoothLeAdvertiser = BluetoothAdapter.getDefaultAdapter().bluetoothLeAdvertiser
bluetoothLeAdvertiser?.startAdvertising(null, null, advertisingCallback)
Output: Advertisement not visible. No service UUID in scan results.
Right Approach ✅
class BleAdvertiser(private val context: Context) {
private var bluetoothLeAdvertiser: BluetoothLeAdvertiser? = null
private var isAdvertising = false
fun startAdvertising(serviceUuid: String, deviceName: String) {
if (!hasPermissions()) {
requestPermissions()
return
}
val adapter = BluetoothAdapter.getDefaultAdapter()
bluetoothLeAdvertiser = adapter?.bluetoothLeAdvertiser
if (bluetoothLeAdvertiser == null) {
Log.e("BleAdv", "BLE advertising not supported on this device")
return
}
// Advertising data
val advertiseData = AdvertiseData.Builder()
.setIncludeDeviceName(true)
.setIncludeTxPowerLevel(true)
.addServiceUuid(ParcelUuid(UUID.fromString(serviceUuid)))
.addServiceData(
ParcelUuid(UUID.fromString(serviceUuid)),
"active".toByteArray()
)
.build()
// Scan response data (additional data when scanned)
val scanResponse = AdvertiseData.Builder()
.setIncludeDeviceName(false) // Already in advertise data
.addManufacturerData(
0x1234, // Manufacturer ID
deviceName.toByteArray()
)
.build()
// Advertising settings
val advertiseSettings = AdvertiseSettings.Builder()
.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY) // Fast advertising
.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) // Maximum range
.setConnectable(true) // Allow connections
.setTimeout(0) // No timeout
.build()
bluetoothLeAdvertiser?.startAdvertising(
advertiseSettings,
advertiseData,
scanResponse,
object : AdvertiseCallback() {
override fun onStartSuccess(settingsInEffect: AdvertiseSettings) {
isAdvertising = true
Log.d("BleAdv", "Advertising started")
}
override fun onStartFailure(errorCode: Int) {
isAdvertising = false
Log.e("BleAdv", "Advertising failed: $errorCode")
handleError(errorCode)
}
}
)
}
fun stopAdvertising() {
bluetoothLeAdvertiser?.stopAdvertising(advertisingCallback)
isAdvertising = false
}
private fun handleError(errorCode: Int) {
when (errorCode) {
AdvertiseCallback.ADVERTISE_FAILED_ALREADY_STARTED -> {
stopAdvertising()
// Retry
}
AdvertiseCallback.ADVERTISE_FAILED_FEATURE_UNSUPPORTED -> {
// BLE advertising not supported
}
AdvertiseCallback.ADVERTISE_FAILED_TOO_MANY_ADVERTISERS -> {
// Another app is advertising
}
}
}
private fun hasPermissions(): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
ContextCompat.checkSelfPermission(context, Manifest.permission.BLUETOOTH_ADVERTISE) == PERMISSION_GRANTED
} else {
ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION) == PERMISSION_GRANTED
}
}
}
Output: BLE advertisement visible to all nearby scanners.
Prevention
- Check
bluetoothLeAdvertiser != null(not all devices support BLE advertising). - Include service UUID in
AdvertiseDatafor easy filtering. - Use
ADVERTISE_MODE_LOW_LATENCYduring active pairing. - Use
ADVERTISE_TX_POWER_HIGHfor maximum range. - Handle
ADVERTISE_FAILED_TOO_MANY_ADVERTISERS(limit is typically 1-2).
Common Mistakes with bluetooth advertising
- Overlapping type class instances that cause GHC to reject the program with ambiguous dispatch errors
- Non-exhaustive pattern matches that compile with warnings then crash at runtime
- Misunderstanding that
Stringis[Char]with poor performance for large text operations
These mistakes appear frequently in real-world Android code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.
Practice Exercise
Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.
This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.
FAQ
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro