BluetoothAdapter è una classe di sistema Android che rappresenta l'adattatore Bluetooth locale di un dispositivo. BluetoothAdapter è il punto di ingresso per tutte le operazioni Bluetooth su Android: accensione della radio (enable), scansione dei dispositivi, gestione della visibilità (setScanMode), ottenimento delle informazioni dell'adattatore (getName, getAddress, getState). La classe è disponibile tramite BluetoothManager.getAdapter() (API 18+) o BluetoothAdapter.getDefaultAdapter(). Sui dispositivi senza modulo Bluetooth, getDefaultAdapter() restituisce null. Secondo Android Developers, 2026, BluetoothAdapter è un componente obbligatorio per qualsiasi applicazione BLE su Android, a partire dall'API 5.
Punti chiave
BluetoothAdapter rappresenta l'adattatore Bluetooth fisico di un dispositivo Android. Ogni dispositivo ha esattamente un adattatore (eccezione: Android Automotive con più chip Bluetooth, che utilizza BluetoothManager.getAdapterList()). BluetoothAdapter incapsula lo stato della radio: STATE_OFF (0), STATE_TURNING_ON (1), STATE_ON (2), STATE_TURNING_OFF (3). Lo stato viene tracciato tramite BroadcastReceiver su ACTION_STATE_CHANGED.
Ottenere un'istanza di BluetoothAdapter è il primo passo di qualsiasi applicazione BLE su Android. Il metodo raccomandato è tramite BluetoothManager.getAdapter() dall'API 18+. L'alternativa è il metodo statico BluetoothAdapter.getDefaultAdapter(), che funziona dall'API 5 ma è meno flessibile. Entrambi i metodi restituiscono null se il dispositivo non ha un modulo Bluetooth (tablet solo Wi-Fi, emulatore). Il controllo di null è obbligatorio: l'applicazione deve terminare correttamente o disabilitare le funzionalità BLE.
// Ottenere BluetoothAdapter (consigliato)
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.content.Context
class BluetoothHelper(context: Context) {
private val bluetoothAdapter: BluetoothAdapter?
init {
// Metodo 1: tramite BluetoothManager (API 18+)
val manager = context.getSystemService(Context.BLUETOOTH_SERVICE)
as BluetoothManager?
bluetoothAdapter = manager?.adapter
// Metodo 2: tramite metodo statico (API 5+)
// val adapter = BluetoothAdapter.getDefaultAdapter()
// Controllo di null
if (bluetoothAdapter == null) {
// Il dispositivo non supporta Bluetooth
}
}
// Verificare lo stato Bluetooth
fun isBluetoothEnabled(): Boolean {
return bluetoothAdapter?.isEnabled == true
}
// Ottenere informazioni sull'adattatore
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 dimostra l'ottenimento di BluetoothAdapter tramite BluetoothManager con un successivo controllo di null. isBluetoothEnabled verifica isEnabled — una condizione obbligatoria prima di qualsiasi operazione BLE. getAdapterInfo restituisce il nome del dispositivo, l'indirizzo MAC, lo stato e la modalità di visibilità. Importante: su Android 10+ (API 29+), il servizio di sistema restituisce un indirizzo MAC fittizio (02:00:00:00:00:00) se l'applicazione non ha le autorizzazioni BLUETOOTH_ADMIN e ACCESS_FINE_LOCATION.
BluetoothAdapter fornisce metodi per controllare la radio Bluetooth. enable() e disable() accendono e spengono il Bluetooth. Entrambi i metodi richiedono l'autorizzazione BLUETOOTH_ADMIN e vengono eseguiti in modo asincrono: dopo aver chiamato enable(), il sistema avvia il processo di accensione della radio e lo stato viene tracciato tramite BroadcastReceiver con l'azione BluetoothAdapter.ACTION_STATE_CHANGED. Su Android 10+, enable() e disable() richiedono un privilegio di sistema aggiuntivo — le applicazioni normali non possono controllare Bluetooth a livello di programmazione senza un dialogo utente.
getState() restituisce lo stato corrente dell'adattatore: STATE_OFF (10), STATE_TURNING_ON (11), STATE_ON (12), STATE_TURNING_OFF (13). getAddress() restituisce l'indirizzo MAC dell'adattatore Bluetooth. Su Android 6+, è necessario ACCESS_FINE_LOCATION (o ACCESS_COARSE_LOCATION per API 31+) per richiedere l'indirizzo MAC. Su Android 10+, getAddress() restituisce l'indirizzo costante 02:00:00:00:00:00 — l'indirizzo reale non è disponibile tramite l'API pubblica.
getScanMode() determina la modalità di visibilità dell'adattatore: SCAN_MODE_NONE (invisibile), SCAN_MODE_CONNECTABLE (visibile per i dispositivi connessi), SCAN_MODE_CONNECTABLE_DISCOVERABLE (visibile a tutti). La modalità di visibilità è limitata nel tempo (di solito 60–300 secondi) per sicurezza. Impostare la modalità tramite setScanMode() richiede BLUETOOTH_ADMIN e autorizzazione di sistema su Android 10+.
| Metodo | Descrizione | Autorizzazione richiesta |
|---|---|---|
| enable() | Accendere la radio Bluetooth | BLUETOOTH_ADMIN |
| disable() | Spegnere la radio Bluetooth | BLUETOOTH_ADMIN |
| getState() | Stato corrente dell'adattatore | BLUETOOTH |
| getAddress() | Indirizzo MAC dell'adattatore | BLUETOOTH + ACCESS_FINE_LOCATION (API 23+) |
| getScanMode() | Modalità di visibilità del dispositivo | BLUETOOTH |
| setScanMode() | Impostare la modalità di visibilità | BLUETOOTH_ADMIN |
BluetoothAdapter supporta due tipi di scansione. La scansione Bluetooth classica (BR/EDR) viene avviata tramite startDiscovery() — rileva dispositivi Bluetooth di tutti i tipi, inclusi telefoni e cuffie. I risultati vengono restituiti tramite BroadcastReceiver con l'azione BluetoothDevice.ACTION_FOUND. startDiscovery() funziona per 12 secondi e può essere annullato chiamando cancelDiscovery(). Questo metodo è deprecato per BLE — utilizzare BluetoothLeScanner.
La scansione BLE tramite BluetoothAdapter utilizza il metodo deprecato startLeScan(LeScanCallback). A partire dall'API 21, Google raccomanda di utilizzare BluetoothLeScanner, ottenuto tramite BluetoothAdapter.getBluetoothLeScanner(). BluetoothLeScanner fornisce un'API più flessibile: configurazione della scansione tramite ScanSettings (modalità, tipo di callback, modalità di corrispondenza), filtraggio tramite ScanFilter (per UUID del servizio, nome del dispositivo, indirizzo MAC) e supporto per PendingIntent per la scansione in background.
// API di scansione BLE vecchia (deprecata) vs nuova
import android.bluetooth.BluetoothAdapter
import android.bluetooth.le.*
class BLEScanner(private val bluetoothAdapter: BluetoothAdapter?) {
// DEPRECATO: startLeScan (API 18+, API 21)
@Suppress("DEPRECATION")
fun legacyScan() {
bluetoothAdapter?.startLeScan { device, rssi, scanRecord ->
print("Found (LE Scan): $device.name, RSSI: $rssi")
}
}
// NUOVO: BluetoothLeScanner (API 21+)
fun modernScan() {
val scanner = bluetoothAdapter?.bluetoothLeScanner
?: return
// Impostazioni di scansione
val settings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
.build()
// Filtra per servizio (UUID frequenza cardiaca)
val filters = listOf(
ScanFilter.Builder()
.setServiceUuid(ParcelUuid.fromString("0000180D-0000-1000-8000-00805F9B34FB"))
.build()
)
// Avvia scansione
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 confronta il deprecato startLeScan con il moderno BluetoothLeScanner. In legacyScan, LeScanCallback riceve BluetoothDevice, RSSI e scanRecord grezzo. In modernScan, vengono utilizzati ScanSettings con modalità LOW_LATENCY (massima velocità di rilevamento) e ScanFilter per filtrare per UUID del servizio di frequenza cardiaca (0x180D). ScanCallback fornisce onScanResult con un oggetto ScanResult contenente informazioni estese: nome, RSSI, dati pubblicitari, tipo di connessione.
BluetoothManager è un servizio di sistema Android, introdotto nell'API 18 (Android 4.3), per gestire le operazioni Bluetooth. Prima dell'API 18, l'unico modo per ottenere BluetoothAdapter era il metodo statico getDefaultAdapter(). BluetoothManager fornisce: adapter — un'istanza di BluetoothAdapter, getConnectedDevices() — un elenco di dispositivi connessi, getDevicesMatchingConnectionStates() — filtraggio per stato. BluetoothManager viene utilizzato anche per ottenere BluetoothLeScanner sulle API precedenti.
Vantaggi di BluetoothManager rispetto alla chiamata diretta di BluetoothAdapter.getDefaultAdapter(): l'applicazione non dipende da un singleton statico, il gestore rispetta il contesto (Activity/Application), importante per scenari multi-account di Android Enterprise. Su Android Automotive con più chip Bluetooth, BluetoothManager.getAdapterList() restituisce tutti gli adattatori disponibili — BluetoothAdapter.getDefaultAdapter() restituisce solo il primo.
// Usare BluetoothManager per BLE
class BLEConnection(context: Context) {
private val bluetoothManager: BluetoothManager =
context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
private val adapter: BluetoothAdapter? = bluetoothManager.adapter
// Ottenere elenco dispositivi BLE connessi
fun getConnectedDevices(): List<BluetoothDevice> {
return bluetoothManager.getConnectedDevices(
BluetoothProfile.GATT
)
}
// Filtrare dispositivi per stato
fun getDevicesByState(states: IntArray): List<BluetoothDevice> {
return bluetoothManager.getDevicesMatchingConnectionStates(
BluetoothProfile.GATT, states
)
}
// Verificare supporto BLE sul dispositivo
fun isBLESupported(): Boolean {
return adapter != null && context.packageManager
.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
}
// Richiedere attivazione Bluetooth tramite dialogo di sistema
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 utilizza BluetoothManager per accedere a BluetoothAdapter e ottenere l'elenco dei dispositivi GATT connessi. isBLESupported verifica la presenza di radio BLE tramite PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) — un controllo importante per dispositivi con Bluetooth Classic senza BLE. requestEnableBluetooth mostra il dialogo di sistema per attivare Bluetooth (ACTION_REQUEST_ENABLE), senza richiedere l'autorizzazione BLUETOOTH_ADMIN — questo è l'unico modo legale per attivare Bluetooth su Android 10+ senza un'applicazione di sistema.
Le autorizzazioni per BluetoothAdapter si sono evolute con ogni versione di Android. Su Android 6–11 (API 23–30), BLUETOOTH, BLUETOOTH_ADMIN e ACCESS_FINE_LOCATION sono obbligatorie per la scansione BLE. Su Android 12+ (API 31+), Google ha suddiviso le autorizzazioni: ACCESS_FINE_LOCATION viene sostituita da BLUETOOTH_SCAN (scansione), BLUETOOTH_CONNECT (connessione), BLUETOOTH_ADVERTISE (pubblicità). Per il rilevamento di dispositivi BLE, BLUETOOTH_SCAN è sufficiente, la posizione non è richiesta.
Tabella delle autorizzazioni per versione Android:
| Operazione | API 23–30 | API 31+ |
|---|---|---|
| Scansione BLE | ACCESS_FINE_LOCATION | BLUETOOTH_SCAN (senza posizione) |
| Connessione BLE | ACCESS_FINE_LOCATION | BLUETOOTH_CONNECT |
| Pubblicità BLE | ACCESS_FINE_LOCATION | BLUETOOTH_ADVERTISE |
| Attivazione/disattivazione | BLUETOOTH_ADMIN | BLUETOOTH_ADMIN (sistema) |
| Ottenimento indirizzo MAC | ACCESS_FINE_LOCATION | BLUETOOTH_CONNECT (indirizzo fittizio) |
Su Android 12+, tutte le autorizzazioni Bluetooth sono autorizzazioni di runtime — devono essere richieste in fase di esecuzione tramite ActivityResultContracts.RequestMultiplePermissions. BLUETOOTH_SCAN e BLUETOOTH_ADVERTISE appartengono al gruppo NEARBY_DEVICES, BLUETOOTH_CONNECT appartiene allo stesso gruppo. Le autorizzazioni BLUETOOTH e BLUETOOTH_ADMIN rimangono nel manifest per compatibilità con API < 31, ma per API 31+ vengono ignorate — Google richiede di specificare esplicitamente le nuove autorizzazioni.
// Richiedere autorizzazioni Bluetooth su 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
)
}
}
// Verificare tutte le autorizzazioni
fun hasPermissions(context: Context): Boolean {
return getRequiredPermissions().all { permission ->
ContextCompat.checkSelfPermission(context, permission)
== PackageManager.PERMISSION_GRANTED
}
}
}
La classe PermissionHelper restituisce il set corretto di autorizzazioni in base al livello API. Su Android 12+, vengono utilizzati BLUETOOTH_SCAN, BLUETOOTH_CONNECT, BLUETOOTH_ADVERTISE senza posizione. Su Android 6–11, ACCESS_FINE_LOCATION è ancora richiesto per la scansione BLE. Lo sviluppatore deve considerare entrambi gli scenari quando richiede le autorizzazioni tramite ActivityResultContracts o RxPermissions.
Esempio completo di un'applicazione BLE in Kotlin che utilizza BluetoothAdapter per scansionare, connettere e leggere dati da un dispositivo BLE. L'esempio copre la verifica delle autorizzazioni, l'ottenimento dell'adattatore, la scansione tramite BluetoothLeScanner e la connessione tramite BluetoothDevice.connectGatt.
// Gestore BLE completo in 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. Scoperta servizi
fun canScan(): Boolean {
return adapter?.isEnabled == true
&& scanner != null
&& PermissionHelper(context).hasPermissions(context)
}
// 2. con filtro
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. Fermare scansione
fun stopScanning() {
scanner?.stopScan(object : ScanCallback() {})
}
// 4. Connettersi al dispositivo 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) {
// Servizi trovati, può leggere caratteristiche
}
},
BluetoothDevice.TRANSPORT_LE
)
}
// 5. Rilasciare risorse
fun disconnect() {
gatt?.disconnect()
gatt?.close()
gatt = null
}
}
La classe BLEManager unifica il ciclo BLE completo su Android: verifica dell'adattatore e delle autorizzazioni (canScan), scansione tramite BluetoothLeScanner con ScanSettings (startScanning), connessione tramite BluetoothDevice.connectGatt con TRANSPORT_LE (connectToDevice) e rilascio delle risorse (disconnect). Tutte le operazioni BLE vengono eseguite sul thread dell'interfaccia utente — Android chiama i callback BluetoothGattCallback sul thread principale. Per attività BLE intensive, si consiglia di spostare le operazioni GATT su un HandlerThread in background.
Domande frequenti
BluetoothAdapter è una classe che rappresenta l'adattatore Bluetooth locale di un dispositivo Android. Si ottiene tramite BluetoothManager.getAdapter() (API 18+) o BluetoothAdapter.getDefaultAdapter(). Fornisce metodi per attivare/disattivare Bluetooth, scansionare dispositivi, gestire la visibilità e ottenere informazioni sull'adattatore. Restituisce null su dispositivi senza modulo Bluetooth.
Il motivo è l'assenza di radio Bluetooth sul dispositivo. Tipico per tablet solo Wi-Fi, emulatore Android e Android TV senza Bluetooth. Controllare getDefaultAdapter() come null all'avvio dell'applicazione e disattivare le funzionalità BLE se l'adattatore è assente. Un'alternativa è il controllo tramite PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) per un rilevamento più preciso.
BluetoothLeScanner (API 21+) è l'API moderna per la scansione BLE con supporto per ScanFilter, ScanSettings e PendingIntent. startLeScan (API 18+) è un metodo deprecato di BluetoothAdapter che accetta LeScanCallback con un set di dati limitato. Google raccomanda BluetoothLeScanner per tutti i nuovi progetti, consente di filtrare per UUID, configurare la modalità di consumo energetico e funzionare in background tramite PendingIntent.
Su Android 12+ (API 31), la scansione BLE richiede BLUETOOTH_SCAN, la connessione richiede BLUETOOTH_CONNECT e la pubblicità richiede BLUETOOTH_ADVERTISE. L'autorizzazione di posizione ACCESS_FINE_LOCATION non è più richiesta per BLE. Su Android 6–11, è richiesto ACCESS_FINE_LOCATION. Tutte le autorizzazioni vengono richieste in fase di esecuzione tramite ActivityResultContracts.
Su Android 10+, l'attivazione programmatica del Bluetooth senza dialogo di sistema è disponibile solo per applicazioni di sistema con autorizzazione BLUETOOTH_PRIVILEGED. Le applicazioni normali devono utilizzare Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) e startActivityForResult — l'utente conferma l'attivazione nel dialogo di sistema. BLUETOOTH_ADMIN nel manifest non concede diritti enable() su Android 10+.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche