Detección de dispositivos Bluetooth en Android

Desde hace años es común ver cómo la tecnología Bluetooth está presente en muchos de los gadgets que usamos a diario. Con ella podemos compartir ficheros con amigos, integrar nuestro teléfono en el equipo de audio de nuestro coche o incluso conectar los mandos de nuestras videoconsolas.

Existen usos de esta tecnología menos conocidos como es el caso de actuar como lanzadores de eventos concretos en nuestras aplicaciones. La simple detección de un emisor de Bluetooth del que se conoce su ubicación exacta nos permite saber que nos encontramos próximos a ese punto y por lo tanto enriquecer la experiencia del usuario con contenidos y funcionalidades propias a dicha ubicación.

Uno de los principales problemas de la tecnología Bluetooth es que un uso muy continuado puede ocasionar dejar al usuario prácticamente sin batería en algunos dispositivos. En los últimos años, la aparición del Bluetooth de baja energía, BLE, (Bluetooth Low Energy) y su implementación en gran parte de los modelos lanzados al mercado, ha permitido que el uso del Bluetooth conlleve un menor consumo que el Bluetooth clásico.

Android dispone de dos APIs diferentes una para el Bluetooth clásico y otra para el BLE. En esta entrada veremos cómo se puede usar estas APIs para descubrir los dispositivos Bluetooth cercanos.

Bluetooth clásico

Empecemos por la API del Bluetooth clásico. Necesitaremos añadir dos permisos al manifiesto de la aplicación para poder usar el Bluetooth y descubrir dispositivos que se encuentren a nuestro alcance. En concreto, los permisos son BLUETOOTH y BLUETOOTH_ADMIN.

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

Para obtener la información que el sensor de Bluetooth proporciona necesitamos disponer del adaptador de Bluetooth. La llamada al método estático getDefaultAdapter en la clase BluetoothAdapter devolverá una instancia del mismo, que será nula en caso de que el dispositivo no soporte Bluetooth.

// Se obtiene una instancia de la clase BluetoothAdapter
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
    // Si el adaptador es null significa que el dispositivo no soporta Bluetooth
}

Una vez que estamos seguros de que el dispositivo soporta la tecnología debemos asegurarnos de que el sensor está activo, y en caso contrario solicitar permiso al usuario para habilitarlo. Las siguientes líneas lanzarán un diálogo solicitando habilitar el Bluetooth sólo cuando no está ya habilitado.

if (!mBluetoothAdapter.isEnabled()) {
    // El Bluetooth está apagado, solicitamos permiso al usuario para iniciarlo
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    // REQUEST_ENABLE_BT es un valor entero que vale 1
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

El esquema de funcionamiento será a través de un broadcast receiver que permitirá registrar una serie de acciones a realizar cuando el sistema estén sucediendo ciertos eventos. Para emplear este broadcast receiver tendremos que registrarlo a través de la función registerReceiver disponible en el contexto. Para este caso en concreto, los argumentos que necesitamos pasarle a la función son dos. El primero de ellos es el propio broadcast receiver. El segundo es el intent filter que indica qué tipo de eventos (broadcast intents) lanzarán las acciones de ese receiver. Nosotros lo crearemos con la opción BluetoothDevice.ACTION_FOUND que indica que se ha detectado un dispositivo Bluetooth.

// Se registra el broadcast receiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter);

En esas líneas de código mReceiver será el broadcastReceiver. El broadcast receiver es simplemente una implementación de la clase abstracta de Android BroadcastReceiver en la que a través del método onReceive indicaremos la acción a realizar cuando se ha descubierto el nuevo dispositivo. En él a través de los extras del intent recibido podemos recoger diferente información. Por ejemplo:

  • En BluetoothDevice.EXTRA_DEVICE viene un objeto de tipo BluetoothDevice que contiene información del dispositivo encontrado como su nombre o dirección MAC y también permite establecer una conexión con él.
  • En BluetoothDevice.EXTRA_CLASS se obtiene una instancia de BluetoothClass con la que se puede ver de forma aproximada las características del dispositivo.
  • En BluetoothDevice.EXTRA_RSSI devuelve el indicador de fuerza recibida (RSSI) que permite saber la potencia de la señal recibida. No todos los dispositivos soportan RSSI, por lo que puede que este valor no venga informado.
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            // Se ha encontrado un dispositivo Bluetooth
            // Se obtiene la información del dispositivo del intent
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            Log.i(TAG, "Dispositivo encontrado: " + device.getName() + "; MAC " + device.getAddress());
        }
    }
};

Por último sólo falta iniciar el proceso de rastreo de dispositivos Bluetooth. Para ello el adaptador dispone del método startDiscovery. Puede que cuando se registre el broadcast receiver el Bluetooth ya se encuentre en modo discovery, por lo que podemos detener este proceso con cancelDiscovery e iniciarlo de nuevo para forzar que los dispositivos lleguen al receiver.

