BluetoothAdapter — cos'è, metodi e gestione del Bluetooth in Android

Autore: IT Sectr Pubblicato: 2026-07-16 Tempo di lettura: 10 min

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 è una classe di sistema Android per gestire l'adattatore Bluetooth locale di un dispositivo
  • getDefaultAdapter() è un metodo statico per ottenere un'istanza; restituisce null su dispositivi senza Bluetooth
  • getBluetoothLeScanner() è il metodo per accedere allo scanner BLE moderno (API 21+)
  • enable/disable sono metodi per accendere e spegnere la radio Bluetooth (richiedono autorizzazione BLUETOOTH_ADMIN)
  • startDiscovery/startLeScan sono metodi di scansione classica e BLE (LE Scan deprecato, utilizzare BluetoothLeScanner)

Cos'è BluetoothAdapter: essenza e ottenimento dell'istanza

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.

kotlin
// 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.

Metodi principali di BluetoothAdapter: enable, disable, getState, getAddress

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+.

MetodoDescrizioneAutorizzazione richiesta
enable()Accendere la radio BluetoothBLUETOOTH_ADMIN
disable()Spegnere la radio BluetoothBLUETOOTH_ADMIN
getState()Stato corrente dell'adattatoreBLUETOOTH
getAddress()Indirizzo MAC dell'adattatoreBLUETOOTH + ACCESS_FINE_LOCATION (API 23+)
getScanMode()Modalità di visibilità del dispositivoBLUETOOTH
setScanMode()Impostare la modalità di visibilitàBLUETOOTH_ADMIN

Scansione dei dispositivi: startDiscovery e BluetoothLeScanner

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.

kotlin
// 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: API moderna per accedere a BluetoothAdapter

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.

kotlin
// 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.

Autorizzazioni Android per lavorare con BluetoothAdapter

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:

OperazioneAPI 23–30API 31+
Scansione BLEACCESS_FINE_LOCATIONBLUETOOTH_SCAN (senza posizione)
Connessione BLEACCESS_FINE_LOCATIONBLUETOOTH_CONNECT
Pubblicità BLEACCESS_FINE_LOCATIONBLUETOOTH_ADVERTISE
Attivazione/disattivazioneBLUETOOTH_ADMINBLUETOOTH_ADMIN (sistema)
Ottenimento indirizzo MACACCESS_FINE_LOCATIONBLUETOOTH_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.

kotlin
// 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 BluetoothAdapter in Kotlin

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.

kotlin
// 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

Cos'è BluetoothAdapter in Android?

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.

Perché BluetoothAdapter.getDefaultAdapter() restituisce null?

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.

In cosa differisce BluetoothLeScanner da startLeScan?

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.

Quali autorizzazioni sono necessarie per BLE su Android 12+?

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.

Si può attivare Bluetooth a livello di programmazione senza dialogo?

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

  • BluetoothAdapter è il punto di ingresso per tutte le operazioni Bluetooth su Android, ottenuto tramite BluetoothManager o getDefaultAdapter()
  • getState() e isEnabled sono controlli di base dello stato della radio Bluetooth prima di qualsiasi operazione BLE
  • BluetoothLeScanner (API 21+) sostituisce il deprecato startLeScan con supporto per ScanFilter, ScanSettings e PendingIntent
  • BluetoothManager è il modo moderno per ottenere BluetoothAdapter con supporto per scenari multi-account e Automotive
  • Le autorizzazioni si sono evolute: ACCESS_FINE_LOCATION (API 23-30) → BLUETOOTH_SCAN/CONNECT/ADVERTISE (API 31+)
  • enable()/disable() — su Android 10+ solo tramite il dialogo di sistema ACTION_REQUEST_ENABLE per applicazioni normali
  • connectGatt è un metodo di BluetoothDevice per stabilire una connessione GATT con periferiche BLE tramite BluetoothGattCallback

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.

Discuti il progetto

Leggi anche