Visão geral do Bluetooth

A plataforma Android inclui suporte à pilha de rede Bluetooth, que permite que um dispositivo troque dados sem fio com outros dispositivos Bluetooth. O framework do aplicativo oferece acesso à funcionalidade Bluetooth usando as APIs Bluetooth do Android. Essas APIs permitem a conexão sem fio de aplicativos a outros dispositivos Bluetooth, ativando recursos sem fio ponto a ponto e multiponto.

Com as APIs Bluetooth, um aplicativo Android pode fazer o seguinte:

  • Procurar outros dispositivos Bluetooth
  • Consultar o adaptador Bluetooth local para verificar a existência de dispositivos Bluetooth pareados
  • Estabelecer canais RFCOMM
  • Conectar-se a outros dispositivos por meio da descoberta de serviços
  • Transferir dados de e para outros dispositivos
  • Gerenciar várias conexões

Esta página se concentra no Bluetooth clássico. O Classic Bluetooth é a escolha certa para operações com maior consumo de bateria, como streaming e comunicação entre dispositivos Android. Para dispositivos Bluetooth com baixos requisitos de energia, o Android 4.3 (API de nível 18) introduz compatibilidade com a API para Bluetooth Low Energy. Para saber mais, consulte Bluetooth de baixa energia.

Este documento descreve diferentes perfis de Bluetooth, incluindo o perfil do dispositivo de saúde. Em seguida, explica como usar as APIs Bluetooth do Android para realizar as quatro principais tarefas necessárias para se comunicar usando Bluetooth: configurar o Bluetooth, encontrar dispositivos pareados ou disponíveis na área local, conectar dispositivos e transferir dados entre dispositivos.

Noções básicas

Para que dispositivos com Bluetooth transmitam dados entre si, eles precisam primeiro formar um canal de comunicação usando um processo de pareamento. Um dispositivo, o dispositivo detectável, fica disponível para solicitações de conexão recebidas. Outro dispositivo encontra o dispositivo detectável usando um processo de descoberta de serviço. Depois que o dispositivo detectável aceita a solicitação de pareamento, os dois dispositivos concluem um processo de vinculação, em que trocam as chaves de segurança. Eles armazenam essas chaves em cache para uso posterior. Após a conclusão dos processos de pareamento e vinculação, os dois dispositivos trocam informações. Quando a sessão é concluída, o dispositivo que iniciou a solicitação de pareamento libera o canal que o vinculou ao dispositivo detectável. No entanto, os dois dispositivos permanecem vinculados para que possam se reconectar automaticamente durante uma sessão futura, desde que estejam ao alcance um do outro, e nenhum dos dispositivos tenha removido o vínculo.

Permissões do Bluetooth

Para usar recursos do Bluetooth no seu aplicativo, é necessário declarar duas permissões. A primeira delas é BLUETOOTH. Você precisa dessa permissão para realizar qualquer comunicação Bluetooth, como solicitar ou aceitar uma conexão e transferir dados.

A outra permissão que você precisa declarar é ACCESS_FINE_LOCATION. Seu app precisa dessa permissão porque uma busca por Bluetooth pode ser usada para coletar informações sobre a localização do usuário. Essas informações podem vir dos dispositivos do usuário e de sensores Bluetooth em uso em locais como lojas e instalações de transporte público.

Os serviços executados no Android 10 e versões mais recentes não podem detectar dispositivos Bluetooth, a menos que tenham a permissão ACCESS_BACKGROUND_LOCATION. Para mais informações sobre esse requisito, consulte Acessar a localização em segundo plano.

O snippet de código a seguir mostra como verificar a permissão.

Kotlin

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    if (ContextCompat.checkSelfPermission(baseContext,
        Manifest.permission.ACCESS_BACKGROUND_LOCATION)
        != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
                    PERMISSION_CODE)
        }
}

Java

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
  if (ContextCompat.checkSelfPermission(baseContext,
      Manifest.permission.ACCESS_BACKGROUND_LOCATION)
      != PackageManager.PERMISSION_GRANTED) {
          ActivityCompat.requestPermissions(
              MyActivity.this,
              new String[]{Manifest.permission.ACCESS_BACKGROUND_LOCATION},
                  PERMISSION_CODE)
      }
}

Uma exceção a esse requisito de permissão é quando o app é instalado em um dispositivo com o Android 11 ou versões mais recentes e usa o pareamento de dispositivo complementar para associar um dispositivo. Nesse caso, depois que um dispositivo é associado, os apps podem buscar os dispositivos Bluetooth associados sem precisar de uma permissão de localização.