if (mBluetoothAdapter.isDiscovering()) {
    // El Bluetooth ya está en modo discover, lo cancelamos para iniciarlo de nuevo
    mBluetoothAdapter.cancelDiscovery();
}
mBluetoothAdapter.startDiscovery();

Bluetooth de baja energía (BLE)

El soporte para BLE está en Android a partir de la versión de API 18 (Android 4.3), por lo que es la mínima versión de API que debemos emplear para hacer poder hacer uso de ella. Hay que tener en cuenta que actualmente muchos de los dispositivos no disponen de esta tecnología, por lo que debemos hacer un control en el código de la aplicación para usar la API anterior en caso de que no podamos hacer uso de BLE. Si añadimos esta línea al manifiesto de la aplicación limitaremos que no se pueda instalar en dispositivos sin el hardware necesario para BLE.

<uses-feature android:name="android.hardware.bluetooth_le" android:required="true"/>

Es posible que la parte de BLE sea opcional en nuestra aplicación por lo que podemos comprobar en tiempo de ejecución el soporte de BLE y en tal caso utilizar las llamadas a esta API.

if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    // El dispositivo soporta BLE, podemos usar su API
} else {
    // El dispositivo no soporta. No usar la API de BLE aquí
}

Los permisos necesarios en el manifest son los mismos que en la API anterior y también se requiere de una instancia del adaptador de Bluetooth. Sin embargo, en este caso el sistema de funcionamiento es diferente ya que no se trata de un broadcast receiver sino de un método de escaneo que tras finalizar invocará una función callback. El método startLeScan del adaptador permite pasar una función de callback en la que se dispone de la información de los dispositivos encontrados. El método stopLeScan detendrá el escaneo. Es importante detenerlo y sólo escanear en intervalos cuando sea necesario, ya que un escaneo continuo puede vaciar la batería del dispositivo. En la función de callback dispondremos de acceso directo a la información que necesitamos a través de los parámetros de la función, sin necesidad de leer un intent como en la API anterior. La información viene en 3 parámetros: una instancia de BluetoothDevice con la información del dispositivo detectado, el valor de RSSI y el contenido del anuncio enviado por el otro dispositivo (scanRecord).

private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
    @Override
     public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) {
        // Se ha encontrado el dispositivo. Podemos obtener la información
        Log.i(TAG, "Dispositivo BLE encontrado: " + device.getName() + "; MAC " + device.getAddress());
    }
};
// Esta callback se añade como parámetro al método de inicio de escaneo de esta forma
// bleAdapter.startLeScan(mLeScanCallback);

Es importante saber que cuando se realiza un escaneo BLE sólo se obtendrá los dispositivos cercanos BLE y no los que disponen sólo de tecnología Bluetooth clásica, para los que habría que usar la API anterior. Además, hay que tener en cuenta que no se puede realizar de forma simultánea un escaneo de dispositivos Bluetooth a través de las dos APIs.

Conclusiones

Como habéis podido comprobar, ambas APIs permiten descubrir de forma sencilla dispositivos cercanos con los que podemos establecer comunicación. La API del Bluetooth clásico es más madura ya que lleva muchos más años es el mercado. Por otro lado, la nueva BLE ofrece abre más posibilidades debido a su menor consumo, pero no es sustitutiva de la anterior ya que necesitaremos la API clásica para comunicarnos con los dispositivos Bluetooth que no sean BLE.

De todos modos, el proceso de búsqueda de dispositivos cercanos es un proceso que puede consumir mucha batería, por lo que es importante usarlas con control y sólo cuando sea necesario, para así maximizar la duración de la batería.

 

Artículos relacionados

Informes en formato PDF en Android

ASO – Posiciona tu app en el App Store o Google Play

Librería Android Priority Job Queue para tus tareas en segundo plano

OCR en Android

2 comentarios en «Detección de dispositivos Bluetooth en Android»

  1. Hola felicitaciones por tu post!! Quería preguntarte, yo consigo conectar con mi móvil Android a un adafruit BLE y me funciona, pero si intento añadir un 2º BLE ya no soy capaz. Ambos BLE les tengo asignados nombres, y yo esperaba que en el callback onLeScan realizar el mismo proceso cuando encuentro el primero, pues hacerlo con el 2º, ¿Hay alguna consideración que me esté perdiendo? Gracias!!

    Responder
    • Hola Guillermo, a simple vista yo también esperaría que cogiera correctamente el segundo dispositivo. Hay gente que con ciertos móviles está teniendo problemas detectando algunos dispositivos BLE. Puedes intentar que el código pare el escaneo en curso y lo reintente a ver si así lo detecte. Suerte!

      Responder

Deja un comentario

¿Necesitas una estimación?

Calcula ahora