A plataforma Android é compatível com a pilha de rede Bluetooth, o que permite a um dispositivo trocar dados em comunicação sem fios com outros dispositivos Bluetooth. A biblioteca do aplicativo oferece acesso à funcionalidade do Bluetooth por meio das APIs Bluetooth do Android. Essas APIs permitem a conexão sem fio de aplicativos a outros dispositivos Bluetooth, permitindo recursos sem fio ponto a ponto e multiponto.
Ao usar as APIs Bluetooth, um aplicativo Android pode executar as seguintes atividades:
- 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 tem como foco o Classic Bluetooth. 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 APIs compatíveis com o Bluetooth de baixa energia. Para saber mais, consulte Bluetooth de baixa energia.
Este documento descreve diferentes perfis de Bluetooth, inclusive o Perfil Health Device. Este documento explica como usar a API Bluetooth do Android para executar as quatro principais tarefas necessárias para a comunicação usando Bluetooth: configurar o Bluetooth, encontrar dispositivos pareados ou disponíveis na área local, conectar dispositivos e transferir dados entre dispositivos.
Conceitos básicos
Para que dispositivos com Bluetooth ativado transmitam dados entre si, eles precisam primeiro formar um canal de comunicação por meio de um processo de pareamento. Um dispositivo detectável fica disponível para futuras solicitações de conexão. Outro dispositivo encontra o dispositivo detectável usando um processo de descoberta de serviços. Após o dispositivo detectável aceitar a solicitação de pareamento, os dois dispositivos concluem um processo de vínculo, onde trocam chaves de segurança. Eles armazenam essas chaves em cache para uso posterior. Após a finalização dos processos de pareamento e vínculo, 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 vinculava ao dispositivo detectável. No entanto, ambos permanecem vinculados para que possam se reconectar automaticamente durante uma sessão futura, desde que estejam ao alcance um do outro e nenhum dos dois tenha removido o vínculo.
Permissões do Bluetooth
Para usar recursos do Bluetooth no aplicativo, você precisa declarar duas permissões. A primeira delas é BLUETOOTH
. Você precisa dessa permissão para executar todas as comunicações do Bluetooth, como solicitar ou aceitar uma conexão e transferir dados.
A outra permissão que precisa ser declarada é ACCESS_FINE_LOCATION
. Seu aplicativo precisa dessa permissão porque uma verificação Bluetooth pode ser usada para coletar informações sobre o local do usuário. Essa informação pode vir do próprio dispositivo do usuário, assim como de sensores Bluetooth em uso em locais como centros comerciais e de grande circulação.
Observação: caso seu aplicativo seja direcionado ao Android 9 (API de nível 28) ou posteriores, você pode declarar a permissão ACCESS_COARSE_LOCATION
em vez da permissão ACCESS_FINE_LOCATION
.
Se você quiser que o aplicativo inicie a descoberta de dispositivos ou manipule configurações do Bluetooth, será necessário declarar também a permissão BLUETOOTH_ADMIN
, além da permissão BLUETOOTH
. A maioria dos aplicativos precisa dessa permissão somente para poder descobrir dispositivos Bluetooth locais. Os outros recursos concedidos por essa permissão não devem ser usados, a menos que o aplicativo seja um “gerenciador de energia” que modifique configurações do Bluetooth mediante solicitações 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 a declaração de permissões do aplicativo.
Trabalhar com perfis
A partir do Android 3.0, a API Bluetooth permite trabalhar com perfis Bluetooth. O perfil Bluetooth é uma especificação de interface sem fio para comunicação com base em Bluetooth entre dispositivos. Um exemplo é o perfil Hands-Free. Para um smartphone se conectar a um fone de ouvido sem fio, os dois dispositivos precisam ser compatíveis com o perfil Hands-Free.
A API Bluetooth do Android oferece implementações para os seguintes perfis Bluetooth:
- Headset. O perfil Headset permite usar fones de ouvido Bluetooth com smartphones. O Android oferece a classe
BluetoothHeadset
, que é um proxy para controlar o Bluetooth Headset Service. Isso abrange os perfis Bluetooth Headset e Hands-Free (v1.5). A classeBluetoothHeadset
é compatível com comandos AT. Para ver uma discussão mais detalhada desse 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 usando uma conexão Bluetooth. O Android oferece a classe
BluetoothA2dp
, que é um proxy para controlar o Bluetooth A2DP Service. - Health Device. O Android 4.0 (API de nível 14) introduz a compatibilidade com o perfil Health Device (HDP) do Bluetooth. Esse perfil permite criar aplicativos que usam o Bluetooth para comunicação com dispositivos de saúde compatíveis com Bluetooth, como monitores de frequência cardíaca, medidores de sangue, termômetros, balanças etc. Para ter acesso a uma lista de dispositivos compatíveis e os códigos de especialização de dados de 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 ver uma discussão mais detalhada do HDP, consulte Perfil Health Device.
Veja a seguir as etapas básicas para trabalhar com um perfil:
- Tenha acesso ao adaptador padrão conforme descrito em Configurar o Bluetooth.
- Configure um
BluetoothProfile.ServiceListener
. Esse listener notifica os clientes deBluetoothProfile
quando são conectados ou desconectados do serviço. - Use
getProfileProxy()
para estabelecer uma conexão ao objeto de proxy de perfil associado ao perfil. No exemplo abaixo, o objeto de proxy de perfil é uma instância deBluetoothHeadset
. - Em
onServiceConnected()
, acesse um manipulador para o objeto de proxy de perfil. - Após ter o objeto de proxy de perfil, você pode usá-lo para monitorar o estado da conexão e executar outras operações relevantes para esse perfil.
Por exemplo, este snippet de código mostra como conectar-se a um objeto de proxy de BluetoothHeadset
para poder 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 registrar-se para receber transmissões de sistema de comandos AT predefinidos específicos de 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, bem como notificar o usuário ou executar outra ação necessária. Crie um broadcast receiver para o intent ACTION_VENDOR_SPECIFIC_HEADSET_EVENT
para processar comandos AT específicos de fornecedor para o fone de ouvido.
Perfil Health Device
O Android 4.0 (API de nível 14) introduz a compatibilidade com o perfil Health Device (HDP) do Bluetooth. Esse perfil permite criar aplicativos que usam o Bluetooth para comunicação 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 Bluetooth Health API contém as classes BluetoothHealth
, BluetoothHealthCallback
e BluetoothHealthAppConfiguration
, descritas em Classes principais e interfaces.
A compreensão destes conceitos principais do HDP pode ajudar a usar a API Bluetooth Health:
- Fonte
- É 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 aplicativos HDP do 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 em particular.
- 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:
- Tenha uma referência ao objeto de proxy de
BluetoothHealth
.Da mesma forma que nos dispositivos com perfil Headset e A2DP, você precisa chamar
getProfileProxy()
com umBluetoothProfile.ServiceListener
e o tipo de perfilHEALTH
para estabelecer uma conexão com o objeto de proxy de perfil. - Crie um
BluetoothHealthCallback
e registre uma configuração de aplicativo (BluetoothHealthAppConfiguration
) que atue como um coletor de saúde. 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.
- Quando conectado a um dispositivo de saúde, leia/grave dados nele usando o descritor de arquivo. Os dados recebidos precisam ser interpretados usando um gerenciador de saúde que implemente as especificações IEEE 11073.
- Quando terminar, feche o canal de saúde e cancele a inscrição do aplicativo. O canal também se fechará quando houver um longo período de inatividade.
Configurar o Bluetooth
Antes que o aplicativo se comunique usando o Bluetooth, é necessário verificar se o dispositivo permite Bluetooth e, caso permita, se o Bluetooth está ativado.
Se o dispositivo não permitir o Bluetooth, desative todos os recursos do Bluetooth de forma ordenada. Se o dispositivo for compatível com Bluetooth, mas estiver desativado, você poderá solicitar que o usuário ative o Bluetooth sem sair do aplicativo. Essa configuração é efetuada em duas etapas, usando o BluetoothAdapter
:
- Tenha o
BluetoothAdapter
.O
BluetoothAdapter
é necessário para todas as atividades do Bluetooth. Para ter oBluetoothAdapter
, chame o método estáticogetDefaultAdapter()
. UmBluetoothAdapter
será retornado para representar 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. SegetDefaultAdapter()
retornarnull
, significa que o dispositivo não é compatível com 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 }
- Ative o Bluetooth.
Em seguida, é necessário garantir a ativação do Bluetooth. Chame
isEnabled()
para verificar se o Bluetooth está ativado. Se o método retornar “false”, o Bluetooth está desativado. Para solicitar a ativação do Bluetooth, chamestartActivityForResult()
, passando em uma ação de intentACTION_REQUEST_ENABLE
. Essa chamada envia uma solicitação para ativar o Bluetooth por meio das configurações do sistema (sem que o aplicativo seja interrompido). 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); }
Será exibida uma caixa de diálogo para solicitar ao usuário permissão de ativação do Bluetooth, conforme visto na Figura 1. Se o usuário responder “Yes”, o sistema começará a ativar o Bluetooth e o foco retornará ao seu aplicativo após a conclusão (ou falha) do processo.
Figura 1. A caixa de diálogo de ativação do Bluetooth.
A constante
REQUEST_ENABLE_BT
passada astartActivityForResult()
é um número inteiro definido localmente (que precisa ser maior que 0) passado pelo sistema para a implementação deonActivityResult()
como o parâmetrorequestCode
.Se a ativação do Bluetooth for bem-sucedida, a atividade receberá o código de resultado
RESULT_OK
no callback.onActivityResult()
Se o Bluetooth não foi ativado devido a um erro (ou à resposta “No” do usuário), o código de resultado seráRESULT_CANCELED
.
Opcionalmente, o aplicativo também pode ouvir o intent de transmissão ACTION_STATE_CHANGED
, transmitido pelo sistema sempre que o estado do Bluetooth for alterado. Essa transmissão contém os campos adicionais EXTRA_STATE
e EXTRA_PREVIOUS_STATE
, que contêm respectivamente os estados novo e anterior do Bluetooth. Os valores possíveis dos campos adicionais são STATE_TURNING_ON
, STATE_ON
, STATE_TURNING_OFF
e STATE_OFF
. Pode ser útil ouvir essa transmissão se o aplicativo precisar detectar alterações do estado do Bluetooth no ambiente de execução.
Dica: ativar a detecção do dispositivo automaticamente ativa o Bluetooth. Se você planeja sempre ativar a detecção do dispositivo antes de executar as atividades do Bluetooth, pule a etapa 2 acima. Para mais informações, leia a seção ativar a detecção do dispositivo nesta página.
Encontrar dispositivos
Use o BluetoothAdapter
para encontrar dispositivos remotos Bluetooth por meio da descoberta de dispositivos ou consultando a lista de dispositivos pareados vinculados.
A descoberta de dispositivos é um procedimento de análise que procura dispositivos com Bluetooth ativado na área local e solicita algumas informações de cada um deles. Às vezes, esse processo é denominado descoberta, consulta ou análise. No entanto, os dispositivos Bluetooth próximos responderão a solicitações de descoberta somente se estiverem configurados como detectáveis. Se o dispositivo estiver detectável, responderá à solicitação de descoberta compartilhando algumas informações, como o nome, a classe e o endereço MAC único do dispositivo. Com essas informações, o dispositivo que executa a descoberta pode optar por iniciar uma conexão com o dispositivo descoberto.
Após a conexão com um dispositivo remoto pela primeira vez, uma solicitação de pareamento será automaticamente apresentada ao usuário. Quando um dispositivo é pareado, as informações básicas sobre esse dispositivo — como nome, classe e endereço MAC do dispositivo — 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 esteja dentro do alcance.
Lembre-se de que há uma diferença entre estar pareado e estar conectado:
- Estar pareado significa que os dois dispositivos estão cientes da existência um do outro, têm um link-chave compartilhado que pode ser usado para autenticação e podem 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 Bluetooth atual do Android exige que os dispositivos estejam pareados antes de estabelecer uma conexão RFCOMM. O pareamento é executado automaticamente na inicialização de uma conexão criptografada com as APIs do Bluetooth.
As sessões a seguir descrevem como encontrar dispositivos que já foram pareados ou descobrir novos dispositivos usando a descoberta de dispositivos.
Observação: por padrão, dispositivos Android não são detectáveis. O usuário pode tornar o dispositivo detectável por tempo limitado por meio das configurações do sistema ou o aplicativo pode solicitar que o usuário ative a detecção do dispositivo sem sair do aplicativo. Para mais informações, consulte a seção ativar detecção do dispositivo nesta página.
Consultar dispositivos pareados
Antes de executar a descoberta de dispositivos, vale a pena consultar o conjunto de dispositivos pareados para verificar se o dispositivo desejado já é conhecido. Para isso, chame getBondedDevices()
. Isso retorna um conjunto de objetos BluetoothDevice
que representam dispositivos pareados. Por exemplo, você pode consultar todos os dispositivos pareados e mostrar o nome e o endereço MAC de cada dispositivo, como 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, só é necessário o endereço MAC do objeto BluetoothDevice
associado, que você pode recuperar chamando getAddress()
. Para saber mais sobre a criação de uma conexão, veja a seção Conectar dispositivos.
Atenção: a realização de descoberta de dispositivos consome muitos recursos do adaptador de Bluetooth. Assim que você encontrar um dispositivo para conexão, certifique-se de interromper a descoberta com cancelDiscovery()
antes de tentar a conexão. Além disso, a realização da descoberta durante a conexão não é recomendada, uma vez que 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()
. Esse processo é assíncrono e retorna um valor booleano que indica se a descoberta foi iniciada corretamente. Normalmente, o processo de descoberta envolve uma análise de consulta de cerca de 12 segundos, seguida por uma análise de página de cada dispositivo encontrado para recuperar o nome do Bluetooth.
Para descobrir informações sobre cada dispositivo descoberto, o aplicativo precisa registrar um BroadcastReceiver para o intent ACTION_FOUND
. O sistema transmite esse intent para cada dispositivo. O 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 a seguir mostra como é possível registrar-se 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, só é necessário o endereço MAC do objeto BluetoothDevice
associado, que você pode recuperar chamando getAddress()
. Para saber mais sobre a criação de uma conexão, veja a seção Conectar dispositivos.
Atenção: a realização de descoberta de dispositivos consome muitos recursos do adaptador de Bluetooth. Assim que você encontrar um dispositivo para conexão, certifique-se de interromper a descoberta com cancelDiscovery()
antes de tentar a conexão. Além disso, a realização da descoberta durante a conexão não é recomendada, uma vez que esse processo reduz significativamente a largura de banda disponível para as conexões atuais.
Ativar a detecção do dispositivo
Se você quer que o dispositivo local seja detectável por outros dispositivos, chame startActivityForResult(Intent, int)
com o Intent de ação ACTION_REQUEST_DISCOVERABLE
. Com isso, uma solicitação será enviada para ativar o modo detectável do sistema, sem que haja necessidade de navegar até o app Configurações, o que interromperia seu próprio aplicativo. Por padrão, o dispositivo torna-se detectável durante 120 segundos (2 minutos). Ao adicionar o extra EXTRA_DISCOVERABLE_DURATION
, é possível definir uma duração diferente, de até 3.600 segundos (1 hora).
Atenção: se você definir o valor do extra EXTRA_DISCOVERABLE_DURATION
como 0, o dispositivo ficará sempre detectável. Não recomendamos essa configuração, uma vez que ela não é segura.
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);

