Introducción general a Bluetooth

La plataforma de Android incluye compatibilidad con la pila de red Bluetooth, la cual permite que un dispositivo intercambie datos de manera inalámbrica con otros dispositivos Bluetooth. El marco de trabajo de la aplicación proporciona acceso a la funcionalidad Bluetooth mediante las API de Bluetooth de Android. Estas API permiten a las aplicaciones conectarse de manera inalámbrica con otros dispositivos Bluetooth y habilitan las funciones inalámbricas punto a punto y multipunto.

Con las API de Bluetooth, una aplicación de Android puede realizar lo siguiente:

  • Buscar otros dispositivos Bluetooth
  • Consultar el adaptador local de Bluetooth en busca de dispositivos Bluetooth sincronizados
  • Establecer canales RFCOMM
  • Conectarse con otros dispositivos mediante el descubrimiento de servicios
  • Transferir datos hacia otros dispositivos y desde estos
  • Administrar varias conexiones

Esta página se centra en el sistema de Bluetooth clásico. Este sistema es la opción ideal para las operaciones que consumen más batería, como la transmisión y la comunicación entre dispositivos Android. Para dispositivos Bluetooth con bajos requisitos de energía, Android 4.3 (API de nivel 18) presenta compatibilidad de API con Bluetooth de bajo consumo. Para obtener más información, consulta Bluetooth de bajo consumo.

En este documento, se describen diferentes perfiles de Bluetooth, incluido el perfil de dispositivo de salud. Luego, se describe la manera de usar las API de Bluetooth de Android a fin de llevar a cabo las cuatro tareas principales necesarias para establecer la comunicación a través de Bluetooth: configuración de Bluetooth, búsqueda de dispositivos sincronizados o disponibles en el área local, conexión con dispositivos y transferencia de datos entre dispositivos.

Conceptos básicos

Para que los dispositivos compatibles con Bluetooth transmitan datos entre sí, primero deben establecer un canal de comunicación mediante un proceso de sincronización. Un dispositivo (el dispositivo detectable) se habilita para recibir solicitudes de conexión entrantes. Otro dispositivo encuentra el dispositivo detectable mediante un proceso de detección del servicio. Después de que el dispositivo detectable acepta la solicitud de sincronización, los dos dispositivos completan un proceso de conexión en el que se intercambian claves de seguridad. Ambos dispositivos almacenan estas claves en caché para usarlas posteriormente. Después de que terminan los procesos de sincronización y conexión, los dispositivos intercambian información. Cuando se completa la sesión, el dispositivo que inicializó la solicitud de sincronización libera el canal que usó para la sincronización con el dispositivo detectable. Sin embargo, siempre y cuando estén dentro del rango de alcance y ninguno elimine la conexión, los dos dispositivos permanecen vinculados para poder volver a conectarse automáticamente en una sesión futura.

Permisos de Bluetooth

Para usar las funciones de Bluetooth en tu aplicación, debes declarar dos permisos. El primero es BLUETOOTH. Necesitas este permiso para establecer cualquier comunicación de Bluetooth, como solicitar o aceptar una conexión y transferir datos.

El otro permiso que debes declarar es ACCESS_COARSE_LOCATION o ACCESS_FINE_LOCATION. Se requiere un permiso de ubicación porque las operaciones de escaneo de Bluetooth pueden usarse para recopilar información sobre la ubicación del usuario. Esta información puede recogerse desde el dispositivo del usuario o desde emisores beacon en determinados lugares, como tiendas y áreas de tránsito.

Si deseas que tu aplicación inicialice la detección de dispositivos o controle la configuración de Bluetooth, debes declarar el permiso BLUETOOTH_ADMIN además del permiso BLUETOOTH. La mayoría de las aplicaciones necesitan este permiso solamente para poder ver dispositivos Bluetooth locales. Las demás capacidades que otorga este permiso no deben usarse, a menos que la aplicación sea un “administrador de energía” que modifique la configuración de Bluetooth a pedido del usuario.

Declara los permisos de Bluetooth del archivo de manifiesto de tu aplicación. Por ejemplo:

<manifest ... >
  <uses-permission android:name="android.permission.BLUETOOTH" />
  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  ...
</manifest>

Consulta la referencia <uses-permission> para obtener más información sobre cómo declarar permisos de la aplicación.

Trabajar con perfiles

A partir de Android 3.0, la API de Bluetooth incluye compatibilidad para trabajar con perfiles de Bluetooth. Un perfil de Bluetooth es una especificación de interfaz inalámbrica para la comunicación entre dispositivos basada en Bluetooth. Un ejemplo es el perfil de manos libres. Para que un teléfono móvil se conecte a auriculares inalámbricos, ambos dispositivos deben ser compatibles con el perfil de manos libres.

La API de Bluetooth de Android proporciona implementaciones para los siguientes perfiles de Bluetooth:

  • Auriculares. El perfil de auriculares ofrece compatibilidad con auriculares Bluetooth para usarlos con teléfonos móviles. Android proporciona la clase BluetoothHeadset, que es un proxy para controlar el servicio de auriculares Bluetooth. Esto incluye los perfiles de manos libres (v1.5) y de auriculares Bluetooth. La clase BluetoothHeadset incluye compatibilidad con comandos de AT. Para obtener más información sobre este tema, consulta la sección Comandos de AT específicos del proveedor.
  • A2DP. El perfil de distribución de audio avanzada (A2DP) define el nivel de audio de alta calidad que puede transmitirse de un dispositivo a otro a través de una conexión Bluetooth. Android proporciona la clase BluetoothA2dp, que es un proxy para controlar el servicio de A2DP Bluetooth.
  • Dispositivo de salud. Android 4.0 (nivel de API 14) presenta compatibilidad con el perfil de dispositivos de salud (HDP) Bluetooth. Esto te permite crear aplicaciones que usen Bluetooth para comunicarse con dispositivos de salud que admiten Bluetooth, como monitores de frecuencia cardíaca, medidores de la sangre, termómetros y balanzas, entre otros. Para acceder a una lista de los dispositivos compatibles y sus correspondientes códigos de especialización de los datos del dispositivo, consulta la sección Especializaciones de datos de los dispositivos con HDP de Bluetooth. También se hace referencia a estos valores en la especificación ISO/IEEE 11073-20601 [7] como MDC_DEV_SPEC_PROFILE_* en el Anexo de códigos de nomenclaturas. Para obtener más información sobre el HDP, consulta la sección Perfil de dispositivos de salud.

A continuación, se detallan los pasos básicos para trabajar con un perfil:

  1. Obtén el adaptador predeterminado, tal como se describe en la sección Configuración de Bluetooth.
  2. Configura un BluetoothProfile.ServiceListener. Este notifica a los clientes del BluetoothProfile cuando se conectan al servicio o se desconectan de este.
  3. Usa getProfileProxy() para entablar una conexión al objeto del proxy de perfil asociado a dicho perfil. En el siguiente ejemplo, el objeto del proxy de perfil es una instancia de BluetoothHeadset.
  4. En onServiceConnected(), obtén un controlador para el objeto del proxy de perfil.
  5. Una vez que tengas el objeto del proxy de perfil, puedes usarlo para controlar el estado de la conexión y realizar otras operaciones correspondientes a ese perfil.

Por ejemplo, en este fragmento de código, se muestra la manera de conectarse a un objeto del proxy de BluetoothHeadset, de modo que se pueda controlar el perfil de auriculares:

Kotlin

var bluetoothHeadset: BluetoothHeadset? = null

// Get the default adapter
val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()

private val profileListener = object : BluetoothProfile.ServiceListener {

    override fun onServiceConnected(profile: Int, proxy: BluetoothProfile) {
        if (profile == BluetoothProfile.HEADSET) {
            bluetoothHeadset = proxy as BluetoothHeadset
        }
    }

    override fun onServiceDisconnected(profile: Int) {
        if (profile == BluetoothProfile.HEADSET) {
            bluetoothHeadset = null
        }
    }
}

// Establish connection to the proxy.
bluetoothAdapter?.getProfileProxy(context, profileListener, BluetoothProfile.HEADSET)

// ... call functions on bluetoothHeadset

// Close proxy connection after use.
bluetoothAdapter?.closeProfileProxy(BluetoothProfile.HEADSET, bluetoothHeadset)

Java

BluetoothHeadset bluetoothHeadset;

// Get the default adapter
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

private BluetoothProfile.ServiceListener profileListener = new BluetoothProfile.ServiceListener() {
    public void onServiceConnected(int profile, BluetoothProfile proxy) {
        if (profile == BluetoothProfile.HEADSET) {
            bluetoothHeadset = (BluetoothHeadset) proxy;
        }
    }
    public void onServiceDisconnected(int profile) {
        if (profile == BluetoothProfile.HEADSET) {
            bluetoothHeadset = null;
        }
    }
};

// Establish connection to the proxy.
bluetoothAdapter.getProfileProxy(context, profileListener, BluetoothProfile.HEADSET);

// ... call functions on bluetoothHeadset

// Close proxy connection after use.
bluetoothAdapter.closeProfileProxy(bluetoothHeadset);

Comandos de AT específicos del proveedor

A partir de Android 3.0 (nivel de API 11), las aplicaciones pueden registrarse para recibir transmisiones del sistema de comandos de AT específicos del proveedor previamente definidos que envían los auriculares (como un comando +XEVENT de Plantronics). Por ejemplo, una aplicación podría recibir transmisiones que indiquen el nivel de batería de un dispositivo conectado y notificar al usuario o tomar otras medidas según sea necesario. Crea un receptor de transmisiones para que la intent ACTION_VENDOR_SPECIFIC_HEADSET_EVENT controle comandos de AT específicos del proveedor para los auriculares.

Perfil de dispositivos de salud

Android 4.0 (nivel de API 14) presenta compatibilidad con el perfil de dispositivos de salud (HDP) Bluetooth. Esto te permite crear aplicaciones que usan Bluetooth para comunicarse con dispositivos de salud que admiten Bluetooth, como monitores de frecuencia cardíaca, medidores de la sangre, termómetros y balanzas. La API de salud de Bluetooth incluye las clases BluetoothHealth, BluetoothHealthCallback y BluetoothHealthAppConfiguration, que se describen en la sección Clases fundamentales e interfaces.

Cuando se usa la API de salud de Bluetooth, resulta útil comprender estos conceptos clave del HDP:

Fuente
Un dispositivo de salud (como una balanza, un medidor de glucosa o un termómetro) que transmite datos médicos a un dispositivo inteligente, como una tablet o un teléfono Android.
Receptor
El dispositivo inteligente que recibe los datos médicos. En una aplicación del HDP de Android, el receptor se representa mediante un objeto BluetoothHealthAppConfiguration.
Registro
El proceso que se usa para registrar un receptor a fin de comunicarse con un dispositivo de salud específico.
Conexión
El proceso que se usa para abrir un canal entre un dispositivo de salud (fuente) y un dispositivo inteligente (receptor).

Crear una aplicación del HDP

Estos son los pasos básicos para crear una aplicación del HDP de Android:

  1. Obtén una referencia al objeto del proxy BluetoothHealth.

    Aplicando un procedimiento similar al de los dispositivos de perfil A2DP y de auriculares, debes llamar a getProfileProxy() con un BluetoothProfile.ServiceListener y el tipo de perfil HEALTH para establecer una conexión con el objeto del proxy de perfil.

  2. Crea una BluetoothHealthCallback y registra una configuración de la aplicación (BluetoothHealthAppConfiguration) que funcione como receptor de salud.
  3. Establece una conexión con el dispositivo de salud.

    Nota: Algunos dispositivos inicializan la conexión automáticamente. No es necesario seguir este paso en el caso de esos dispositivos.

  4. Cuando te conectes con éxito a un dispositivo de salud, realiza operaciones de lectura o escritura en el dispositivo de salud mediante el descriptor del archivo. Los datos recibidos deben interpretarse mediante un administrador de salud que implemente las especificaciones IEEE 11073.
  5. Cuando finalices, cierra el canal de salud y elimina el registro de la aplicación. El canal también se cierra cuando existe inactividad por tiempo prolongado.

Configurar el sistema Bluetooth

Para que tu aplicación pueda comunicarse a través de Bluetooth, debes verificar que Bluetooth sea compatible con el dispositivo y, si es así, asegurarte de que esté habilitado.

Si Bluetooth no es compatible, debes inhabilitar correctamente cualquier función de Bluetooth. En caso de que Bluetooth sea compatible, pero esté inhabilitado, puedes solicitar que el usuario lo habilite sin abandonar tu aplicación. Esta configuración se logra en dos pasos, mediante el BluetoothAdapter.

  1. Obtén el BluetoothAdapter

    El BluetoothAdapter es obligatorio para toda actividad de Bluetooth. Para obtener el BluetoothAdapter, llama al método estático del getDefaultAdapter(). Esto muestra un BluetoothAdapter que representa el propio adaptador de Bluetooth del dispositivo (la radio Bluetooth). Existe un adaptador de Bluetooth para todo el sistema y tu aplicación puede interactuar con él usando este objeto. Si getDefaultAdapter() devuelve null, significa que el dispositivo no es compatible con Bluetooth. Por ejemplo:

    Kotlin

    val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
    if (bluetoothAdapter == null) {
        // Device doesn't support Bluetooth
    }
    

    Java

    BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
    if (bluetoothAdapter == null) {
        // Device doesn't support Bluetooth
    }
    
  2. Habilita Bluetooth.

    A continuación, debes asegurarte de que Bluetooth esté habilitado. Llama al isEnabled() para verificar si Bluetooth se encuentra actualmente habilitado. Si este método muestra “false”, Bluetooth no estará habilitado. Para solicitar que Bluetooth esté habilitado, llama a startActivityForResult() pasando una acción de intent ACTION_REQUEST_ENABLE. Esto emite una solicitud para habilitar Bluetooth mediante la configuración del sistema (sin detener tu aplicación). Por ejemplo:

    Kotlin

    if (bluetoothAdapter?.isEnabled == false) {
        val enableBtIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
    }
    

    Java

    if (!bluetoothAdapter.isEnabled()) {
        Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
    }
    

    Se muestra un cuadro de diálogo (como en la figura 1) en el que se solicita permiso al usuario para habilitar Bluetooth. Si el usuario responde “Sí”, el sistema comienza a habilitar Bluetooth y el enfoque vuelve a tu aplicación una vez que el proceso se completa con éxito (o no).

    Figura 1: Cuadro de diálogo de habilitación de Bluetooth

    La constante REQUEST_ENABLE_BT que se pasa a startActivityForResult() es un valor entero definido localmente que debe ser superior a 0. El sistema te devuelve esta constante en tu implementación de onActivityResult() como parámetro requestCode.

    Si la habilitación de Bluetooth se realiza con éxito, tu actividad recibe el código de resultado RESULT_OK en la devolución de llamada onActivityResult(). Si Bluetooth no se habilita debido a un error (o a que el usuario responde “No”), el código de resultado será RESULT_CANCELED.

Opcionalmente, tu aplicación también puede recibir la intent de transmisión ACTION_STATE_CHANGED, que el sistema emite cada vez que cambia el estado de Bluetooth. Esta emisión contiene los campos adicionales EXTRA_STATE y EXTRA_PREVIOUS_STATE, que incluyen los estados de Bluetooth nuevo y antiguo, respectivamente. Los valores posibles de estos campos adicionales son STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF y STATE_OFF. La recepción de esta emisión puede ser útil si tu aplicación necesita detectar cambios en el tiempo de ejecución realizados en el estado de Bluetooth.

Sugerencia: La habilitación de la visibilidad habilita Bluetooth automáticamente. Si tienes previsto habilitar la visibilidad de dispositivos de manera constante antes de llevar a cabo la actividad de Bluetooth, puedes omitir el paso 2 detallado anteriormente. Para obtener más información, consulta la sección Habilitación de la visibilidad en esta página.

Buscar dispositivos

Si usas BluetoothAdapter, puedes buscar dispositivos Bluetooth remotos mediante la detección de dispositivos o la consulta de la lista de dispositivos sincronizados.

La detección de dispositivos es un procedimiento de escaneo que busca en el área local dispositivos con Bluetooth habilitado y, luego, solicita información sobre cada uno de ellos. A menudo, esto se denomina detección, consulta o escaneo. Sin embargo, un dispositivo Bluetooth cercano solamente responde a una solicitud de visibilidad cuando es detectable y acepta solicitudes de información. Si un dispositivo es detectable, comparte información (como su nombre, su clase y su dirección MAC única) como respuesta a la solicitud de detección. Al usar esta información, el dispositivo que lleva a cabo la visibilidad puede decidir inicializar una conexión con el dispositivo detectado.

Una vez que se establezca una conexión con un dispositivo remoto por primera vez, se presentará automáticamente al usuario una solicitud de vinculación. Cuando un dispositivo está vinculado, la información básica sobre este (como el nombre, la clase y la dirección MAC) se guarda y se puede leer a través de las API de Bluetooth. Usando la dirección MAC conocida de un dispositivo remoto, se puede establecer una conexión con este en cualquier momento sin llevar a cabo la detección, suponiendo que el dispositivo todavía se encuentre dentro del rango.

Ten presente que existe una diferencia entre la vinculación y la conexión:

  • Para estar vinculados, dos dispositivos deben reconocer su existencia mutuamente, tener una clave de enlace compartida que pueda usarse para autenticación y ser capaces de establecer una conexión cifrada entre sí.
  • Para estar conectados, los dispositivos deben compartir un canal RFCOMM y tener la capacidad de transmitirse datos entre sí. Las API de Bluetooth de Android actuales requieren que los dispositivos estén vinculados para que se pueda establecer una conexión RFCOMM. La vinculación se realiza automáticamente cuando inicializas una conexión cifrada con las API de Bluetooth.

En las siguientes secciones, se describe la manera de buscar dispositivos vinculados o hallar dispositivos nuevos mediante la detección de dispositivos.

Nota: Los dispositivos con tecnología de Android no pueden detectarse de manera predeterminada. Un usuario puede hacer que el dispositivo sea detectable durante un tiempo limitado a través de los ajustes del sistema o una aplicación puede solicitar que el usuario habilite la visibilidad sin salir de la aplicación. Para obtener más información, consulta la sección Habilitar la visibilidad en esta página.

Realizar consultas a dispositivos sincronizados

Antes de llevar a cabo la detección de dispositivos, es importante consultar el conjunto de dispositivos sincronizados a fin de ver si el dispositivo deseado ya es conocido. Para ello, llama a getBondedDevices(). Esto devuelve un conjunto de objetos BluetoothDevice que representa a los dispositivos sincronizados. Por ejemplo, puedes consultar todos los dispositivos sincronizados y obtener el nombre y la dirección MAC de cada uno, como se demuestra en el siguiente fragmento de código:

Kotlin

val pairedDevices: Set<BluetoothDevice>? = bluetoothAdapter?.bondedDevices
pairedDevices?.forEach { device ->
    val deviceName = device.name
    val deviceHardwareAddress = device.address // MAC address
}

Java

Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();

if (pairedDevices.size() > 0) {
    // There are paired devices. Get the name and address of each paired device.
    for (BluetoothDevice device : pairedDevices) {
        String deviceName = device.getName();
        String deviceHardwareAddress = device.getAddress(); // MAC address
    }
}

Para inicializar una conexión con un dispositivo Bluetooth, solo se necesita la dirección MAC del objeto BluetoothDevice asociado, que puedes recuperar llamando a getAddress(). Puedes obtener más información acerca de cómo crear una conexión en la sección Conectar dispositivos.

Advertencia: La detección de dispositivos consume muchos recursos del adaptador de Bluetooth. Una vez que hayas encontrado un dispositivo al cual conectarte, asegúrate de detener la detección con cancelDiscovery() antes de intentar establecer una conexión. Además, no debes realizar la detección mientras estás conectado a un dispositivo, ya que el proceso de detección reduce notablemente el ancho de banda disponible para las conexiones existentes.

Detectar dispositivos

Para comenzar a detectar dispositivos, tan solo debes llamar a startDiscovery(). El proceso es asíncrono y devuelve un valor booleano que indica si se inició correctamente la detección. Por lo general, el proceso de visibilidad incluye un escaneo de consulta de 12 segundos aproximadamente, seguido de un escaneo de la página de cada dispositivo encontrado a fin de recuperar su nombre de Bluetooth.

A fin de recibir información sobre cada dispositivo detectado, tu aplicación debe registrar un BroadcastReceiver para la intent ACTION_FOUND. El sistema emite esta intent para cada dispositivo. La intent contiene los campos adicionales EXTRA_DEVICE y EXTRA_CLASS, que, a su vez, contienen un BluetoothDevice y una BluetoothClass, respectivamente. El siguiente fragmento de código permite ver cómo puedes registrarte para manejar la emisión cuando se detectan los dispositivos:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    ...

    // Register for broadcasts when a device is discovered.
    val filter = IntentFilter(BluetoothDevice.ACTION_FOUND)
    registerReceiver(receiver, filter)
}

// Create a BroadcastReceiver for ACTION_FOUND.
private val receiver = object : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        val action: String = intent.action
        when(action) {
            BluetoothDevice.ACTION_FOUND -> {
                // Discovery has found a device. Get the BluetoothDevice
                // object and its info from the Intent.
                val device: BluetoothDevice =
                        intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
                val deviceName = device.name
                val deviceHardwareAddress = device.address // MAC address
            }
        }
    }
}

override fun onDestroy() {
    super.onDestroy()
    ...

    // Don't forget to unregister the ACTION_FOUND receiver.
    unregisterReceiver(receiver)
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    ...

    // Register for broadcasts when a device is discovered.
    IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
    registerReceiver(receiver, filter);
}

// Create a BroadcastReceiver for ACTION_FOUND.
private final BroadcastReceiver receiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            // Discovery has found a device. Get the BluetoothDevice
            // object and its info from the Intent.
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            String deviceName = device.getName();
            String deviceHardwareAddress = device.getAddress(); // MAC address
        }
    }
};

@Override
protected void onDestroy() {
    super.onDestroy();
    ...

    // Don't forget to unregister the ACTION_FOUND receiver.
    unregisterReceiver(receiver);
}

Para inicializar una conexión con un dispositivo Bluetooth, solo se necesita la dirección MAC del objeto BluetoothDevice asociado, que puedes recuperar llamando a getAddress(). Puedes obtener más información acerca de cómo crear una conexión en la sección Conectar dispositivos.

Advertencia: La detección de dispositivos consume muchos recursos del adaptador de Bluetooth. Una vez que hayas encontrado un dispositivo al cual conectarte, asegúrate de detener la detección con cancelDiscovery() antes de intentar establecer una conexión. Además, no debes realizar la detección mientras estás conectado a un dispositivo, ya que el proceso de detección reduce notablemente el ancho de banda disponible para las conexiones existentes.

Habilitar la visibilidad

Si deseas hacer que el dispositivo local sea detectable para otros dispositivos, llama a startActivityForResult(Intent, int) con la intent ACTION_REQUEST_DISCOVERABLE. Con esto, se emite una solicitud para habilitar el modo detectable del sistema sin tener que navegar a la aplicación de configuración, lo que detendría tu propia aplicación. De forma predeterminada, el dispositivo se vuelve detectable durante 120 segundos (o 2 minutos). Puedes definir otra duración agregando el objeto EXTRA_DISCOVERABLE_DURATION adicional, siempre y cuando no supere los 3600 segundos (o una hora).

Advertencia: : Si configuras el valor adicional de EXTRA_DISCOVERABLE_DURATION en 0, el dispositivo permanece siempre detectable. No se recomienda esta configuración debido a su alto nivel de inseguridad.

Con el siguiente fragmento de código, el dispositivo permanece detectable durante cinco minutos (300 segundos):

Kotlin

val discoverableIntent: Intent = Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE).apply {
    putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300)
}
startActivity(discoverableIntent)

Java

Intent discoverableIntent =
        new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
Figura 2: Cuadro de diálogo de habilitación de la visibilidad

Se mostrará un cuadro de diálogo en el que se solicitará permiso al usuario para hacer que el dispositivo sea detectable, como se muestra en la figura 2. Si el usuario responde “Sí”, el dispositivo será detectable durante el período especificado. Luego, tu actividad recibirá una llamada a la devolución de llamada onActivityResult(), con un código de resultado equivalente a la duración por la cual el dispositivo es detectable. Si el usuario responde “No” o se produce un error, el código de resultado será RESULT_CANCELED.

Nota: Si no se habilitó Bluetooth en el dispositivo, se habilitará Bluetooth automáticamente cuando se habilite la visibilidad de dispositivos.

El dispositivo se mantendrá silenciosamente en el modo detectable durante el tiempo asignado. Si deseas recibir una notificación cuando se modifique el modo de capacidad de detección, podrás registrar una clase BroadcastReceiver para la intent .ACTION_SCAN_MODE_CHANGED Esta intent incluye los campos adicionales EXTRA_SCAN_MODE y EXTRA_PREVIOUS_SCAN_MODE, los cuales te notificarán el modo de escaneo nuevo y el antiguo, respectivamente. Estos son los posibles valores de cada campo adicional:

SCAN_MODE_CONNECTABLE_DISCOVERABLE
El dispositivo está en modo detectable.
SCAN_MODE_CONNECTABLE
El dispositivo no está en modo detectable, pero todavía puede recibir conexiones.
SCAN_MODE_NONE
El dispositivo no está en modo detectable y no puede recibir conexiones.

Si inicializas la conexión con un dispositivo remoto, no hace falta que habilites la visibilidad del dispositivo. Solo debes hacerlo si quieres que tu aplicación aloje un socket de servidor que acepte conexiones entrantes, ya que los dispositivos remotos deben poder detectar otros dispositivos antes de inicializar conexiones con ellos.

Conectar dispositivos

A fin de crear una conexión entre dos dispositivos, debes implementar los mecanismos del lado del servidor y del lado del cliente, porque un dispositivo debe abrir un socket de servidor y el otro debe inicializar la conexión usando la dirección MAC del dispositivo del servidor para inicializar la conexión. El dispositivo del servidor y el dispositivo del cliente obtienen el BluetoothSocket obligatorio de diferentes maneras. El servidor recibe la información del socket cuando se acepta una conexión entrante. El cliente proporciona la información del socket cuando abre un canal RFCOMM hacia el servidor.

El servidor y el cliente se consideran conectados entre sí cuando tienen un BluetoothSocket conectado en el mismo canal RFCOMM. En este punto, cada dispositivo puede obtener flujos de entrada y salida, y se puede iniciar la transferencia de datos, que se trata en la sección Administrar una conexión. En esta sección, se describe la manera de inicializar la conexión entre dos dispositivos.

Técnicas de conexión

Una técnica de implementación consiste en preparar automáticamente cada dispositivo como un servidor, de modo que cada uno de ellos tenga un socket de servidor abierto y reciba conexiones. Luego, cualquier dispositivo puede inicializar una conexión con el otro y convertirse en el cliente. Opcionalmente, un dispositivo puede alojar de manera explícita la conexión y abrir un socket de servidor a pedido, y el otro dispositivo inicializa la conexión.

Figura 3: Cuadro de diálogo de sincronización de Bluetooth

Nota: Si los dos dispositivos no se sincronizan previamente, el marco de trabajo de Android muestra automáticamente una notificación de solicitud o un cuadro de diálogo de sincronización al usuario durante el procedimiento de conexión, como se muestra en la figura 3. De este modo, cuando tu aplicación intente conectar los dispositivos, no necesitará identificar si los dispositivos están sincronizados o no. El intento de conexión mediante RFCOMM se bloquea hasta que el usuario sincroniza correctamente ambos dispositivos, y no funciona si el usuario rechaza la sincronización, si el proceso falla o si se agota el tiempo de espera.

Conectarse como servidor

Cuando desees conectar dos dispositivos, uno deberá funcionar como servidor y tener un BluetoothServerSocket abierto. El propósito del socket de servidor es recibir solicitudes de conexiones entrantes y proporcionar un BluetoothSocket conectado cuando se acepta una. Cuando el BluetoothSocket se obtiene del BluetoothServerSocket, el BluetoothServerSocket puede (y debería) descartarse, a menos que quieras que el dispositivo acepte más conexiones.

Para configurar un socket de servidor y aceptar una conexión, sigue estos pasos:

  1. Obtén un BluetoothServerSocket llamando a listenUsingRfcommWithServiceRecord().

    La string es un nombre de identificación de tu servicio, que el sistema escribirá automáticamente en una nueva entrada de la base de datos del protocolo de detección de servicios (SDP) en el dispositivo. El nombre es arbitrario y puede ser simplemente el de tu aplicación. El identificador único universal (UUID) también se incluye en la entrada del SDP y conforma la base del acuerdo de conexión con el dispositivo del cliente. Es decir, cuando el cliente intenta conectarse con este dispositivo, incluye un UUID que identifica de manera única el servicio con el cual desea conectarse. Estos UUID deben coincidir a fin de que se acepte la conexión.

    Un UUID es un ID de string con un formato estandarizado de 128 bits que se emplea para identificar información de manera exclusiva. La ventaja de un UUID es que tiene el tamaño suficiente para que puedas seleccionar cualquiera al azar y que no haya conflictos. En este caso, se usa para identificar de manera única el servicio Bluetooth de tu aplicación. A fin de obtener un UUID que pueda usarse con tu aplicación, puedes emplear uno de los numerosos generadores de UUID al azar de la Web e inicializar un UUID con fromString(String).

  2. Llama a accept() para comenzar a recibir solicitudes de conexión.

    Esta es una llamada de bloqueo. Se muestra cuando se acepta una conexión o se produce una excepción. Una conexión se acepta solo cuando un dispositivo remoto envía una solicitud de conexión que contiene un UUID que coincide con el UUID registrado con este socket de servidor de recepción. Si la conexión se realiza correctamente, accept() devuelve un BluetoothSocket conectado.

  3. A menos que desees aceptar conexiones adicionales, llama a close().

    Este método libera el socket de servidor y todos sus recursos, pero no cierra el BluetoothSocket conectado que accept() devuelve. A diferencia del protocolo TCP/IP, RFCOMM solo permite un cliente conectado por canal a la vez, por lo que, en la mayoría de los casos, tiene sentido llamar a close() en el BluetoothServerSocket inmediatamente después de aceptar un socket conectado.

Debido a que la llamada accept() es de bloqueo, no debe ejecutarse en el subproceso de la IU de la actividad principal, de modo que tu aplicación pueda seguir respondiendo a otras interacciones de los usuarios. Por lo general, resulta lógico hacer todo el trabajo que involucra BluetoothServerSocket o BluetoothSocket en un nuevo subproceso administrado por tu aplicación. Para anular una llamada bloqueada, como accept(), llama a close() en el BluetoothServerSocket o BluetoothSocket desde otro subproceso. Ten en cuenta que todos los métodos en un BluetoothServerSocket o BluetoothSocket tienen seguridad para subprocesos.

Ejemplo

A continuación, se describe un subproceso simplificado para el componente de servidor que acepta conexiones entrantes:

Kotlin

private inner class AcceptThread : Thread() {
    
    private val mmServerSocket: BluetoothServerSocket? by lazy(LazyThreadSafetyMode.NONE) {
        bluetoothAdapter?.listenUsingInsecureRfcommWithServiceRecord(NAME, MY_UUID)
    }

    override fun run() {
        // Keep listening until exception occurs or a socket is returned.
        var shouldLoop = true
        while (shouldLoop) {
            val socket: BluetoothSocket? = try {
                mmServerSocket?.accept()
            } catch (e: IOException) {
                Log.e(TAG, "Socket's accept() method failed", e)
                shouldLoop = false
                null
            }
            socket?.also {
                manageMyConnectedSocket(it)
                mmServerSocket?.close()
                shouldLoop = false
            }
        }
    }

    // Closes the connect socket and causes the thread to finish.
    fun cancel() {
        try {
            mmServerSocket?.close()
        } catch (e: IOException) {
            Log.e(TAG, "Could not close the connect socket", e)
        }
    }
}

Java

private class AcceptThread extends Thread {
    private final BluetoothServerSocket mmServerSocket;

    public AcceptThread() {
        // Use a temporary object that is later assigned to mmServerSocket
        // because mmServerSocket is final.
        BluetoothServerSocket tmp = null;
        try {
            // MY_UUID is the app's UUID string, also used by the client code.
            tmp = bluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        } catch (IOException e) {
            Log.e(TAG, "Socket's listen() method failed", e);
        }
        mmServerSocket = tmp;
    }

    public void run() {
        BluetoothSocket socket = null;
        // Keep listening until exception occurs or a socket is returned.
        while (true) {
            try {
                socket = mmServerSocket.accept();
            } catch (IOException e) {
                Log.e(TAG, "Socket's accept() method failed", e);
                break;
            }

            if (socket != null) {
                // A connection was accepted. Perform work associated with
                // the connection in a separate thread.
                manageMyConnectedSocket(socket);
                mmServerSocket.close();
                break;
            }
        }
    }

    // Closes the connect socket and causes the thread to finish.
    public void cancel() {
        try {
            mmServerSocket.close();
        } catch (IOException e) {
            Log.e(TAG, "Could not close the connect socket", e);
        }
    }
}

En este ejemplo, solo se desea una conexión entrante; por lo tanto, apenas se acepta una conexión y se obtiene el BluetoothSocket, la aplicación pasa el BluetoothSocket adquirido a un subproceso separado, cierra el BluetoothServerSocket y abandona el bucle.

Ten presente que, cuando accept() devuelve el BluetoothSocket, el socket ya está conectado. Por lo tanto, no debes llamar a connect(), como lo haces del lado del cliente.

El método manageMyConnectedSocket() específico de la aplicación está diseñado para inicializar el subproceso de transferencia de datos, que se analiza en la sección Cómo administrar una conexión.

Por lo general, deberás cerrar tu BluetoothServerSocket no bien termines de recibir conexiones entrantes. En este ejemplo, se llama a close() apenas se obtiene el BluetoothSocket. Es posible que también desees proporcionar en tu subproceso un método público que pueda cerrar el BluetoothSocket en caso de que necesites dejar de recibir conexiones en ese socket de servidor.

Conectarse como cliente

Para inicializar una conexión con el dispositivo remoto que acepta conexiones en un socket de servidor abierto, primero debes obtener un objeto BluetoothDevice que represente al dispositivo remoto. Para obtener información sobre cómo crear un BluetoothDevice, consulta Cómo buscar dispositivos. Luego, debes usar el BluetoothDevice para obtener un BluetoothSocket e inicializar la conexión.

El procedimiento básico es el siguiente:

  1. Usando el BluetoothDevice, obtén un BluetoothSocket llamando a createRfcommSocketToServiceRecord(UUID).

    Este método inicializa un objeto BluetoothSocket que le permite al cliente conectarse con un BluetoothDevice. El UUID que se pasa aquí debe coincidir con el UUID empleado por el dispositivo del servidor cuando hizo la llamada a listenUsingRfcommWithServiceRecord(String, UUID) para abrir su BluetoothServerSocket. Para usar un UUID que coincida, codifica la cadena del UUID en tu aplicación y, luego, haz referencia a ella en el código del servidor y el cliente.

  2. Llama a connect() para inicializar la conexión. Ten presente que este método es una llamada de bloqueo.

    Después de que el cliente llama a este método, el sistema realiza una búsqueda de SDP para encontrar un dispositivo remoto que tenga el mismo UUID. Si la búsqueda es correcta y el dispositivo remoto acepta la conexión, este último comparte el canal RFCOMM que se usará durante la conexión, y se devuelve el método connect(). Si la conexión no funciona o si se agota el tiempo de espera del método connect() (después de unos 12 segundos), dicho método devuelve una IOException.

    Debido a que connect() es una llamada de bloqueo, siempre debes realizar este procedimiento de conexión en un subproceso diferente del subproceso de la IU de la actividad principal.

    Nota: Llama siempre a cancelDiscovery() para asegurarte de que no esté realizando la detección de dispositivos antes de que realices la llamada a connect(). Si la detección se encuentra en curso, el intento de conexión se ralentiza considerablemente y es más probable que falle.

Ejemplo

A continuación, se ofrece un ejemplo básico de un subproceso de cliente que inicializa una conexión Bluetooth:

Kotlin

private inner class ConnectThread(device: BluetoothDevice) : Thread() {

    private val mmSocket: BluetoothSocket? by lazy(LazyThreadSafetyMode.NONE) {
        device.createRfcommSocketToServiceRecord(MY_UUID)
    }

    public override fun run() {
        // Cancel discovery because it otherwise slows down the connection.
        bluetoothAdapter?.cancelDiscovery()

        mmSocket?.use { socket ->
            // Connect to the remote device through the socket. This call blocks
            // until it succeeds or throws an exception.
            socket.connect()

            // The connection attempt succeeded. Perform work associated with
            // the connection in a separate thread.
            manageMyConnectedSocket(socket)
        }
    }

    // Closes the client socket and causes the thread to finish.
    fun cancel() {
        try {
            mmSocket?.close()
        } catch (e: IOException) {
            Log.e(TAG, "Could not close the client socket", e)
        }
    }
}

Java

private class ConnectThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final BluetoothDevice mmDevice;

    public ConnectThread(BluetoothDevice device) {
        // Use a temporary object that is later assigned to mmSocket
        // because mmSocket is final.
        BluetoothSocket tmp = null;
        mmDevice = device;

        try {
            // Get a BluetoothSocket to connect with the given BluetoothDevice.
            // MY_UUID is the app's UUID string, also used in the server code.
            tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
        } catch (IOException e) {
            Log.e(TAG, "Socket's create() method failed", e);
        }
        mmSocket = tmp;
    }

    public void run() {
        // Cancel discovery because it otherwise slows down the connection.
        bluetoothAdapter.cancelDiscovery();

        try {
            // Connect to the remote device through the socket. This call blocks
            // until it succeeds or throws an exception.
            mmSocket.connect();
        } catch (IOException connectException) {
            // Unable to connect; close the socket and return.
            try {
                mmSocket.close();
            } catch (IOException closeException) {
                Log.e(TAG, "Could not close the client socket", closeException);
            }
            return;
        }

        // The connection attempt succeeded. Perform work associated with
        // the connection in a separate thread.
        manageMyConnectedSocket(mmSocket);
    }

    // Closes the client socket and causes the thread to finish.
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) {
            Log.e(TAG, "Could not close the client socket", e);
        }
    }
}

En este fragmento, observa cómo se llama a cancelDiscovery() antes del intento de conexión. Siempre debes llamar a cancelDiscovery() antes de connect(), especialmente porque cancelDiscovery() funciona de manera correcta, independientemente de que haya o no una detección de dispositivos en curso. Sin embargo, en caso de que tu aplicación necesite determinar si hay una detección en curso, puedes comprobar esto mediante isDiscovering().

El método manageMyConnectedSocket() específico de la aplicación está diseñado para inicializar el subproceso de transferencia de datos, que se analiza en la sección Cómo administrar una conexión.

Cuando termines con BluetoothSocket, siempre llama a close(). Al hacerlo, se cierra de inmediato el socket conectado y se liberan todos los recursos internos relacionados.

Administrar una conexión

Después de conectar correctamente varios dispositivos, cada uno tiene un BluetoothSocket conectado. Aquí es donde comienza la diversión, ya que puedes compartir información entre dispositivos. Mediante el BluetoothSocket, el procedimiento general para transferir datos es el siguiente:

  1. Obtén los objetos InputStream y OutputStream que administran transmisiones a través del socket mediante getInputStream() y getOutputStream(), respectivamente.
  2. Lee y escribe datos en las emisiones con read(byte[]) y write(byte[]).

Por supuesto, existen detalles relacionados con la implementación que deben tenerse en cuenta. En especial, debes usar un subproceso dedicado para leer de la emisión y escribir en ella. Esto es importante porque los métodos read(byte[]) y write(byte[]) son llamadas de bloqueo. El método read(byte[]) mantiene el bloqueo hasta que haya datos para leer en la emisión. El método write(byte[]) no suele realizar un bloqueo, pero puede hacerlo para controlar el flujo si el dispositivo remoto no está llamando a read(byte[]) con la velocidad suficiente y, en consecuencia, se llenan los búferes intermedios. Por eso, el bucle principal del subproceso debe dedicarse exclusivamente a leer de InputStream. Se puede usar un método público por separado en el subproceso a fin de inicializar escrituras en OutputStream.

Ejemplo

Este es un ejemplo de cómo puedes transferir datos entre dos dispositivos conectados mediante Bluetooth:

Kotlin

private const val TAG = "MY_APP_DEBUG_TAG"

// Defines several constants used when transmitting messages between the
// service and the UI.
const val MESSAGE_READ: Int = 0
const val MESSAGE_WRITE: Int = 1
const val MESSAGE_TOAST: Int = 2
// ... (Add other message types here as needed.)

class MyBluetoothService(
        // handler that gets info from Bluetooth service
        private val handler: Handler) {

    private inner class ConnectedThread(private val mmSocket: BluetoothSocket) : Thread() {

        private val mmInStream: InputStream = mmSocket.inputStream
        private val mmOutStream: OutputStream = mmSocket.outputStream
        private val mmBuffer: ByteArray = ByteArray(1024) // mmBuffer store for the stream

        override fun run() {
            var numBytes: Int // bytes returned from read()

            // Keep listening to the InputStream until an exception occurs.
            while (true) {
                // Read from the InputStream.
                numBytes = try {
                    mmInStream.read(mmBuffer)
                } catch (e: IOException) {
                    Log.d(TAG, "Input stream was disconnected", e)
                    break
                }

                // Send the obtained bytes to the UI activity.
                val readMsg = handler.obtainMessage(
                        MESSAGE_READ, numBytes, -1,
                        mmBuffer)
                readMsg.sendToTarget()
            }
        }

        // Call this from the main activity to send data to the remote device.
        fun write(bytes: ByteArray) {
            try {
                mmOutStream.write(bytes)
            } catch (e: IOException) {
                Log.e(TAG, "Error occurred when sending data", e)

                // Send a failure message back to the activity.
                val writeErrorMsg = handler.obtainMessage(MESSAGE_TOAST)
                val bundle = Bundle().apply {
                    putString("toast", "Couldn't send data to the other device")
                }
                writeErrorMsg.data = bundle
                handler.sendMessage(writeErrorMsg)
                return
            }

            // Share the sent message with the UI activity.
            val writtenMsg = handler.obtainMessage(
                    MESSAGE_WRITE, -1, -1, mmBuffer)
            writtenMsg.sendToTarget()
        }

        // Call this method from the main activity to shut down the connection.
        fun cancel() {
            try {
                mmSocket.close()
            } catch (e: IOException) {
                Log.e(TAG, "Could not close the connect socket", e)
            }
        }
    }
}

Java

public class MyBluetoothService {
    private static final String TAG = "MY_APP_DEBUG_TAG";
    private Handler handler; // handler that gets info from Bluetooth service

    // Defines several constants used when transmitting messages between the
    // service and the UI.
    private interface MessageConstants {
        public static final int MESSAGE_READ = 0;
        public static final int MESSAGE_WRITE = 1;
        public static final int MESSAGE_TOAST = 2;

        // ... (Add other message types here as needed.)
    }

    private class ConnectedThread extends Thread {
        private final BluetoothSocket mmSocket;
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;
        private byte[] mmBuffer; // mmBuffer store for the stream

        public ConnectedThread(BluetoothSocket socket) {
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;

            // Get the input and output streams; using temp objects because
            // member streams are final.
            try {
                tmpIn = socket.getInputStream();
            } catch (IOException e) {
                Log.e(TAG, "Error occurred when creating input stream", e);
            }
            try {
                tmpOut = socket.getOutputStream();
            } catch (IOException e) {
                Log.e(TAG, "Error occurred when creating output stream", e);
            }

            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }

        public void run() {
            mmBuffer = new byte[1024];
            int numBytes; // bytes returned from read()

            // Keep listening to the InputStream until an exception occurs.
            while (true) {
                try {
                    // Read from the InputStream.
                    numBytes = mmInStream.read(mmBuffer);
                    // Send the obtained bytes to the UI activity.
                    Message readMsg = handler.obtainMessage(
                            MessageConstants.MESSAGE_READ, numBytes, -1,
                            mmBuffer);
                    readMsg.sendToTarget();
                } catch (IOException e) {
                    Log.d(TAG, "Input stream was disconnected", e);
                    break;
                }
            }
        }

        // Call this from the main activity to send data to the remote device.
        public void write(byte[] bytes) {
            try {
                mmOutStream.write(bytes);

                // Share the sent message with the UI activity.
                Message writtenMsg = handler.obtainMessage(
                        MessageConstants.MESSAGE_WRITE, -1, -1, mmBuffer);
                writtenMsg.sendToTarget();
            } catch (IOException e) {
                Log.e(TAG, "Error occurred when sending data", e);

                // Send a failure message back to the activity.
                Message writeErrorMsg =
                        handler.obtainMessage(MessageConstants.MESSAGE_TOAST);
                Bundle bundle = new Bundle();
                bundle.putString("toast",
                        "Couldn't send data to the other device");
                writeErrorMsg.setData(bundle);
                handler.sendMessage(writeErrorMsg);
            }
        }

        // Call this method from the main activity to shut down the connection.
        public void cancel() {
            try {
                mmSocket.close();
            } catch (IOException e) {
                Log.e(TAG, "Could not close the connect socket", e);
            }
        }
    }
}

Después de que el constructor obtiene los flujos necesarios, el subproceso espera el ingreso de datos mediante InputStream. Cuando read(byte[]) devuelve datos de la emisión, los datos se envían a la actividad principal mediante un Handler de un miembro de la clase primaria. Luego, el subproceso espera a que haya más bytes para leer de InputStream.

El envío de datos salientes simplemente implica llamar al método write() del subproceso desde la actividad principal y pasar los bytes que se enviarán. Este método llama a write(byte[]) para enviar los datos al dispositivo remoto. Si se devuelve una IOException al llamar a write(byte[]), el subproceso envía a la actividad principal un aviso en el que explica que el dispositivo no pudo enviar los bytes correspondientes al otro dispositivo (conectado).

El método cancel() del subproceso permite cerrar BluetoothSocket y finalizar la conexión en cualquier momento. Siempre debes llamar a este método cuando termines de usar la conexión Bluetooth.

Para ver una demostración de uso de las API de Bluetooth, consulta el ejemplo de la aplicación de chat de Bluetooth.

Clases fundamentales e interfaces

Todas las API de Bluetooth están disponibles en el paquete de android.bluetooth. A continuación, se ofrece un resumen de las clases e interfaces que necesitas para crear conexiones Bluetooth:

BluetoothAdapter
Representa el adaptador local de Bluetooth (radio Bluetooth). El BluetoothAdapter es el punto de entrada de toda interacción de Bluetooth. Gracias a esto, puedes ver otros dispositivos Bluetooth, consultar una lista de los dispositivos conectados (sincronizados), crear una instancia de BluetoothDevice mediante una dirección MAC conocida y crear un BluetoothServerSocket para recibir comunicaciones de otros dispositivos.
BluetoothDevice
Representa un dispositivo Bluetooth remoto. Usa esto para solicitar una conexión con un dispositivo remoto mediante un BluetoothSocket o consultar información sobre el dispositivo, como su nombre, dirección, clase y estado de conexión.
BluetoothSocket
Representa la interfaz de un socket de Bluetooth (similar a un Socket de TCP). Este es el punto de conexión que permite que una aplicación intercambie datos con otro dispositivo Bluetooth a través de InputStream y OutputStream.
BluetoothServerSocket
Representa un socket de servidor abierto que recibe solicitudes entrantes (similar a un ServerSocket de TCP). A fin de conectar dos dispositivos Android, un dispositivo debe abrir un socket de servidor con esta clase. Cuando un dispositivo Bluetooth remoto solicita una conexión con este dispositivo, el dispositivo la acepta y devuelve un BluetoothSocket conectado.
BluetoothClass
Describe las características y capacidades generales de un dispositivo Bluetooth. Se trata de un conjunto de propiedades de solo lectura que define las clases y los servicios del dispositivo. Aunque esta información resulta útil para determinar el tipo de dispositivo, los atributos de esta clase no describen necesariamente todos los perfiles y servicios de Bluetooth con los que es compatible el dispositivo.
BluetoothProfile
Interfaz que representa un perfil de Bluetooth. Un perfil de Bluetooth es una especificación de interfaz inalámbrica para la comunicación entre dispositivos basada en Bluetooth. Un ejemplo es el perfil de manos libres. Para obtener más información sobre los perfiles, consulta Trabajar con perfiles.
BluetoothHeadset
Brinda compatibilidad para el uso de auriculares Bluetooth con teléfonos móviles. Esto incluye el perfil de auriculares de Bluetooth y el perfil de manos libres (v1.5).
BluetoothA2dp
Define cómo se emite el nivel de audio de alta calidad de un dispositivo a otro a través de una conexión Bluetooth mediante el perfil de distribución de audio avanzada (A2DP).
BluetoothHealth
Representa un proxy de perfil de dispositivos de salud que controla el servicio Bluetooth.
BluetoothHealthCallback
Clase abstracta que se usa para implementar devoluciones de llamada de BluetoothHealth. Debes extender esta clase e implementar los métodos de devolución de llamada para recibir actualizaciones sobre los cambios en el estado de registro de la aplicación y el estado de canal de Bluetooth.
BluetoothHealthAppConfiguration
Representa una configuración de aplicación que la aplicación de salud de terceros de Bluetooth registra para comunicarse con un dispositivo de salud Bluetooth remoto.
BluetoothProfile.ServiceListener
Interfaz que notifica a los clientes de la comunicación entre procesos (IPC) del BluetoothProfile cuando se conectan al servicio interno que ejecuta un perfil en particular o se desconectan de este.