Em dispositivos com o Android 8.0 (nível 26 da API) e versões mais recentes, você pode usar o CompanionDeviceManager para realizar uma verificação de dispositivos complementares próximos em nome do app sem precisar da permissão de localização. Para saber mais sobre essa opção, consulte Pareamento de dispositivo complementar.

Observação : caso seu app seja destinado ao Android 9 (API de nível 28) ou versões anteriores, é possível declarar a permissão ACCESS_COARSE_LOCATION em vez da permissão ACCESS_FINE_LOCATION.

Se você quiser que seu app inicie a descoberta de dispositivos ou manipule as configurações do Bluetooth, declare a permissão BLUETOOTH_ADMIN, além da permissão BLUETOOTH. A maioria dos aplicativos precisa dessa permissão apenas para descobrir dispositivos Bluetooth locais. Os outros recursos concedidos por essa permissão não podem ser usados, a menos que o aplicativo seja um "gerenciador de energia" que modifique as configurações do Bluetooth mediante solicitação do usuário.

Declare as permissões do Bluetooth no arquivo de manifesto do aplicativo. Por exemplo:

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

  <!-- If your app targets Android 9 or lower, you can declare
       ACCESS_COARSE_LOCATION instead. -->
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  ...
</manifest>

Consulte a referência <uses-permission> para mais informações sobre como declarar permissões do aplicativo.

Trabalhar com perfis

A partir do Android 3.0, a API Bluetooth inclui suporte para trabalhar com perfis do Bluetooth. Um perfil Bluetooth é uma especificação de interface sem fio para comunicação baseada em Bluetooth entre dispositivos. Um exemplo é o perfil Hands-Free. Para que um smartphone se conecte a um fone de ouvido sem fio, os dois dispositivos precisam ser compatíveis com o perfil Hands-Free.

A API Android Bluetooth oferece implementações para os seguintes perfis Bluetooth:

  • Fone de ouvido. O perfil Headset oferece suporte para fones de ouvido Bluetooth serem usados com smartphones. O Android oferece a classe BluetoothHeadset, que é um proxy para controlar o Bluetooth Headset Service. Isso inclui os perfis Bluetooth Headset e Hands-Free (v1.5). A classe BluetoothHeadset inclui suporte para comandos AT. Para mais discussão sobre esse tópico, consulte Comandos AT específicos de fornecedores
  • A2DP: O perfil Advanced Audio Distribution Profile (A2DP) define como fazer streaming de áudio de alta qualidade de um dispositivo para outro por uma conexão Bluetooth. O Android oferece a classe BluetoothA2dp, que é um proxy para controlar o serviço Bluetooth A2DP.
  • Dispositivo de saúde. O Android 4.0 (API de nível 14) introduz o suporte ao perfil Health Device (HDP) do Bluetooth. Isso permite criar aplicativos que usam o Bluetooth para se comunicar com dispositivos de saúde compatíveis com esse dispositivo, como monitores de frequência cardíaca, medidores de sangue, termômetros, balanças e assim por diante. Para conferir uma lista de dispositivos compatíveis e os códigos de especialização de dados do dispositivo correspondentes, consulte as Especializações de dados do dispositivo HDP do Bluetooth. Esses valores também são referenciados na especificação ISO/IEEE 11073-20601 [7] como MDC_DEV_SPEC_PROFILE_* no Anexo de Códigos de Nomenclatura. Para mais informações sobre o HDP, consulte Perfil do dispositivo de saúde.

Veja a seguir as etapas básicas para trabalhar com um perfil:

  1. Acesse o adaptador padrão, conforme descrito em Configurar o Bluetooth.
  2. Configure um BluetoothProfile.ServiceListener. Esse listener notifica os clientes BluetoothProfile quando eles são conectados ou desconectados do serviço.
  3. Use getProfileProxy() para estabelecer uma conexão com o objeto de proxy de perfil associado ao perfil. No exemplo abaixo, o objeto de proxy de perfil é uma instância de BluetoothHeadset.
  4. Em onServiceConnected(), receba um handle para o objeto de proxy de perfil.
  5. Quando você tiver o objeto de proxy de perfil, poderá usá-lo para monitorar o estado da conexão e executar outras operações relevantes para esse perfil.