Será exibida uma caixa de diálogo que solicitará a permissão do usuário para tornar o dispositivo detectável, como mostrado na Figura 2. Se o usuário responder “Yes”, o dispositivo se tornará detectável pelo tempo especificado. A atividade receberá então uma chamada para o callback onActivityResult()
, com o código de resultado igual à duração do período em que o dispositivo permanecerá detectável. Se o usuário responder “No” ou se ocorrer um erro, o código de resultado será RESULT_CANCELED
.
Observação: se o Bluetooth não foi ativado no dispositivo, ele será ativado automaticamente pela ativação da detecção do dispositivo.
O dispositivo permanecerá silenciosamente no modo detectável pelo tempo especificado. Se você quiser ser notificado quando o modo detectável mudar, registre um BroadcastReceiver para o intent .ACTION_SCAN_MODE_CHANGED
. Esse intent contém os campos extras EXTRA_SCAN_MODE
e EXTRA_PREVIOUS_SCAN_MODE
, que fornecem respectivamente os modos de análise novo e anterior. 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 a um dispositivo remoto, não será necessário ativar a detecção do dispositivo. A ativação da detecção do dispositivo só é necessária se você quiser que o aplicativo hospede um soquete de servidor que aceite as conexões recebidas, uma vez que os dispositivos remotos precisam ser capazes de descobrir o dispositivo antes de iniciar a conexão.
Conectar dispositivos
Para criar uma conexão entre dois dispositivos, você precisa implementar os mecanismos do lado do cliente e do servidor, porque um dispositivo precisa abrir um soquete de servidor e o outro precisa iniciar a conexão (usando o endereço MAC do dispositivo servidor para iniciar a conexão). O dispositivo servidor e o dispositivo cliente recebem o BluetoothSocket
necessário de formas diferentes. O servidor recebe a informação do soquete quando uma conexão de entrada é aceita. O cliente fornece a informação do soquete quando abre um canal RFCOMM para o servidor.
O servidor e o cliente serão considerados conectados entre si quando cada um deles tiver um BluetoothSocket
conectado no mesmo canal RFCOMM. Nesse momento, cada dispositivo pode ter 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 das técnicas de implementação é preparar automaticamente cada dispositivo como servidor para que cada um tenha um soquete de servidor aberto e ouça conexões. Nesse caso, qualquer um dos dispositivos pode iniciar a 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 pode simplesmente iniciar a conexão.

