BluetoothAdapter est une classe système Android qui représente l'adaptateur Bluetooth local d'un appareil. BluetoothAdapter est le point d'entrée pour toutes les opérations Bluetooth sur Android : activation de la radio (enable), scan des appareils, gestion de la visibilité (setScanMode), récupération des informations de l'adaptateur (getName, getAddress, getState). La classe est disponible via BluetoothManager.getAdapter() (API 18+) ou BluetoothAdapter.getDefaultAdapter(). Sur les appareils sans module Bluetooth, getDefaultAdapter() retourne null. Selon Android Developers, 2026, BluetoothAdapter est un composant obligatoire pour toute application BLE sur Android, à partir de l'API 5.
Points clés
BluetoothAdapter représente l'adaptateur Bluetooth physique d'un appareil Android. Chaque appareil possède exactement un adaptateur (exception : Android Automotive avec plusieurs puces Bluetooth, qui utilise BluetoothManager.getAdapterList()). BluetoothAdapter encapsule l'état de la radio : STATE_OFF (0), STATE_TURNING_ON (1), STATE_ON (2), STATE_TURNING_OFF (3). L'état est suivi via BroadcastReceiver sur ACTION_STATE_CHANGED.
Obtenir une instance de BluetoothAdapter est la première étape de toute application BLE sur Android. La méthode recommandée est via BluetoothManager.getAdapter() à partir de l'API 18+. L'alternative est la méthode statique BluetoothAdapter.getDefaultAdapter(), qui fonctionne depuis l'API 5 mais est moins flexible. Les deux méthodes retournent null si l'appareil n'a pas de module Bluetooth (tablettes Wi-Fi uniquement, émulateur). La vérification de null est obligatoire : l'application doit se terminer correctement ou désactiver les fonctionnalités BLE.
// Obtenir BluetoothAdapter (recommandé)
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.content.Context
class BluetoothHelper(context: Context) {
private val bluetoothAdapter: BluetoothAdapter?
init {
// Méthode 1 : via BluetoothManager (API 18+)
val manager = context.getSystemService(Context.BLUETOOTH_SERVICE)
as BluetoothManager?
bluetoothAdapter = manager?.adapter
// Méthode 2 : via méthode statique (API 5+)
// val adapter = BluetoothAdapter.getDefaultAdapter()
// Vérification de null
if (bluetoothAdapter == null) {
// L'appareil ne prend pas en charge Bluetooth
}
}
// Vérifier l'état Bluetooth
fun isBluetoothEnabled(): Boolean {
return bluetoothAdapter?.isEnabled == true
}
// Obtenir les informations de l'adaptateur
fun getAdapterInfo(): Map<String, String> {
return mapOf(
"name" to (bluetoothAdapter?.name ?: "N/A"),
"address" to (bluetoothAdapter?.address ?: "N/A"),
"state" to (bluetoothAdapter?.state?.toString() ?: "N/A"),
"scanMode" to (bluetoothAdapter?.scanMode?.toString() ?: "N/A")
)
}
}
La classe BluetoothHelper démontre l'obtention de BluetoothAdapter via BluetoothManager avec une vérification de null ultérieure. isBluetoothEnabled vérifie isEnabled — une condition obligatoire avant toute opération BLE. getAdapterInfo retourne le nom de l'appareil, l'adresse MAC, l'état et le mode de visibilité. Important : sur Android 10+ (API 29+), le service système retourne une adresse MAC fictive (02:00:00:00:00:00) si l'application n'a pas les autorisations BLUETOOTH_ADMIN et ACCESS_FINE_LOCATION.
BluetoothAdapter fournit des méthodes pour contrôler la radio Bluetooth. enable() et disable() activent et désactivent le Bluetooth. Les deux méthodes nécessitent l'autorisation BLUETOOTH_ADMIN et s'exécutent de manière asynchrone : après avoir appelé enable(), le système lance le processus d'activation de la radio, et l'état est suivi via BroadcastReceiver avec l'action BluetoothAdapter.ACTION_STATE_CHANGED. Sur Android 10+, enable() et disable() nécessitent un privilège système supplémentaire — les applications normales ne peuvent pas contrôler Bluetooth par programmation sans dialogue utilisateur.
getState() retourne l'état actuel de l'adaptateur : STATE_OFF (10), STATE_TURNING_ON (11), STATE_ON (12), STATE_TURNING_OFF (13). getAddress() retourne l'adresse MAC de l'adaptateur Bluetooth. Sur Android 6+, ACCESS_FINE_LOCATION (ou ACCESS_COARSE_LOCATION pour API 31+) est requis pour demander l'adresse MAC. Sur Android 10+, getAddress() retourne l'adresse constante 02:00:00:00:00:00 — l'adresse réelle n'est pas disponible via l'API publique.
getScanMode() détermine le mode de visibilité de l'adaptateur : SCAN_MODE_NONE (invisible), SCAN_MODE_CONNECTABLE (visible pour les appareils connectés), SCAN_MODE_CONNECTABLE_DISCOVERABLE (visible pour tous). Le mode de visibilité est limité dans le temps (généralement 60 à 300 secondes) pour des raisons de sécurité. Définir le mode via setScanMode() nécessite BLUETOOTH_ADMIN et une autorisation système sur Android 10+.
| Méthode | Description | Autorisation requise |
|---|---|---|
| enable() | Activer la radio Bluetooth | BLUETOOTH_ADMIN |
| disable() | Désactiver la radio Bluetooth | BLUETOOTH_ADMIN |
| getState() | État actuel de l'adaptateur | BLUETOOTH |
| getAddress() | Adresse MAC de l'adaptateur | BLUETOOTH + ACCESS_FINE_LOCATION (API 23+) |
| getScanMode() | Mode de visibilité de l'appareil | BLUETOOTH |
| setScanMode() | Définir le mode de visibilité | BLUETOOTH_ADMIN |
BluetoothAdapter prend en charge deux types de scan. Le scan Bluetooth classique (BR/EDR) est lancé via startDiscovery() — il détecte les appareils Bluetooth de tous types, y compris les téléphones et les casques. Les résultats sont retournés via BroadcastReceiver avec l'action BluetoothDevice.ACTION_FOUND. startDiscovery() fonctionne pendant 12 secondes et peut être annulé en appelant cancelDiscovery(). Cette méthode est obsolète pour le BLE — utilisez BluetoothLeScanner.
Le scan BLE via BluetoothAdapter utilise la méthode obsolète startLeScan(LeScanCallback). À partir de l'API 21, Google recommande d'utiliser BluetoothLeScanner, obtenu via BluetoothAdapter.getBluetoothLeScanner(). BluetoothLeScanner fournit une API plus flexible : configuration du scan via ScanSettings (mode, type de callback, mode de correspondance), filtrage via ScanFilter (par UUID de service, nom d'appareil, adresse MAC) et prise en charge de PendingIntent pour le scan en arrière-plan.
// API de scan BLE ancienne (obsolète) vs nouvelle
import android.bluetooth.BluetoothAdapter
import android.bluetooth.le.*
class BLEScanner(private val bluetoothAdapter: BluetoothAdapter?) {
// OBSOLÈTE : startLeScan (API 18+, API 21)
@Suppress("DEPRECATION")
fun legacyScan() {
bluetoothAdapter?.startLeScan { device, rssi, scanRecord ->
print("Found (LE Scan): $device.name, RSSI: $rssi")
}
}
// NOUVEAU : BluetoothLeScanner (API 21+)
fun modernScan() {
val scanner = bluetoothAdapter?.bluetoothLeScanner
?: return
// Paramètres de scan
val settings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
.build()
// Filtrer par service (UUID fréquence cardiaque)
val filters = listOf(
ScanFilter.Builder()
.setServiceUuid(ParcelUuid.fromString("0000180D-0000-1000-8000-00805F9B34FB"))
.build()
)
// Démarrer le scan
scanner.startScan(filters, settings, object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
val device = result.device
val rssi = result.rssi
print("Found (BLE Scanner): ${device.name}, RSSI: $rssi, address: ${device.address}")
}
override fun onScanFailed(errorCode: Int) {
print("Scan error: $errorCode")
}
})
}
}
La classe BLEScanner compare l'obsolète startLeScan avec le moderne BluetoothLeScanner. Dans legacyScan, LeScanCallback reçoit BluetoothDevice, RSSI et scanRecord brut. Dans modernScan, ScanSettings avec le mode LOW_LATENCY (vitesse de détection maximale) et ScanFilter pour filtrer par UUID du service de fréquence cardiaque (0x180D) sont utilisés. ScanCallback fournit onScanResult avec un objet ScanResult contenant des informations étendues : nom, RSSI, données publicitaires, type de connexion.
BluetoothManager est un service système Android, introduit dans l'API 18 (Android 4.3), pour gérer les opérations Bluetooth. Avant l'API 18, la seule façon d'obtenir BluetoothAdapter était la méthode statique getDefaultAdapter(). BluetoothManager fournit : adapter — une instance de BluetoothAdapter, getConnectedDevices() — une liste des appareils connectés, getDevicesMatchingConnectionStates() — filtrage par état. BluetoothManager est également utilisé pour obtenir BluetoothLeScanner sur les anciennes API.
Avantages de BluetoothManager par rapport à l'appel direct de BluetoothAdapter.getDefaultAdapter() : l'application ne dépend pas d'un singleton statique, le gestionnaire respecte le contexte (Activity/Application), ce qui est important pour les scénarios multi-comptes d'Android Enterprise. Sur Android Automotive avec plusieurs puces Bluetooth, BluetoothManager.getAdapterList() retourne tous les adaptateurs disponibles — BluetoothAdapter.getDefaultAdapter() retourne uniquement le premier.
// Utiliser BluetoothManager pour BLE
class BLEConnection(context: Context) {
private val bluetoothManager: BluetoothManager =
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
private val adapter: BluetoothAdapter? = bluetoothManager.adapter
// Obtenir la liste des appareils BLE connectés
fun getConnectedDevices(): List<BluetoothDevice> {
return bluetoothManager.getConnectedDevices(
BluetoothProfile.GATT
)
}
// Filtrer les appareils par état
fun getDevicesByState(states: IntArray): List<BluetoothDevice> {
return bluetoothManager.getDevicesMatchingConnectionStates(
BluetoothProfile.GATT, states
)
}
// Vérifier la prise en charge BLE sur l'appareil
fun isBLESupported(): Boolean {
return adapter != null && context.packageManager
.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
}
// Demander l'activation Bluetooth via la boîte de dialogue système
fun requestEnableBluetooth(activity: MainActivity) {
if (adapter?.isEnabled == false) {
val intent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
activity.startActivityForResult(intent, REQUEST_ENABLE_BT)
}
}
companion object {
const val REQUEST_ENABLE_BT = 1001
}
}
La classe BLEConnection utilise BluetoothManager pour accéder à BluetoothAdapter et obtenir la liste des appareils GATT connectés. isBLESupported vérifie la présence de radio BLE via PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) — une vérification importante pour les appareils avec Bluetooth Classic sans BLE. requestEnableBluetooth affiche la boîte de dialogue système d'activation du Bluetooth (ACTION_REQUEST_ENABLE), sans nécessiter l'autorisation BLUETOOTH_ADMIN — c'est la seule façon légale d'activer Bluetooth sur Android 10+ sans application système.
Les autorisations pour BluetoothAdapter ont évolué avec chaque version d'Android. Sur Android 6–11 (API 23–30), BLUETOOTH, BLUETOOTH_ADMIN et ACCESS_FINE_LOCATION sont obligatoires pour le scan BLE. Sur Android 12+ (API 31+), Google a divisé les autorisations : ACCESS_FINE_LOCATION est remplacé par BLUETOOTH_SCAN (scan), BLUETOOTH_CONNECT (connexion), BLUETOOTH_ADVERTISE (publicité). Pour la détection des appareils BLE, BLUETOOTH_SCAN suffit, la localisation n'est pas requise.
Tableau des autorisations par version d'Android :
| Opération | API 23–30 | API 31+ |
|---|---|---|
| Scan BLE | ACCESS_FINE_LOCATION | BLUETOOTH_SCAN (sans localisation) |
| Connexion BLE | ACCESS_FINE_LOCATION | BLUETOOTH_CONNECT |
| Publicité BLE | ACCESS_FINE_LOCATION | BLUETOOTH_ADVERTISE |
| Activation/désactivation | BLUETOOTH_ADMIN | BLUETOOTH_ADMIN (système) |
| Obtention adresse MAC | ACCESS_FINE_LOCATION | BLUETOOTH_CONNECT (adresse fictive) |
Sur Android 12+, toutes les autorisations Bluetooth sont des autorisations d'exécution — elles doivent être demandées au moment de l'exécution via ActivityResultContracts.RequestMultiplePermissions. BLUETOOTH_SCAN et BLUETOOTH_ADVERTISE appartiennent au groupe NEARBY_DEVICES, BLUETOOTH_CONNECT appartient au même groupe. Les autorisations BLUETOOTH et BLUETOOTH_ADMIN restent dans le manifeste pour la compatibilité avec API < 31, mais pour API 31+ elles sont ignorées — Google exige de spécifier explicitement les nouvelles autorisations.
// Demander les autorisations Bluetooth sur Android 12+
import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import androidx.core.content.ContextCompat
class PermissionHelper(context: Context) {
fun getRequiredPermissions(): Array<String> {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
// Android 12+ : BLE-
arrayOf(
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.BLUETOOTH_ADVERTISE
)
} else {
// Android 6-11 : BLE
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_ADMIN
)
}
}
// Vérifier toutes les autorisations
fun hasPermissions(context: Context): Boolean {
return getRequiredPermissions().all { permission ->
ContextCompat.checkSelfPermission(context, permission)
== PackageManager.PERMISSION_GRANTED
}
}
}
La classe PermissionHelper retourne l'ensemble correct d'autorisations en fonction du niveau d'API. Sur Android 12+, BLUETOOTH_SCAN, BLUETOOTH_CONNECT, BLUETOOTH_ADVERTISE sans localisation sont utilisés. Sur Android 6–11, ACCESS_FINE_LOCATION est toujours requis pour le scan BLE. Le développeur doit prendre en compte les deux scénarios lors de la demande d'autorisations via ActivityResultContracts ou RxPermissions.
Exemple complet d'une application BLE en Kotlin utilisant BluetoothAdapter pour scanner, connecter et lire les données d'un appareil BLE. L'exemple couvre la vérification des autorisations, l'obtention de l'adaptateur, le scan via BluetoothLeScanner et la connexion via BluetoothDevice.connectGatt.
// Gestionnaire BLE complet en Kotlin
class BLEManager(private val context: Context) {
private val bluetoothManager: BluetoothManager =
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
private val adapter: BluetoothAdapter? = bluetoothManager.adapter
private var scanner: BluetoothLeScanner? = adapter?.bluetoothLeScanner
private var gatt: BluetoothGatt? = null
// 1. Découverte de services
fun canScan(): Boolean {
return adapter?.isEnabled == true
&& scanner != null
&& PermissionHelper(context).hasPermissions(context)
}
// 2. avec filtre
fun startScanning(callback: (BluetoothDevice, Int) -> Unit) {
if (!canScan()) return
val settings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setReportDelay(0)
.build()
scanner?.startScan(null, settings, object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
callback(result.device, result.rssi)
}
})
}
// 3. Arrêter le scan
fun stopScanning() {
scanner?.stopScan(object : ScanCallback() {})
}
// 4. Se connecter à l'appareil BLE
fun connectToDevice(device: BluetoothDevice) {
if (adapter?.isEnabled != true) return
gatt = device.connectGatt(
context,
false,
object : BluetoothGattCallback() {
override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices()
}
}
override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) {
// Services trouvés, peut lire les caractéristiques
}
},
BluetoothDevice.TRANSPORT_LE
)
}
// 5. Libérer les ressources
fun disconnect() {
gatt?.disconnect()
gatt?.close()
gatt = null
}
}
La classe BLEManager unifie le cycle BLE complet sur Android : vérification de l'adaptateur et des autorisations (canScan), scan via BluetoothLeScanner avec ScanSettings (startScanning), connexion via BluetoothDevice.connectGatt avec TRANSPORT_LE (connectToDevice) et libération des ressources (disconnect). Toutes les opérations BLE s'exécutent sur le thread de l'interface utilisateur — Android appelle les callbacks BluetoothGattCallback sur le thread principal. Pour les tâches BLE intensives, il est recommandé de déporter les opérations GATT vers un HandlerThread en arrière-plan.
Foire aux questions
BluetoothAdapter est une classe représentant l'adaptateur Bluetooth local d'un appareil Android. Elle est obtenue via BluetoothManager.getAdapter() (API 18+) ou BluetoothAdapter.getDefaultAdapter(). Elle fournit des méthodes pour activer/désactiver le Bluetooth, scanner des appareils, gérer la visibilité et obtenir des informations sur l'adaptateur. Retourne null sur les appareils sans module Bluetooth.
La raison est l'absence de radio Bluetooth sur l'appareil. Typique pour les tablettes Wi-Fi uniquement, l'émulateur Android et Android TV sans Bluetooth. Vérifiez getDefaultAdapter() à null au démarrage de l'application et désactivez les fonctions BLE si l'adaptateur est absent. Une alternative est la vérification via PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) pour une détection plus précise.
BluetoothLeScanner (API 21+) est l'API moderne pour le scan BLE avec prise en charge de ScanFilter, ScanSettings et PendingIntent. startLeScan (API 18+) est une méthode obsolète de BluetoothAdapter qui accepte LeScanCallback avec un ensemble de données limité. Google recommande BluetoothLeScanner pour tous les nouveaux projets, permet de filtrer par UUID, de configurer le mode de consommation d'énergie et de fonctionner en arrière-plan via PendingIntent.
Sur Android 12+ (API 31), le scan BLE nécessite BLUETOOTH_SCAN, la connexion nécessite BLUETOOTH_CONNECT et la publicité nécessite BLUETOOTH_ADVERTISE. L'autorisation de localisation ACCESS_FINE_LOCATION n'est plus requise pour le BLE. Sur Android 6–11, ACCESS_FINE_LOCATION est requis. Toutes les autorisations sont demandées au moment de l'exécution via ActivityResultContracts.
Sur Android 10+, l'activation programmatique du Bluetooth sans dialogue système est disponible uniquement pour les applications système avec l'autorisation BLUETOOTH_PRIVILEGED. Les applications normales doivent utiliser Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) et startActivityForResult — l'utilisateur confirme l'activation dans la boîte de dialogue système. BLUETOOTH_ADMIN dans le manifeste n'accorde pas les droits enable() sur Android 10+.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi