BluetoothAdapter: qué es, métodos y gestión de Bluetooth en Android

Autor: IT Sectr Publicado: 2026-07-16 Tiempo de lectura: 10 min

BluetoothAdapter es una clase de sistema Android que representa el adaptador Bluetooth local de un dispositivo. BluetoothAdapter es el punto de entrada para todas las operaciones Bluetooth en Android: activar la radio (enable), escanear dispositivos, gestionar la visibilidad (setScanMode) y obtener información del adaptador (getName, getAddress, getState). La clase está disponible mediante BluetoothManager.getAdapter() (API 18+) o BluetoothAdapter.getDefaultAdapter(). En dispositivos sin módulo Bluetooth, getDefaultAdapter() devuelve null. Según Android Developers, 2026, BluetoothAdapter es un componente obligatorio para cualquier aplicación BLE en Android, desde API 5.

Puntos clave

  • BluetoothAdapter es una clase de sistema Android para gestionar el adaptador Bluetooth local de un dispositivo
  • getDefaultAdapter() es un método estático para obtener una instancia; devuelve null en dispositivos sin Bluetooth
  • getBluetoothLeScanner() es el método para acceder al escáner BLE moderno (API 21+)
  • enable/disable son métodos para activar y desactivar la radio Bluetooth (requieren permiso BLUETOOTH_ADMIN)
  • startDiscovery/startLeScan son métodos de escaneo clásico y BLE (LE Scan obsoleto, use BluetoothLeScanner)

Qué es BluetoothAdapter: esencia y obtención de la instancia

BluetoothAdapter representa el adaptador Bluetooth físico de un dispositivo Android. Cada dispositivo tiene exactamente un adaptador (excepto Android Automotive con múltiples chips Bluetooth, que usa BluetoothManager.getAdapterList()). BluetoothAdapter encapsula el estado de la radio: STATE_OFF (0), STATE_TURNING_ON (1), STATE_ON (2), STATE_TURNING_OFF (3). El estado se rastrea mediante BroadcastReceiver en ACTION_STATE_CHANGED.

Obtener una instancia de BluetoothAdapter es el primer paso de cualquier aplicación BLE en Android. El método recomendado es mediante BluetoothManager.getAdapter() desde API 18+. La alternativa es el método estático BluetoothAdapter.getDefaultAdapter(), que funciona desde API 5 pero es menos flexible. Ambos métodos devuelven null si el dispositivo no tiene módulo Bluetooth (tabletas solo Wi-Fi, emulador). La comprobación de null es obligatoria: la aplicación debe finalizar correctamente o deshabilitar las funciones BLE.

kotlin
// Obtener BluetoothAdapter (recomendado)
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.content.Context

class BluetoothHelper(context: Context) {

    private val bluetoothAdapter: BluetoothAdapter?

    init {
        // Método 1: mediante BluetoothManager (API 18+)
        val manager = context.getSystemService(Context.BLUETOOTH_SERVICE)
                as BluetoothManager?
        bluetoothAdapter = manager?.adapter

        // Método 2: mediante método estático (API 5+)
        // val adapter = BluetoothAdapter.getDefaultAdapter()

        // Comprobación de null
        if (bluetoothAdapter == null) {
            // El dispositivo no admite Bluetooth
        }
    }

    // Verificar estado de Bluetooth
    fun isBluetoothEnabled(): Boolean {
        return bluetoothAdapter?.isEnabled == true
    }

    // Obtener información del adaptador
    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 clase BluetoothHelper demuestra cómo obtener BluetoothAdapter mediante BluetoothManager con una comprobación de null posterior. isBluetoothEnabled verifica isEnabled, una condición obligatoria antes de cualquier operación BLE. getAdapterInfo devuelve el nombre del dispositivo, la dirección MAC, el estado y el modo de visibilidad. Importante: en Android 10+ (API 29+), el servicio del sistema devuelve una dirección MAC ficticia (02:00:00:00:00:00) si la aplicación no tiene permisos BLUETOOTH_ADMIN y ACCESS_FINE_LOCATION.

Métodos principales de BluetoothAdapter: enable, disable, getState, getAddress

BluetoothAdapter proporciona métodos para controlar la radio Bluetooth. enable() y disable() activan y desactivan el Bluetooth. Ambos métodos requieren el permiso BLUETOOTH_ADMIN y se ejecutan de forma asíncrona: después de llamar a enable(), el sistema inicia el proceso de activación de la radio y el estado se rastrea mediante BroadcastReceiver con la acción BluetoothAdapter.ACTION_STATE_CHANGED. En Android 10+, enable() y disable() requieren privilegios adicionales del sistema: las aplicaciones normales no pueden controlar Bluetooth mediante programación sin un diálogo de usuario.

getState() devuelve el estado actual del adaptador: STATE_OFF (10), STATE_TURNING_ON (11), STATE_ON (12), STATE_TURNING_OFF (13). getAddress() devuelve la dirección MAC del adaptador Bluetooth. En Android 6+, se requiere ACCESS_FINE_LOCATION (o ACCESS_COARSE_LOCATION para API 31+) para solicitar la dirección MAC. En Android 10+, getAddress() devuelve la dirección constante 02:00:00:00:00:00 — la dirección real no está disponible a través de la API pública.

getScanMode() determina el modo de visibilidad del adaptador: SCAN_MODE_NONE (invisible), SCAN_MODE_CONNECTABLE (visible para dispositivos conectados), SCAN_MODE_CONNECTABLE_DISCOVERABLE (visible para todos). El modo de visibilidad está limitado en el tiempo (normalmente 60–300 segundos) por seguridad. Establecer el modo mediante setScanMode() requiere BLUETOOTH_ADMIN y permiso del sistema en Android 10+.

MétodoDescripciónPermiso requerido
enable()Activar radio BluetoothBLUETOOTH_ADMIN
disable()Desactivar radio BluetoothBLUETOOTH_ADMIN
getState()Estado actual del adaptadorBLUETOOTH
getAddress()Dirección MAC del adaptadorBLUETOOTH + ACCESS_FINE_LOCATION (API 23+)
getScanMode()Modo de visibilidad del dispositivoBLUETOOTH
setScanMode()Establecer modo de visibilidadBLUETOOTH_ADMIN

Escaneo de dispositivos: startDiscovery y BluetoothLeScanner

BluetoothAdapter admite dos tipos de escaneo. El escaneo Bluetooth clásico (BR/EDR) se inicia mediante startDiscovery(): descubre dispositivos Bluetooth de todo tipo, incluidos teléfonos y auriculares. Los resultados se devuelven mediante BroadcastReceiver con la acción BluetoothDevice.ACTION_FOUND. startDiscovery() funciona durante 12 segundos y puede cancelarse llamando a cancelDiscovery(). Este método está obsoleto para BLE: use BluetoothLeScanner.

El escaneo BLE mediante BluetoothAdapter utiliza el método obsoleto startLeScan(LeScanCallback). A partir de API 21, Google recomienda usar BluetoothLeScanner, obtenido mediante BluetoothAdapter.getBluetoothLeScanner(). BluetoothLeScanner proporciona una API más flexible: configuración de escaneo mediante ScanSettings (modo, tipo de callback, modo de coincidencia), filtrado mediante ScanFilter (por UUID de servicio, nombre de dispositivo, dirección MAC) y soporte para PendingIntent para escaneo en segundo plano.

kotlin
// API de escaneo BLE antigua (obsoleta) vs nueva
import android.bluetooth.BluetoothAdapter
import android.bluetooth.le.*

class BLEScanner(private val bluetoothAdapter: BluetoothAdapter?) {

    // OBSOLETO: startLeScan (API 18+, API 21)
    @Suppress("DEPRECATION")
    fun legacyScan() {
        bluetoothAdapter?.startLeScan { device, rssi, scanRecord ->
            print("Found (LE Scan): $device.name, RSSI: $rssi")
        }
    }

    // NUEVO: BluetoothLeScanner (API 21+)
    fun modernScan() {
        val scanner = bluetoothAdapter?.bluetoothLeScanner
            ?: return

        // Configuración de escaneo
        val settings = ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
            .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
            .setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
            .build()

        // Filtrar por servicio (UUID de frecuencia cardíaca)
        val filters = listOf(
            ScanFilter.Builder()
                .setServiceUuid(ParcelUuid.fromString("0000180D-0000-1000-8000-00805F9B34FB"))
                .build()
        )

        // Iniciar escaneo
        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 clase BLEScanner compara el obsoleto startLeScan y el moderno BluetoothLeScanner. En legacyScan, el LeScanCallback recibe BluetoothDevice, RSSI y scanRecord sin procesar. En modernScan, se usan ScanSettings con modo LOW_LATENCY (máxima velocidad de detección) y ScanFilter para filtrar por UUID del servicio de frecuencia cardíaca (0x180D). ScanCallback proporciona onScanResult con un objeto ScanResult que contiene información ampliada: nombre, RSSI, datos de publicidad, tipo de conexión.

BluetoothManager: API moderna para acceder a BluetoothAdapter

BluetoothManager es un servicio de sistema Android, introducido en API 18 (Android 4.3), para gestionar operaciones Bluetooth. Antes de API 18, la única forma de obtener BluetoothAdapter era el método estático getDefaultAdapter(). BluetoothManager proporciona: adapter — una instancia de BluetoothAdapter, getConnectedDevices() — una lista de dispositivos conectados, getDevicesMatchingConnectionStates() — filtrado por estado. BluetoothManager también se usa para obtener BluetoothLeScanner en APIs antiguas.

Ventajas de BluetoothManager sobre llamar directamente a BluetoothAdapter.getDefaultAdapter(): la aplicación no depende de un singleton estático, el gestor respeta el contexto (Activity/Application), lo que es importante para escenarios multi-cuenta de Android Enterprise. En Android Automotive con múltiples chips Bluetooth, BluetoothManager.getAdapterList() devuelve todos los adaptadores disponibles — BluetoothAdapter.getDefaultAdapter() devuelve solo el primero.

kotlin
// Usar BluetoothManager para BLE
class BLEConnection(context: Context) {

    private val bluetoothManager: BluetoothManager =
        context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager

    private val adapter: BluetoothAdapter? = bluetoothManager.adapter

    // Obtener lista de dispositivos BLE conectados
    fun getConnectedDevices(): List<BluetoothDevice> {
        return bluetoothManager.getConnectedDevices(
            BluetoothProfile.GATT
        )
    }

    // Filtrar dispositivos por estado
    fun getDevicesByState(states: IntArray): List<BluetoothDevice> {
        return bluetoothManager.getDevicesMatchingConnectionStates(
            BluetoothProfile.GATT, states
        )
    }

    // Verificar soporte BLE en el dispositivo
    fun isBLESupported(): Boolean {
        return adapter != null && context.packageManager
            .hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)
    }

    // Solicitar activación de Bluetooth mediante diálogo del 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 clase BLEConnection usa BluetoothManager para acceder a BluetoothAdapter y obtener la lista de dispositivos GATT conectados. isBLESupported verifica la presencia de radio BLE mediante PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) — una comprobación importante para dispositivos con Bluetooth Classic sin BLE. requestEnableBluetooth muestra el diálogo del sistema para activar Bluetooth (ACTION_REQUEST_ENABLE), sin requerir permiso BLUETOOTH_ADMIN — esta es la única forma legal de activar Bluetooth en Android 10+ sin una aplicación de sistema.

Permisos de Android para trabajar con BluetoothAdapter

Los permisos para BluetoothAdapter han evolucionado con cada versión de Android. En Android 6–11 (API 23–30), se requieren BLUETOOTH, BLUETOOTH_ADMIN y ACCESS_FINE_LOCATION para el escaneo BLE. En Android 12+ (API 31+), Google dividió los permisos: ACCESS_FINE_LOCATION se reemplaza por BLUETOOTH_SCAN (escaneo), BLUETOOTH_CONNECT (conexión), BLUETOOTH_ADVERTISE (publicidad). Para la detección de dispositivos BLE, BLUETOOTH_SCAN es suficiente, no se requiere ubicación.

Tabla de permisos por versión de Android:

OperaciónAPI 23–30API 31+
Escaneo BLEACCESS_FINE_LOCATIONBLUETOOTH_SCAN (sin ubicación)
Conexión BLEACCESS_FINE_LOCATIONBLUETOOTH_CONNECT
Publicidad BLEACCESS_FINE_LOCATIONBLUETOOTH_ADVERTISE
Activar/desactivarBLUETOOTH_ADMINBLUETOOTH_ADMIN (sistema)
Obtener dirección MACACCESS_FINE_LOCATIONBLUETOOTH_CONNECT (dirección ficticia)

En Android 12+, todos los permisos Bluetooth son permisos de tiempo de ejecución: deben solicitarse en tiempo de ejecución mediante ActivityResultContracts.RequestMultiplePermissions. BLUETOOTH_SCAN y BLUETOOTH_ADVERTISE pertenecen al grupo NEARBY_DEVICES, BLUETOOTH_CONNECT pertenece al mismo grupo. Los permisos BLUETOOTH y BLUETOOTH_ADMIN permanecen en el manifiesto para compatibilidad con API < 31, pero para API 31+ se ignoran — Google requiere especificar explícitamente los nuevos permisos.

kotlin
// Solicitar permisos Bluetooth en 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
            )
        }
    }

    // Verificar todos los permisos
    fun hasPermissions(context: Context): Boolean {
        return getRequiredPermissions().all { permission ->
            ContextCompat.checkSelfPermission(context, permission)
                    == PackageManager.PERMISSION_GRANTED
        }
    }
}

La clase PermissionHelper devuelve el conjunto correcto de permisos según el nivel de API. En Android 12+, se usan BLUETOOTH_SCAN, BLUETOOTH_CONNECT, BLUETOOTH_ADVERTISE sin ubicación. En Android 6–11, todavía se requiere ACCESS_FINE_LOCATION para el escaneo BLE. El desarrollador debe considerar ambos escenarios al solicitar permisos mediante ActivityResultContracts o RxPermissions.

Ejemplo completo de BluetoothAdapter en Kotlin

Ejemplo completo de una aplicación BLE en Kotlin que usa BluetoothAdapter para escanear, conectar y leer datos de un dispositivo BLE. El ejemplo cubre la verificación de permisos, la obtención del adaptador, el escaneo mediante BluetoothLeScanner y la conexión mediante BluetoothDevice.connectGatt.

kotlin
// Gestor BLE completo 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. Descubrimiento de servicios
    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. Detener escaneo
    fun stopScanning() {
        scanner?.stopScan(object : ScanCallback() {})
    }

    // 4. Conectar 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) {
                    // Servicios encontrados, se pueden leer características
                }
            },
            BluetoothDevice.TRANSPORT_LE
        )
    }

    // 5. Liberar recursos
    fun disconnect() {
        gatt?.disconnect()
        gatt?.close()
        gatt = null
    }
}

La clase BLEManager unifica el ciclo BLE completo en Android: verificación del adaptador y permisos (canScan), escaneo mediante BluetoothLeScanner con ScanSettings (startScanning), conexión mediante BluetoothDevice.connectGatt con TRANSPORT_LE (connectToDevice) y liberación de recursos (disconnect). Todas las operaciones BLE se ejecutan en el hilo de la interfaz de usuario: Android llama a las devoluciones de llamada BluetoothGattCallback en el hilo principal. Para tareas BLE intensivas, se recomienda trasladar las operaciones GATT a un HandlerThread en segundo plano.

Preguntas frecuentes

Qué es BluetoothAdapter en Android?

BluetoothAdapter es una clase que representa el adaptador Bluetooth local de un dispositivo Android. Se obtiene mediante BluetoothManager.getAdapter() (API 18+) o BluetoothAdapter.getDefaultAdapter(). Proporciona métodos para activar/desactivar Bluetooth, escanear dispositivos, gestionar la visibilidad y obtener información del adaptador. Devuelve null en dispositivos sin módulo Bluetooth.

Por qué BluetoothAdapter.getDefaultAdapter() devuelve null?

La razón es la ausencia de radio Bluetooth en el dispositivo. Es típico en tabletas solo Wi-Fi, emulador Android y Android TV sin Bluetooth. Verifique getDefaultAdapter() como null al iniciar la aplicación y deshabilite las funciones BLE si el adaptador está ausente. Una alternativa es verificar mediante PackageManager.hasSystemFeature(FEATURE_BLUETOOTH_LE) para una detección más precisa.

En qué se diferencia BluetoothLeScanner de startLeScan?

BluetoothLeScanner (API 21+) es la API moderna para escaneo BLE con soporte para ScanFilter, ScanSettings y PendingIntent. startLeScan (API 18+) es un método obsoleto de BluetoothAdapter que acepta LeScanCallback con un conjunto de datos limitado. Google recomienda BluetoothLeScanner para todos los proyectos nuevos, permite filtrar por UUID, configurar el modo de consumo de energía y funcionar en segundo plano mediante PendingIntent.

Qué permisos se necesitan para BLE en Android 12+?

En Android 12+ (API 31), el escaneo BLE requiere BLUETOOTH_SCAN, la conexión requiere BLUETOOTH_CONNECT y la publicidad requiere BLUETOOTH_ADVERTISE. El permiso de ubicación ACCESS_FINE_LOCATION ya no es necesario para BLE. En Android 6–11, se requiere ACCESS_FINE_LOCATION. Todos los permisos se solicitan en tiempo de ejecución mediante ActivityResultContracts.

Se puede activar Bluetooth mediante programación sin diálogo?

En Android 10+, la activación programática de Bluetooth sin diálogo del sistema solo está disponible para aplicaciones de sistema con permiso BLUETOOTH_PRIVILEGED. Las aplicaciones normales deben usar Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE) y startActivityForResult: el usuario confirma la activación en el diálogo del sistema. BLUETOOTH_ADMIN en el manifiesto no otorga derechos enable() en Android 10+.

Resumen

  • BluetoothAdapter es el punto de entrada para todas las operaciones Bluetooth en Android, se obtiene mediante BluetoothManager o getDefaultAdapter()
  • getState() e isEnabled son comprobaciones básicas del estado de la radio Bluetooth antes de cualquier operación BLE
  • BluetoothLeScanner (API 21+) reemplaza al obsoleto startLeScan con soporte para ScanFilter, ScanSettings y PendingIntent
  • BluetoothManager es la forma moderna de obtener BluetoothAdapter con soporte para escenarios multi-cuenta y Automotive
  • Los permisos evolucionaron: ACCESS_FINE_LOCATION (API 23-30) → BLUETOOTH_SCAN/CONNECT/ADVERTISE (API 31+)
  • enable()/disable() — en Android 10+ solo a través del diálogo del sistema ACTION_REQUEST_ENABLE para aplicaciones normales
  • connectGatt es un método de BluetoothDevice para establecer una conexión GATT con periféricos BLE mediante BluetoothGattCallback

Desarrollaremos una aplicación móvil llave en mano

IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.

Discutir el proyecto

Lea también