Observação: se os dois dispositivos não foram pareados previamente, a biblioteca do Android mostrará automaticamente uma notificação ou caixa de diálogo de solicitação de pareamento ao usuário durante o procedimento de conexão, como mostrado na Figura 3. Portanto, ao tentar conectar dispositivos, o aplicativo não precisa se preocupar com o pareamento deles. A tentativa de conexão RFCOMM ficará bloqueada até que o usuário execute o pareamento entre os dois dispositivos corretamente. Ela falhará se o usuário rejeitar o pareamento ou se o pareamento falhar ou esgotar-se o tempo limite.
Conectar como servidor
Para conectar dois dispositivos, um precisa atuar como servidor, mantendo um BluetoothServerSocket
aberto. O objetivo do soquete do servidor é ouvir solicitações de conexão de entrada e, quando uma for aceita, fornecer um BluetoothSocket
conectado. Quando o BluetoothSocket
é adquirido do BluetoothServerSocket
, o BluetoothServerSocket
pode — e precisa — ser descartado, a menos que você queria aceitar mais conexões.
Veja a seguir o procedimento básico para configurar um soquete de servidor e aceitar uma conexão:
- Chame um
listenUsingRfcommWithServiceRecord()
e receba umBluetoothServerSocket
.A string é um nome identificável do serviço e será gravada automaticamente pelo sistema 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 universalmente único (UUID, na sigla em inglês) também está incluído na entrada SDP e será a base para o acordo de conexão com o dispositivo cliente. Ou seja, quando o cliente tentar se conectar com este dispositivo, ele terá um UUID que identifica unicamente o serviço com que quer se conectar. Esses UUIDs precisam corresponder para que a conexão seja aceita.
O UUID é um formato padronizado de 128 bits para um código de string usado para identificar unicamente informações. A vantagem de um UUID é que é grande o suficiente para permitir a seleção aleatória de qualquer código sem conflitos com outros. Nesse caso, ele é usado para identificar unicamente o serviço Bluetooth do aplicativo. Para ter um UUID para uso no aplicativo, você pode usar um dos diversos geradores de UUID aleatórios na Web e inicializar um
UUID
comfromString(String)
. - Comece a ouvir solicitações de conexão chamando
accept()
.Essa é uma chamada de bloqueio. Ela retornará quando uma conexão for aceita ou ocorrer uma exceção. A conexão só será aceita quando um dispositivo remoto enviar uma solicitação de conexão com um UUID correspondente ao registrado nesse soquete do servidor ouvinte. Quando bem-sucedido,
accept()
retornará umBluetoothSocket
conectado. - A menos que você queira aceitar mais conexões, chame
close()
.Com isso, o soquete do servidor e todos os seus recursos serão liberados, mas o
BluetoothSocket
conectado retornado poraccept()
não será fechado. Ao contrário do TCP/IP, o RFCOMM somente permite um cliente conectado por canal em um determinado momento. Portanto, na maioria dos casos, faz sentido chamarclose()
noBluetoothServerSocket
imediatamente depois de aceitar um soquete conectado.
Como a chamada accept()
é de bloqueio, ela não deve ser executada no thread de IU da atividade principal. Assim, o aplicativo ainda poderá responder a outras interações do usuário. Normalmente, faz sentido fazer todo o trabalho que envolve um BluetoothServerSocket
ou BluetoothSocket
em um novo thread, gerenciado pelo aplicativo. Para cancelar uma chamada bloqueada como accept()
, chame close()
no BluetoothServerSocket
ou BluetoothSocket
de outro thread. Todos os métodos em um BluetoothServerSocket
ou BluetoothSocket
podem ser usados com segurança no thread.
Exemplo
Veja a seguir um thread simplificado do componente de 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); } } }
Nesse exemplo, só se quer uma conexão de entrada. Portanto, assim que uma conexão for aceita e o BluetoothSocket
for adquirido, o aplicativo passará o BluetoothSocket
adquirido a um thread separado, fechará o BluetoothServerSocket
e interromperá o loop.
Quando accept()
retorna o BluetoothSocket
, o soquete já está conectado. Portanto, não chame connect()
, como é feito no lado do cliente.
O método manageMyConnectedSocket()
específico do aplicativo é projetado para iniciar o thread para transferência de dados. Isso é discutido na seção sobre Gerenciar uma conexão.
Normalmente, é necessário fechar o BluetoothServerSocket
assim que acabar de ouvir conexões de entrada. Nesse exemplo, close()
é chamado assim que o BluetoothSocket
é adquirido. Também pode ser necessário fornecer um método público no thread que possa fechar o BluetoothSocket
privado se for preciso parar de ouvir no soquete do servidor.
Conectar como cliente
Para iniciar uma conexão com um dispositivo remoto que aceite conexões com um soquete de servidor aberto, é necessário antes ter um objeto BluetoothDevice
que representa o dispositivo remoto. Para saber como criar um BluetoothDevice
, consulte Encontrar dispositivos. Em seguida, use BluetoothDevice
para adquirir um BluetoothSocket
e iniciar a conexão.
Este é o procedimento básico:
- Usando o
BluetoothDevice
, chamecreateRfcommSocketToServiceRecord(UUID)
para ter umBluetoothSocket
.Esse método inicializa um objeto
BluetoothSocket
que permite que o cliente se conecte a umBluetoothDevice
. O UUID passado aqui precisa corresponder ao UUID usado pelo dispositivo servidor quando chamoulistenUsingRfcommWithServiceRecord(String, UUID)
para abrir oBluetoothServerSocket
. Para usar o UUID correspondente, codifique a string UUID no seu aplicativo e a referencie no código do servidor e do cliente. - Chame
connect()
para iniciar a conexão. Esse método é uma chamada de bloqueio.Após esse método ser chamado pelo cliente, o sistema realizará uma pesquisa SDP para encontrar dispositivos remotos 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()
retornará. Caso a conexão falhe ou o tempo do métodoconnect()
se encerrar (após cerca de 12 segundos), o método lançará umaIOException
.Como
connect()
é uma chamada de bloqueio, sempre realize o procedimento de conexão em um thread separado do thread (IU) da atividade principal.Observação: sempre chame
cancelDiscovery()
para garantir que o dispositivo não faça descoberta de dispositivos antes de oconnect()
ser chamado. Se houver uma descoberta em andamento, a tentativa de conexão terá a velocidade substancialmente reduzida e provavelmente falhará.
Exemplo
Veja um exemplo básico de um thread 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 que a tentativa de conexão ocorra. Sempre chame cancelDiscovery()
antes de connect()
, principalmente porque cancelDiscovery()
será bem-sucedido, independentemente de a descoberta do dispositivo estar em progresso. No entanto, se o aplicativo precisar determinar se a descoberta do dispositivo está em progresso, é possível conferir usando isDiscovering()
.
O método manageMyConnectedSocket()
específico do aplicativo é projetado para iniciar o thread para transferência de dados. Isso é discutido na seção sobre Gerenciar uma conexão.
Ao concluir o BluetoothSocket
, sempre chame close()
. Ao fazer isso, o soquete conectado automaticamente se fecha e libera todos os recursos internos relacionados.
Gerenciar uma conexão
Após você ter conectado corretamente vários dispositivos, cada um deles terá um BluetoothSocket
conectado. É aí que começa a ficar interessante: você pode compartilhar dados entre dispositivos. Ao usar o BluetoothSocket
, o procedimento geral para transferência de dados é este:
- Acesse o
InputStream
e oOutputStream
que processam transmissões pelo soquete por meio degetInputStream()
egetOutputStream()
, respectivamente. - Leia e grave dados nos streams com
read(byte[])
ewrite(byte[])
.
Naturalmente, é necessário considerar detalhes de implementação. Em particular, é necessário usar um thread dedicado para ler a partir do stream e registrar nele. Isso é importante, já que os métodos read(byte[])
e write(byte[])
são chamadas de bloqueio. O método read(byte[])
bloqueia até que haja algo a ser lido do stream. O método write(byte[])
não costuma bloquear, mas pode fazer isso para controle de fluxo, se o dispositivo remoto não estiver chamando read(byte[])
rápido o suficiente e os buffers intermediários ficarem cheios como resultado. Portanto, o loop principal do thread precisa ser dedicado à leitura do InputStream
. Pode-se usar um método público separado no thread para iniciar gravações no OutputStream
.
Exemplo
Veja 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, 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); } } } }
Após o construtor adquirir os streams necessários, o thread espera a chegada dos dados por meio do InputStream
. Quando read(byte[])
retorna com dados do stream, eles são enviados à atividade principal usando um Handler
de membros da classe pai. O thread espera que mais bytes sejam lidos do InputStream
.
Para enviar dados de saída, basta chamar o método write()
do thread da atividade principal e passar os bytes a enviar. Esse método chama write(byte[])
para enviar os dados ao dispositivo remoto. Se uma IOException
for lançada ao chamar write(byte[])
, o thread enviará um toast à atividade principal, explicando ao usuário que o dispositivo não pôde enviar os bytes a outro dispositivo conectado.
O método cancel()
do thread permite que a conexão seja finalizada a qualquer momento por meio do fechamento do BluetoothSocket
. Chame esse método sempre que acabar de usar a conexão Bluetooth.
Para ver uma demonstração do uso das APIs Bluetooth, consulte o aplicativo de amostra Bluetooth Chat.
Classes principais e interfaces
Todas as APIs Bluetooth estão disponíveis no pacote .android.bluetooth
Veja a seguir 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. Use-o para descobrir outros dispositivos Bluetooth, consultar uma lista de dispositivos vinculados (pareados), instanciar umBluetoothDevice
usando um endereço MAC conhecido e criar umBluetoothServerSocket
para ouvir comunicações de outros dispositivos. BluetoothDevice
- Representa um dispositivo remoto Bluetooth. Use-o para solicitar uma conexão com um dispositivo remoto por meio de 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
TCP). É o ponto de conexão que permite que um aplicativo troque dados com outro dispositivo Bluetooth por meio deInputStream
eOutputStream
. BluetoothServerSocket
- Representa um soquete de servidor aberto que ouve solicitações recebidas (semelhante a um
ServerSocket
TCP). Para conectar dois dispositivos Android, um dispositivo precisa abrir um soquete de servidor com esta classe. Quando um dispositivo remoto Bluetooth estabelecer uma solicitação de conexão com esse dispositivo, o dispositivo aceitará a conexão e retornará umBluetoothSocket
conectado. BluetoothClass
- Descreve as características e os recursos gerais de um dispositivo Bluetooth. Essa descrição é um conjunto de propriedades somente para leitura que definem as classes e os serviços do dispositivo. Embora essa informação forneça uma dica útil com relação ao tipo de dispositivo, os atributos dessa classe não descrevem necessariamente todos os serviços e perfis Bluetooth compatíveis com o dispositivo.
BluetoothProfile
- É uma interface que representa um perfil Bluetooth. O perfil Bluetooth é uma especificação de interface sem fio para comunicação com base em Bluetooth entre dispositivos. Um exemplo é o perfil Hands-Free. Para ver uma discussão mais detalhada dos perfis, consulte Trabalho com perfis.
BluetoothHeadset
- Permite o uso de fones de ouvido Bluetooth 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 meio de uma conexão Bluetooth usando o perfil Advanced Audio Distribution Profile (A2DP).
BluetoothHealth
- Representa um proxy do perfil Health Device que controla o serviço Bluetooth.
BluetoothHealthCallback
- Classe abstrata usada para implementar callbacks de
BluetoothHealth
. É necessário estender essa classe e implementar os métodos de callback para receber atualizações sobre as alterações no estado do registro do aplicativo e no estado do canal Bluetooth. BluetoothHealthAppConfiguration
- Representa uma configuração de aplicativo registrada pelo aplicativo de saúde de terceiros do Bluetooth para comunicação com um dispositivo de saúde remoto do Bluetooth.
BluetoothProfile.ServiceListener
- É uma interface que notifica clientes de comunicação entre processos (IPC, na sigla em inglês) do
BluetoothProfile
quando eles se conectam ou desconectam do serviço interno que executa um perfil em particular.