Cómo buscar dispositivos Bluetooth

Con BluetoothAdapter, puedes encontrar dispositivos Bluetooth remotos mediante la detección de dispositivos o una consulta de la lista de dispositivos vinculados.

Asegúrate de tener los permisos de Bluetooth adecuados y configura tu app para Bluetooth antes de intentar encontrar dispositivos Bluetooth.

La detección de dispositivos es un procedimiento de escaneo que busca dispositivos compatibles con Bluetooth en el área local y solicita información sobre cada uno. A veces, este proceso se denomina descubrimiento, consulta o análisis. Un dispositivo Bluetooth cercano responde a una solicitud de detección solo si actualmente acepta solicitudes de información por ser detectable. Si un dispositivo es detectable, comparte información como el nombre del dispositivo, su clase y su dirección MAC única como respuesta a la solicitud de descubrimiento. Con esta información, el dispositivo que realiza el proceso de descubrimiento puede elegir iniciar una conexión con el dispositivo detectado.

Como los dispositivos detectables pueden revelar información sobre la ubicación del usuario, el proceso de descubrimiento del dispositivo requiere acceso a la ubicación. Si tu app se usa en un dispositivo con Android 8.0 (nivel de API 26) o una versión posterior, procura usar la API del Administrador de dispositivos complementarios en su lugar. Esta API realiza el descubrimiento de dispositivos en nombre de tu app, por lo que no necesita solicitar permisos de ubicación.

Una vez que se establece una conexión con un dispositivo remoto por primera vez, se presenta automáticamente al usuario una solicitud de vinculación. Cuando se vincula un dispositivo, se guarda su información básica (como el nombre, la clase y la dirección MAC) y se puede leer con las APIs 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 aún se encuentre dentro del rango.

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

  • Para estar vinculados, dos dispositivos reconocen la existencia del otro, tienen una clave de vínculo compartida que se puede usar para la autenticación y son capaces de establecer una conexión encriptada entre sí.
  • Que esté conectado significa que los dispositivos comparten un canal RFCOMM y pueden transmitir datos entre sí. Las APIs de Bluetooth actuales requieren que los dispositivos estén vinculados antes de que se pueda establecer una conexión RFCOMM. La vinculación se realiza automáticamente cuando inicias una conexión encriptada con las APIs de Bluetooth.

En las siguientes secciones, se describe cómo encontrar dispositivos vinculados y cómo descubrir dispositivos nuevos con la detección de dispositivos.

Realizar consultas a dispositivos sincronizados

Antes de llevar a cabo el descubrimiento de dispositivos, es conveniente consultar el conjunto de dispositivos vinculados para ver si el dispositivo deseado ya es conocido. Para ello, llama a getBondedDevices(). Se mostrará un conjunto de objetos BluetoothDevice que representan dispositivos vinculados. Por ejemplo, puedes consultar todos los dispositivos vinculados y obtener el nombre y la dirección MAC de cada uno, como se muestra 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 iniciar una conexión con un dispositivo Bluetooth, todo lo que se necesita del objeto BluetoothDevice asociado es la dirección MAC, que recuperas llamando a getAddress(). Puedes obtener más información para crear una conexión en Cómo conectar dispositivos Bluetooth.

Detectar dispositivos

Para comenzar a detectar dispositivos, llama a startDiscovery(). El proceso es asíncrono y muestra un valor booleano que indica si el descubrimiento se inició de forma correcta. Por lo general, el proceso de descubrimiento implica un análisis de consulta de alrededor de 12 segundos, seguido de un escaneo de la página de cada dispositivo encontrado para recuperar su nombre de Bluetooth.

Para recibir información sobre cada dispositivo detectado, tu app debe registrar un BroadcastReceiver para el intent ACTION_FOUND. El sistema emite esta intent para cada dispositivo. El intent contiene los campos adicionales EXTRA_DEVICE y EXTRA_CLASS, que, a su vez, contienen un BluetoothDevice y un BluetoothClass respectivamente. En el siguiente fragmento de código, se muestra cómo puedes registrarte para controlar la transmisió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 iniciar una conexión con un dispositivo Bluetooth, debes llamar a getAddress() en el objeto BluetoothDevice para recuperar la dirección MAC asociada.

Habilitar la visibilidad

Para hacer que el dispositivo local sea detectable para otros dispositivos, llama a startActivityForResult(Intent, int) con el intent ACTION_REQUEST_DISCOVERABLE. Esto emite una solicitud para habilitar el modo detectable del sistema sin tener que navegar a la app de Configuración, lo que detenería tu propia app. De forma predeterminada, el dispositivo se vuelve detectable durante dos minutos. Puedes definir una duración diferente, hasta una hora, agregando el extra EXTRA_DISCOVERABLE_DURATION.

En el siguiente fragmento de código, se configura el dispositivo para que sea detectable durante cinco minutos:

Kotlin

val requestCode = 1;
val discoverableIntent: Intent = Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE).apply {
   putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300)
}
startActivityForResult(discoverableIntent, requestCode)

Java

int requestCode = 1;
Intent discoverableIntent =
       new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivityForResult(discoverableIntent, requestCode);


Figura 2: Diálogo de habilitación de la visibilidad

Aparecerá un diálogo en el que se solicitará permiso al usuario para que el dispositivo sea detectable, como se muestra en la Figura 2. Si el usuario responde "Permitir", el dispositivo se vuelve detectable durante el período especificado. Luego, tu actividad recibe una llamada a la devolución de llamada onActivityResult(), con un código de resultado igual a la duración por la que el dispositivo es detectable. Si el usuario respondió "Rechazar" o si se produjo un error, el código de resultado será RESULT_CANCELED.

El dispositivo se mantendrá silenciosamente en el modo detectable durante el tiempo asignado. Para recibir una notificación cuando cambie el modo detectable, registra un BroadcastReceiver para el intent ACTION_SCAN_MODE_CHANGED. Este intent contiene los campos adicionales EXTRA_SCAN_MODE y EXTRA_PREVIOUS_SCAN_MODE, que proporcionan el modo de análisis nuevo y el antiguo, respectivamente. Los valores posibles para cada extra son los siguientes:

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

Si inicias la conexión a un dispositivo remoto, no es necesario que habilites la visibilidad del dispositivo. Solo es necesario habilitar la visibilidad cuando quieres que tu app aloje un socket de servidor que acepte conexiones entrantes, ya que los dispositivos remotos deben poder descubrir otros dispositivos antes de iniciar conexiones en ellos.