Por exemplo, o snippet de código abaixo mostra como se conectar a um objeto de proxy BluetoothHeadset para que você possa controlar o perfil Headset:

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 AT específicos de fornecedores

A partir do Android 3.0 (API de nível 11), os aplicativos podem se registrar para receber transmissões do sistema de comandos AT predefinidos específicos do fornecedor enviados por fones de ouvido (como um comando +XEVENT da Plantronics). Por exemplo, um aplicativo pode receber transmissões que indicam o nível de bateria de um dispositivo conectado, além de notificar o usuário ou executar outra ação necessária. Crie um broadcast receiver para a intent ACTION_VENDOR_SPECIFIC_HEADSET_EVENT para processar comandos AT específicos do fornecedor para o fone de ouvido.

Perfil Health Device

O Android 4.0 (nível 14 da API) introduz o suporte ao Perfil de dispositivo Saúde (HDP) Bluetooth. Isso permite criar aplicativos que usam o Bluetooth para se comunicar com dispositivos de saúde compatíveis com Bluetooth, como monitores de frequência cardíaca, medidores de sangue, termômetros e balanças. A API Bluetooth Health inclui as classes BluetoothHealth, BluetoothHealthCallback e BluetoothHealthAppConfiguration, que são descritas em Classes principais e interfaces.

Ao usar a API Bluetooth Health, é útil entender estes conceitos-chave do HDP:

Source
Um dispositivo de saúde, como uma balança, um medidor de glicose ou um termômetro, que transmite dados médicos para um dispositivo inteligente, como um smartphone ou tablet Android.
Coletor
O dispositivo inteligente que recebe dados médicos. Em um aplicativo HDP para Android, o coletor é representado por um objeto BluetoothHealthAppConfiguration.
Registro
O processo usado para registrar um coletor para se comunicar com um dispositivo de saúde específico.
Conexão
O processo usado para abrir um canal entre um dispositivo de saúde (fonte) e um dispositivo inteligente (coletor).

Criar um aplicativo HDP

Veja a seguir as etapas básicas envolvidas na criação de um aplicativo HDP do Android:

  1. Consiga uma referência para o objeto de proxy BluetoothHealth.

    Assim como nos dispositivos com perfil A2DP e fone de ouvido normais, é necessário chamar getProfileProxy() com um BluetoothProfile.ServiceListener e o tipo de perfil HEALTH para estabelecer uma conexão com o objeto de proxy de perfil.

  2. Crie um BluetoothHealthCallback e registre uma configuração de aplicativo (BluetoothHealthAppConfiguration) que funciona como um coletor de integridade.
  3. Estabeleça uma conexão a um dispositivo de saúde.

    Observação : alguns dispositivos iniciam a conexão automaticamente. Não é necessário executar esta etapa para esses dispositivos.

  4. Quando conectado a um dispositivo de saúde, leia/grave no dispositivo de saúde usando o descritor de arquivos. Os dados recebidos precisam ser interpretados usando um gerenciador de integridade que implementa as especificações IEEE 11073.
  5. Quando terminar, feche o canal de saúde e cancele a inscrição do aplicativo. O canal também será fechado quando houver um longo período de inatividade.

Configurar o Bluetooth

Antes que seu aplicativo possa se comunicar por Bluetooth, você precisa verificar se o dispositivo tem suporte para o Bluetooth e, em caso afirmativo, conferir se ele está ativado.

Se não houver suporte para o Bluetooth, desative todos os recursos do Bluetooth. Se houver suporte para o Bluetooth, mas ele estiver desativado, você poderá solicitar que o usuário ative o Bluetooth sem sair do aplicativo. Essa configuração é realizada em duas etapas, usando o BluetoothAdapter:

  1. Acesse o BluetoothAdapter.

    O BluetoothAdapter é necessário para toda e qualquer atividade do Bluetooth. Para receber o BluetoothAdapter, chame o método estático getDefaultAdapter(). Isso retorna um BluetoothAdapter que representa o próprio adaptador Bluetooth do dispositivo (o rádio Bluetooth). Há um adaptador Bluetooth para todo o sistema, e o aplicativo pode usar esse objeto para interagir com ele. Se getDefaultAdapter() retornar null, o dispositivo não oferece suporte a Bluetooth. Por exemplo:

    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. Ative o Bluetooth.

    Em seguida, é necessário garantir a ativação do Bluetooth. Chame isEnabled() para verificar se o Bluetooth está ativado no momento. Se método retornar “false”, o Bluetooth está desativado. Para solicitar que o Bluetooth seja ativado, chame startActivityForResult() transmitindo uma ação da intent ACTION_REQUEST_ENABLE. Essa chamada emite uma solicitação para ativar o Bluetooth pelas configurações do sistema (sem interromper seu app). Por exemplo:

    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);
    }
    

    Uma caixa de diálogo será exibida solicitando a permissão do usuário para ativar o Bluetooth, conforme mostrado na Figura 1. Se o usuário responder "Yes", o sistema começará a ativar o Bluetooth e o foco retornará ao aplicativo quando o processo for concluído (ou falhar).

    Figura 1. Caixa de diálogo de ativação do Bluetooth.

    A constante REQUEST_ENABLE_BT transmitida para startActivityForResult() é um número inteiro definido localmente que precisa ser maior que 0. O sistema transmite essa constante de volta para você na implementação de onActivityResult() como o parâmetro requestCode.

    Se a ativação do Bluetooth for bem-sucedida, sua atividade vai receber o código de resultado RESULT_OK no callback onActivityResult(). Se o Bluetooth não foi ativado devido a um erro (ou o usuário respondeu "Não"), o código de resultado será RESULT_CANCELED.

Opcionalmente, seu aplicativo também pode detectar a intent de transmissão ACTION_STATE_CHANGED, que o sistema transmite sempre que o estado do Bluetooth muda. Essa transmissão contém os campos extras EXTRA_STATE e EXTRA_PREVIOUS_STATE, com os estados novo e antigo do Bluetooth, respectivamente. Os valores possíveis para esses campos extras são STATE_TURNING_ON, STATE_ON, STATE_TURNING_OFF e STATE_OFF. Ouvir essa transmissão pode ser útil se o app precisar detectar mudanças feitas no estado do Bluetooth durante a execução.

Dica:a ativação da detecção do dispositivo ativa automaticamente o Bluetooth. Se você planeja ativar consistentemente a detecção do dispositivo antes de executar a atividade do Bluetooth, pule a etapa 2 acima. Para mais informações, leia a seção sobre como ativar a detecção nesta página.

Encontrar dispositivos

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

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. No entanto, um dispositivo Bluetooth próximo responderá a uma solicitação de descoberta somente se estiver aceitando solicitações de informações sendo detectável. Se um dispositivo for 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. Com essas informações, o dispositivo que está executando o processo de descoberta pode optar por iniciar uma conexão com o dispositivo descoberto.

Como dispositivos detectáveis podem revelar informações sobre a localização do usuário, o processo de descoberta de dispositivos requer 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 seu app. Assim, ele não precisa solicitar permissões de localização.

Depois que uma conexão com um dispositivo remoto é feita pela primeira vez, uma solicitação de pareamento é apresentada automaticamente ao usuário. Quando um dispositivo é pareado, as informações básicas dele, como nome, classe e endereço MAC, são salvas e podem ser lidas usando as APIs Bluetooth. Com o endereço MAC conhecido de um dispositivo remoto, é possível iniciar uma conexão com ele a qualquer momento sem executar a descoberta, desde que o dispositivo ainda esteja dentro do alcance.

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

  • Pareamento significa que dois dispositivos estão cientes 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 no momento um canal RFCOMM e podem transmitir dados entre si. A API Android Bluetooth atual exige que os dispositivos estejam pareados antes de estabelecer uma conexão RFCOMM. O pareamento é realizado automaticamente quando você inicia uma conexão criptografada com as APIs Bluetooth.

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

Observação:dispositivos Android não são detectáveis por padrão. Um usuário pode tornar o dispositivo detectável por um tempo limitado usando as configurações do sistema ou um aplicativo pode solicitar que o usuário ative a detecção sem sair do aplicativo. Para mais informações, consulte a seção Ativar detecção nesta página.

Consultar dispositivos pareados

Antes de executar a descoberta de dispositivos, vale a pena consultar o conjunto de dispositivos pareados para ver se o dispositivo desejado já é conhecido. Para fazer isso, chame getBondedDevices(). Isso retorna um conjunto de objetos BluetoothDevice que representam dispositivos pareados. Por exemplo, você pode consultar todos os dispositivos pareados e conseguir o nome e o endereço MAC de cada dispositivo, conforme demonstrado no snippet de código a seguir:

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 que é necessário do objeto BluetoothDevice associado é o endereço MAC, que você extrai chamando getAddress(). Saiba mais sobre como criar uma conexão na seção Conectar dispositivos.

Cuidado:a realização de descobertas de dispositivos consome muitos recursos do adaptador Bluetooth. Depois de encontrar um dispositivo para se conectar, não esqueça de interromper a descoberta com cancelDiscovery() antes de tentar uma conexão. Além disso, não realize a descoberta enquanto estiver conectado a um dispositivo, porque esse processo reduz significativamente a largura de banda disponível para as conexões atuais.

Descobrir dispositivos

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

Para receber informações sobre cada dispositivo descoberto, seu app precisa registrar um BroadcastReceiver para a intent ACTION_FOUND. O sistema transmite essa 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 você pode se registrar para processar a transmissão quando 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, tudo o que é necessário do objeto BluetoothDevice associado é o endereço MAC, que você extrai chamando getAddress(). Saiba mais sobre como criar uma conexão na seção sobre Conectar dispositivos.

Cuidado:a realização de descobertas de dispositivos consome muitos recursos do adaptador Bluetooth. Depois de encontrar um dispositivo para se conectar, não esqueça de interromper a descoberta com cancelDiscovery() antes de tentar uma conexão. Além disso, não realize a descoberta enquanto estiver conectado a um dispositivo, porque esse processo reduz significativamente a largura de banda disponível para as conexões atuais.

Ativar a detecção do dispositivo

Se você quiser tornar o dispositivo local detectável para 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 ter que navegar até o app Configurações, o que interromperia seu próprio app. Por padrão, o dispositivo fica detectável por 120 segundos, ou 2 minutos. Defina uma duração diferente, de até 3.600 segundos (1 hora), adicionando o EXTRA_DISCOVERABLE_DURATION extra.

Cuidado : se você definir o valor do extra EXTRA_DISCOVERABLE_DURATION como 0, o dispositivo vai ficar sempre detectável. Essa configuração não é segura e, portanto, não é recomendada.

O snippet de código a seguir define o dispositivo como detectável durante 300 segundos (5 minutos):

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. Caixa de diálogo de ativação da detecção.

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

Observação:se o Bluetooth não estiver ativado no dispositivo, ele será ativado automaticamente para que o dispositivo seja detectável.

O dispositivo permanecerá silenciosamente no modo detectável pelo tempo especificado. Para receber uma notificação quando o modo detectável 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 o antigo, respectivamente. Veja possíveis valores para cada extra:

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 precisará ativar a detecção do dispositivo. Isso só é necessário quando você quer que o aplicativo hospede um soquete de servidor que aceite conexões de entrada, já que os dispositivos remotos precisam ser capazes de descobrir outros dispositivos antes de iniciar conexões com eles.

Conectar dispositivos

Para criar uma conexão entre dois dispositivos, você precisa implementar os mecanismos do lado do servidor e do cliente, porque um dispositivo precisa abrir um soquete de servidor e o outro precisa iniciar a conexão usando o endereço MAC do dispositivo do servidor. O dispositivo servidor e o dispositivo cliente recebem o BluetoothSocket necessário de maneiras diferentes. O servidor recebe informações do soquete quando uma conexão de entrada é aceita. O cliente fornece informações sobre o soquete quando abre um canal RFCOMM para o servidor.

O servidor e o cliente são considerados conectados entre si quando cada um tem um BluetoothSocket conectado no mesmo canal RFCOMM. Nesse ponto, cada dispositivo pode receber streams de entrada e saída, e a transferência de dados pode começar, o que é discutido na seção Gerenciar uma conexão. Esta seção descreve como iniciar a conexão entre dois dispositivos.

Técnicas de conexão

Uma técnica de implementação é preparar automaticamente cada dispositivo como um servidor para que cada dispositivo tenha um soquete de servidor aberto e detecte conexões. Nesse caso, qualquer um dos dispositivos pode iniciar uma conexão com o outro e se tornar o cliente. Como alternativa, um dispositivo pode hospedar explicitamente a conexão e abrir um soquete de servidor sob demanda, e o outro dispositivo inicia a conexão.

Figura 3. Caixa de diálogo de pareamento do Bluetooth.

Observação:se os dois dispositivos não tiverem sido pareados anteriormente, o framework do Android mostrará automaticamente uma notificação de solicitação de pareamento ou uma caixa de diálogo ao usuário durante o procedimento de conexão, como mostrado na Figura 3. Portanto, quando o aplicativo tenta conectar dispositivos, ele não precisa se preocupar se os dispositivos estão ou não pareados. A tentativa de conexão RFCOMM será bloqueada até que o usuário tenha pareado os dois dispositivos com êxito, e a tentativa falhará se o usuário rejeitar o pareamento ou se o processo de pareamento falhar ou expirar.

Conectar como servidor

Para conectar dois dispositivos, um precisa atuar como servidor, mantendo uma BluetoothServerSocket aberta. O objetivo do soquete do servidor é ouvir solicitações de conexão de entrada e fornecer um BluetoothSocket conectado depois que uma solicitação for aceita. Quando o BluetoothSocket é adquirido do BluetoothServerSocket, o BluetoothServerSocket pode (e precisa) ser descartado, a menos que você queira que o dispositivo aceite mais conexões.

Para configurar um soquete de servidor e aceitar uma conexão, siga estas etapas:

  1. Para receber um BluetoothServerSocket, chame listenUsingRfcommWithServiceRecord().

    A string é um nome identificável do serviço, que o sistema grava automaticamente em uma nova entrada de banco de dados do Service Discovery Protocol (SDP) no dispositivo. O nome é arbitrário e pode ser simplesmente o nome do aplicativo. O identificador universal exclusivo (UUID, na sigla em inglês) também está incluído na entrada SDP e forma a base para o acordo de conexão com o dispositivo cliente. Ou seja, quando o cliente tenta se conectar a esse dispositivo, ele carrega um UUID que identifica exclusivamente o serviço com que quer se conectar. Esses UUIDs precisam ser correspondentes para que a conexão seja aceita.

    Um UUID é um formato padronizado de 128 bits para um ID de string usado para identificar informações de maneira exclusiva. O objetivo de um UUID é que ele é grande o suficiente para que você possa selecionar qualquer ID aleatório e ele não entre em conflito com nenhum outro ID. Nesse caso, ele é usado para identificar exclusivamente o serviço Bluetooth do aplicativo. Para ter um UUID para usar com seu aplicativo, você pode usar um dos muitos geradores de UUID aleatórios na Web e inicializar um UUID com fromString(String).

  2. Comece a detectar solicitações de conexão chamando accept().

    Essa é uma chamada de bloqueio. Ela retorna quando uma conexão é aceita ou ocorre uma exceção. A conexão será aceita somente quando um dispositivo remoto enviar uma solicitação de conexão contendo um UUID correspondente ao registrado nesse soquete do servidor de detecção. Quando bem-sucedido, accept() retorna um BluetoothSocket conectado.

  3. A menos que você queira aceitar outras conexões, chame close().

    Essa chamada de método libera o soquete do servidor e todos os recursos dele, mas não fecha o BluetoothSocket conectado retornado por accept(). Ao contrário do TCP/IP, o RFCOMM permite apenas um cliente conectado por canal de cada vez. Portanto, na maioria dos casos, faz sentido chamar close() no BluetoothServerSocket imediatamente depois de aceitar um soquete conectado.

Como a chamada accept() é de bloqueio, ela não pode ser executada na linha de execução de interface da atividade principal para que o aplicativo ainda possa responder a outras interações do usuário. Geralmente, faz sentido fazer todo o trabalho que envolve um BluetoothServerSocket ou BluetoothSocket em uma nova linha de execução gerenciada pelo aplicativo. Para cancelar uma chamada bloqueada, como accept(), chame close() na BluetoothServerSocket ou BluetoothSocket em outra linha de execução. Todos os métodos em um BluetoothServerSocket ou BluetoothSocket são thread-safe.

Exemplo

Veja uma linha de execução simplificada para o componente do servidor que aceita conexões de entrada:

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);
        }
    }
}

Neste exemplo, apenas uma conexão de entrada é desejada. Portanto, assim que uma conexão for aceita e a BluetoothSocket for adquirida, o app vai transmitir o BluetoothSocket adquirido para uma linha de execução separada, fechar a BluetoothServerSocket e sair do loop.

Quando accept() retornar o BluetoothSocket, o soquete já estará conectado. Portanto, não chame connect(), como no lado do cliente.

O método manageMyConnectedSocket() específico do app foi projetado para iniciar a linha de execução de transferência de dados. Isso é discutido na seção Gerenciar uma conexão.

Normalmente, você precisa fechar o BluetoothServerSocket assim que terminar de detectar as conexões de entrada. Neste exemplo, close() é chamado assim que o BluetoothSocket é adquirido. Também convém fornecer um método público na linha de execução que possa fechar o BluetoothSocket particular caso você precise parar de detectar esse soquete do servidor.

Conectar como cliente

Para iniciar uma conexão com um dispositivo remoto que aceite conexões em um soquete de servidor aberto, primeiro consiga um objeto BluetoothDevice que represente o dispositivo remoto. Para aprender a criar um BluetoothDevice, consulte Encontrar dispositivos. Em seguida, use o BluetoothDevice para adquirir um BluetoothSocket e iniciar a conexão.

Este é o procedimento básico:

  1. Usando o BluetoothDevice, chame createRfcommSocketToServiceRecord(UUID) para gerar um BluetoothSocket.

    Esse método inicializa um objeto BluetoothSocket que permite que o cliente se conecte a um BluetoothDevice. O UUID transmitido aqui precisa corresponder ao UUID usado pelo dispositivo servidor quando chamou listenUsingRfcommWithServiceRecord(String, UUID) para abrir o BluetoothServerSocket. Para usar um UUID correspondente, codifique a string desse mesmo elemento no aplicativo e faça referência a ela no código do servidor e do cliente.

  2. Inicie a conexão chamando connect(). Esse método é uma chamada de bloqueio.

    Depois que um cliente chama esse método, o sistema executa uma pesquisa de SDP para encontrar o dispositivo remoto com o UUID correspondente. Se a pesquisa for bem-sucedida e o dispositivo remoto aceitar a conexão, ele compartilhará o canal RFCOMM para uso durante a conexão e o método connect() será retornado. Se a conexão falhar ou se o método connect() expirar (após cerca de 12 segundos), o método gerará uma IOException.

    Como connect() é uma chamada de bloqueio, sempre execute esse procedimento de conexão em uma linha de execução separada da linha de execução da atividade (interface) principal.

    Observação : sempre chame cancelDiscovery() para garantir que o dispositivo não esteja realizando a descoberta de dispositivos antes de chamar connect(). Se a descoberta estiver em andamento, a tentativa de conexão será significativamente mais lenta e provavelmente falhará.

Exemplo

Veja um exemplo básico de uma linha de execução de cliente que inicia uma conexão 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);
        }
    }
}

Nesse snippet, cancelDiscovery() é chamado antes da tentativa de conexão. Sempre chame cancelDiscovery() antes de connect(), especialmente porque cancelDiscovery() será bem-sucedido, independente de a descoberta do dispositivo estar em andamento. No entanto, caso seu app precise determinar se a descoberta do dispositivo está em andamento, verifique usando isDiscovering().

O método manageMyConnectedSocket() específico do app foi projetado para iniciar a linha de execução de transferência de dados. Isso é discutido na seção Gerenciar uma conexão.

Quando terminar de usar BluetoothSocket, sempre chame close(). Essa ação fecha imediatamente o soquete conectado e libera todos os recursos internos relacionados.

Gerenciar uma conexão

Depois de conectar vários dispositivos, cada um terá um BluetoothSocket conectado. É aí que começa a diversão, porque você pode compartilhar informações entre dispositivos. Usando o BluetoothSocket, o procedimento geral para transferir dados é o seguinte:

  1. Receba o InputStream e o OutputStream que processam transmissões pelo soquete usando getInputStream() e getOutputStream(), respectivamente.
  2. Leia e grave dados nos streams usando read(byte[]) e write(byte[]).

Naturalmente, é necessário considerar detalhes de implementação. Em particular, é necessário usar uma linha de execução dedicada para ler o stream e gravar nele. Isso é importante porque os métodos read(byte[]) e write(byte[]) estão bloqueando chamadas. O método read(byte[]) fica bloqueado até que haja algo para ler no stream. O método write(byte[]) geralmente não é bloqueado, mas pode ser bloqueado para o controle de fluxo se o dispositivo remoto não estiver chamando read(byte[]) com rapidez suficiente e os buffers intermediários ficarem cheios como resultado. Portanto, o loop principal na linha de execução precisa ser dedicado à leitura de InputStream. Um método público separado na linha de execução pode ser usado para iniciar gravações no OutputStream.

Exemplo

Confira um exemplo de como transferir dados entre dois dispositivos conectados por 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, bytes)
            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, bytes);
                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);
            }
        }
    }
}

