Encontrar dispositivos Bluetooth

Usando o BluetoothAdapter, é possível encontrar dispositivos Bluetooth remotos pela descoberta de dispositivos ou consultando a lista de dispositivos pareados.

Confira se você tem as permissões de Bluetooth adequadas e configure seu app para Bluetooth antes de tentar encontrar dispositivos Bluetooth.

A descoberta de dispositivos é um procedimento de verificação que procura dispositivos com Bluetooth na área local e solicita algumas informações sobre cada um deles. Esse processo às vezes é chamado de descoberta, consulta ou verificação. Um dispositivo Bluetooth por perto só vai responder a uma solicitação de descoberta se estiver aceitando solicitações de informações. Se um dispositivo estiver detectável, ele vai responder à solicitação de descoberta compartilhando algumas informações, como o nome, a classe e o endereço MAC exclusivo do dispositivo. Usando essas informações, o dispositivo que está realizando o processo de descoberta pode escolher iniciar uma conexão com o dispositivo descoberto.

Como os dispositivos detectáveis podem revelar informações sobre a localização do usuário, o processo de detecção de dispositivos exige acesso à localização. Se o app estiver sendo usado em um dispositivo com o Android 8.0 (nível 26 da API) ou mais recente, use a API Companion Device Manager. Essa API realiza a descoberta de dispositivos em nome do app, para que ele não precise solicitar permissões de localização.

Quando uma conexão é estabelecida com um dispositivo remoto pela primeira vez, uma solicitação de pareamento é apresentada automaticamente ao usuário. Quando um dispositivo é pareado, as informações básicas sobre ele, como o nome, a classe e o endereço MAC, são salvas e podem ser lidas usando as APIs do Bluetooth. Usando o endereço MAC conhecido de um dispositivo remoto, uma conexão pode ser iniciada com ele a qualquer momento sem realizar a descoberta, desde que o dispositivo ainda esteja no alcance.

Lembre-se de que há uma diferença entre estar pareado e estar conectado:

  • Estar pareado significa que dois dispositivos sabem da existência um do outro, têm uma chave de link compartilhada que pode ser usada para autenticação e são capazes de estabelecer uma conexão criptografada entre si.
  • Estar conectado significa que os dispositivos compartilham um canal RFCOMM e podem transmitir dados entre si. As APIs atuais do Bluetooth exigem que os dispositivos sejam pareados antes que uma conexão RFCOMM possa ser estabelecida. O pareamento é realizado automaticamente quando você inicia uma conexão criptografada com as APIs Bluetooth.

As seções a seguir descrevem como encontrar dispositivos pareados e como descobrir novos dispositivos usando a descoberta de dispositivos.

Consultar dispositivos pareados

Antes de realizar a descoberta de dispositivos, vale a pena consultar o conjunto de dispositivos emparelhados para saber se o dispositivo desejado já é conhecido. Para fazer isso, chame o método getBondedDevices(). Isso retorna um conjunto de objetos BluetoothDevice que representam dispositivos pareados. Por exemplo, é possível consultar todos os dispositivos pareados e receber o nome e o endereço MAC de cada um deles, conforme demonstrado no snippet de código abaixo:

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 uma conexão com um dispositivo Bluetooth, tudo o que é necessário do objeto BluetoothDevice associado é o endereço MAC, que você recupera chamando getAddress(). Saiba mais sobre como criar uma conexão em Conectar dispositivos Bluetooth.

Descobrir dispositivos

Para começar a descobrir dispositivos, chame startDiscovery(). O processo é assíncrono e retorna um valor booleano indicando se a descoberta foi iniciada. O processo de descoberta geralmente envolve uma verificação de consulta de cerca de 12 segundos, seguida por uma verificação de página de cada dispositivo encontrado para recuperar o nome do Bluetooth.

Para receber informações sobre cada dispositivo descoberto, o app precisa registrar um BroadcastReceiver para a intent ACTION_FOUND. O sistema transmite esse intent para cada dispositivo. A intent contém os campos extras EXTRA_DEVICE e EXTRA_CLASS, que, por sua vez, contêm um BluetoothDevice e um BluetoothClass, respectivamente. O snippet de código abaixo mostra como se registrar para processar a transmissão quando os dispositivos são descobertos:

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 uma conexão com um dispositivo Bluetooth, chame getAddress() no BluetoothDevice para recuperar o endereço MAC associado.

Ativar a detecção do dispositivo

Para tornar o dispositivo local detectável por outros dispositivos, chame startActivityForResult(Intent, int) com a intent ACTION_REQUEST_DISCOVERABLE. Isso emite uma solicitação para ativar o modo detectável do sistema sem precisar navegar até o app Configurações, o que interromperia seu próprio app. Por padrão, o dispositivo fica detectável por dois minutos. É possível definir uma duração diferente, de até cinco minutos, adicionando o extra EXTRA_DISCOVERABLE_DURATION.

O snippet de código abaixo define o dispositivo como detectável por 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. Caixa de diálogo de ativação da capacidade de descoberta.

Uma caixa de diálogo é mostrada, solicitando a permissão do usuário para tornar o dispositivo detectável, conforme mostrado na Figura 2. Se o usuário responder "Permitir", o dispositivo vai ficar detectável pelo período especificado. Sua atividade vai receber uma chamada para o callback onActivityResult(), com o código de resultado igual à duração em que o dispositivo é detectável. Se o usuário responder "Negado" ou se ocorrer um erro, o código de resultado será RESULT_CANCELED.

O dispositivo permanecerá silenciosamente no modo detectável pelo tempo especificado. Para receber notificações quando o modo de descoberta mudar, registre um BroadcastReceiver para a intent ACTION_SCAN_MODE_CHANGED. Essa intent contém os campos extras EXTRA_SCAN_MODE e EXTRA_PREVIOUS_SCAN_MODE, que fornecem o modo de verificação novo e antigo, respectivamente. Os valores possíveis para cada extra são os seguintes:

SCAN_MODE_CONNECTABLE_DISCOVERABLE
O dispositivo está no modo detectável.
SCAN_MODE_CONNECTABLE
O dispositivo não está no modo detectável, mas ainda pode receber conexões.
SCAN_MODE_NONE
O dispositivo não está no modo detectável e não pode receber conexões.

Se você estiver iniciando a conexão com um dispositivo remoto, não será necessário ativar a detecção do dispositivo. Ativar a detectabilidade é necessário apenas quando você quer que o app hospede um socket de servidor que aceite conexões de entrada, já que os dispositivos remotos precisam detectar outros dispositivos antes de iniciar conexões com eles.