Depois que o construtor adquire os streams necessários, a linha de execução aguarda a chegada dos dados pelo InputStream. Quando read(byte[]) retorna com dados do fluxo, os dados são enviados para a atividade principal usando um membro Handler da classe mãe. Em seguida, a linha de execução espera que mais bytes sejam lidos do InputStream.

Para enviar dados de saída, basta chamar o método write() da linha de execução da atividade principal e transmitir os bytes a serem enviados. Esse método chama write(byte[]) para enviar os dados ao dispositivo remoto. Se um IOException for gerado ao chamar write(byte[]), a linha de execução enviará um aviso para a atividade principal, explicando ao usuário que o dispositivo não conseguiu enviar os bytes especificados para o outro dispositivo (conectado).

O método cancel() da linha de execução permite que a conexão seja encerrada a qualquer momento ao fechar a BluetoothSocket. Chame esse método sempre que terminar de usar a conexão Bluetooth.

Para uma demonstração do uso das APIs Bluetooth, consulte o app de exemplo Bluetooth Chat (em inglês).

Classes principais e interfaces

Todas as APIs Bluetooth estão disponíveis no pacote android.bluetooth. Veja um resumo das classes e interfaces necessárias para criar conexões Bluetooth:

BluetoothAdapter
Representa o adaptador Bluetooth local (rádio Bluetooth). O BluetoothAdapter é o ponto de entrada para toda a interação com o Bluetooth. Com isso, é possível descobrir outros dispositivos Bluetooth, consultar uma lista de dispositivos vinculados (pareados), instanciar um BluetoothDevice usando um endereço MAC e criar um BluetoothServerSocket para detectar comunicações de outros dispositivos.
BluetoothDevice
Representa um dispositivo Bluetooth remoto. Use-o para solicitar uma conexão com um dispositivo remoto usando um BluetoothSocket ou consultar informações sobre o dispositivo, como nome, endereço, classe e estado de vinculação.
BluetoothSocket
Representa a interface de um soquete Bluetooth (semelhante a um Socket do TCP). Esse é o ponto de conexão que permite que um aplicativo troque dados com outro dispositivo Bluetooth usando InputStream e OutputStream.
BluetoothServerSocket
Representa um soquete de servidor aberto que ouve solicitações recebidas (semelhante a um TCP ServerSocket). Para conectar dois dispositivos Android, um deles precisa abrir um soquete de servidor com essa classe. Quando um dispositivo remoto Bluetooth faz uma solicitação de conexão a esse dispositivo, o dispositivo aceita a conexão e retorna um BluetoothSocket conectado.
BluetoothClass
Descreve as características e recursos gerais de um dispositivo Bluetooth. Esse é um conjunto de propriedades somente leitura que define as classes e os serviços do dispositivo. Embora essas informações forneçam uma dica útil sobre o tipo de um dispositivo, os atributos dessa classe não descrevem necessariamente todos os perfis e serviços Bluetooth com suporte ao dispositivo.
BluetoothProfile
Uma interface que representa um perfil Bluetooth. Um perfil Bluetooth é uma especificação de interface sem fio para comunicação por Bluetooth entre dispositivos. Um exemplo é o perfil Hands-Free. Para saber mais sobre perfis, consulte Como trabalhar com perfis.
BluetoothHeadset
Oferece suporte para fones de ouvido Bluetooth que possam ser usados com smartphones. Inclui os perfis Bluetooth Headset e Hands-Free (v1.5).
BluetoothA2dp
define como fazer streaming de áudio de alta qualidade de um dispositivo para outro por uma conexão Bluetooth usando o Advanced Audio Distribution Profile (A2DP).
BluetoothHealth
representa um proxy do perfil do Health Device que controla o serviço do Bluetooth.
BluetoothHealthCallback
Uma classe abstrata usada para implementar callbacks BluetoothHealth. É necessário estender essa classe e implementar os métodos de callback para receber atualizações sobre mudanças no estado de registro do aplicativo e no estado do canal Bluetooth.
BluetoothHealthAppConfiguration
Representa uma configuração de aplicativo que o aplicativo de saúde do Bluetooth registra para se comunicar com um dispositivo de integridade Bluetooth remoto.
BluetoothProfile.ServiceListener
Uma interface que notifica os clientes de comunicação entre processos (IPC, na sigla em inglês) BluetoothProfile quando eles são conectados ou desconectados do serviço interno que executa um perfil específico.