Quando seu dispositivo com tecnologia Android está no modo de host USB, ele atua como host USB, alimenta o barramento e enumera os dispositivos USB conectados. O modo de host USB é compatível com o Android 3.1 e posterior.
Visão geral da API
Antes de começar, é importante entender as classes com as quais você precisa trabalhar. A
a tabela a seguir descreve as APIs de host USB no pacote android.hardware.usb
.
Classe | Descrição |
---|---|
UsbManager |
Permite que você enumere e se comunique com dispositivos USB conectados. |
UsbDevice |
Representa um dispositivo USB conectado e contém métodos para acessar o sistema de identificação informações, interfaces e endpoints. |
UsbInterface |
Representa uma interface de um dispositivo USB, que define um conjunto de funcionalidades para o dispositivo. Um dispositivo pode ter uma ou mais interfaces de comunicação. |
UsbEndpoint |
Representa um endpoint da interface, que é um canal de comunicação dessa interface. Um interface pode ter um ou mais endpoints e geralmente tem endpoints de entrada e saída para comunicação bidirecional com o dispositivo. |
UsbDeviceConnection |
Representa uma conexão com o dispositivo, que transfere dados em endpoints. Esta turma permite que você envie dados de forma síncrona ou assíncrona. |
UsbRequest |
Representa uma solicitação assíncrona para se comunicar com um dispositivo por meio de UsbDeviceConnection . |
UsbConstants |
Define constantes USB que correspondem às definições em linux/usb/ch9.h do sistema operacional Linux. grão |
Na maioria das situações, você precisa usar todas essas classes (UsbRequest
só é necessário se você estiver fazendo comunicação assíncrona).
ao se comunicar com um dispositivo USB. Em geral, você usa um UsbManager
para recuperar o UsbDevice
desejado.
Quando você tiver o dispositivo, vai precisar encontrar o UsbInterface
e o UsbEndpoint
apropriados
para se comunicar. Depois de ter o endpoint correto, abra um UsbDeviceConnection
para se comunicar com o dispositivo USB.
Requisitos de manifesto do Android
A lista a seguir descreve o que você precisa adicionar ao arquivo de manifesto do aplicativo antes trabalhando com as APIs de host USB:
- Como nem todos os dispositivos Android têm suporte às APIs de host USB,
incluir um elemento
<uses-feature>
que declare que seu aplicativo usa o recursoandroid.hardware.usb.host
. - Defina o SDK mínimo do app como a API nível 12 ou posterior. As APIs de host USB não são presentes em níveis anteriores de API.
- Se quiser que seu aplicativo seja notificado sobre um dispositivo USB conectado, especifique um
Par de elementos
<intent-filter>
e<meta-data>
para oandroid.hardware.usb.action.USB_DEVICE_ATTACHED
na sua atividade principal. A O elemento<meta-data>
aponta para um arquivo de recurso XML externo que declara informações de identificação sobre o dispositivo que você quer detectar.No arquivo de recurso XML, declare elementos
<usb-device>
para o USB. os dispositivos que você quer filtrar. A lista a seguir descreve os atributos<usb-device>
: Em geral, use o ID do produto e do fornecedor se você quiser filtrar para um dispositivo específico e use classe, subclasse e protocolo se você quiser filtrar por um grupo de dispositivos USB, como dispositivos de armazenamento em massa ou câmeras digitais. É possível especificar "nenhum" ou todos esses atributos. Especificar que nenhum atributo corresponde a cada dispositivo USB, portanto, faça isso apenas se o aplicativo exigir:vendor-id
product-id
class
subclass
protocol
(dispositivo ou interface)
Salve o arquivo de recurso no diretório
res/xml/
. O nome do arquivo de recurso (sem a extensão .xml) deve ser igual ao especificado na<meta-data>
. O formato do arquivo de recurso XML está na exemplo abaixo.
Exemplos de arquivo de manifesto e recurso
O exemplo a seguir mostra um manifesto e o arquivo de recurso correspondente:
<manifest ...> <uses-feature android:name="android.hardware.usb.host" /> <uses-sdk android:minSdkVersion="12" /> ... <application> <activity ...> ... <intent-filter> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/device_filter" /> </activity> </application> </manifest>
Nesse caso, o arquivo de recurso a seguir deve ser salvo
res/xml/device_filter.xml
e especifica que qualquer dispositivo USB com a
atributos devem ser filtrados:
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-device vendor-id="1234" product-id="5678" class="255" subclass="66" protocol="1" /> </resources>
Trabalhar com dispositivos
Quando os usuários conectam dispositivos USB a um dispositivo com tecnologia Android, o sistema Android pode determinar se o aplicativo está interessado no dispositivo conectado. Nesse caso, configure comunicação com o dispositivo, se quiser. Para fazer isso, seu app precisa:
- Descubra dispositivos USB conectados usando um filtro de intent para ser notificado quando o usuário conecta um dispositivo USB ou enumera os dispositivos USB que já estão conectados.
- pedir permissão ao usuário para se conectar ao dispositivo USB, se ainda não tiver recebido;
- Comunicar-se com o dispositivo USB lendo e gravando dados na interface adequada endpoints.
Descobrir um dispositivo
Seu aplicativo pode descobrir dispositivos USB usando um filtro de intent para ser notificado quando o usuário conecta um dispositivo ou enumera os dispositivos USB que já estão conectados. Usar um o filtro de intent é útil quando você quer que seu aplicativo detecte automaticamente dispositivo desejado. Enumerar os dispositivos USB conectados é útil se você quiser uma lista de todos os dispositivos conectados ou se o aplicativo não filtrou uma intent.
Usar um filtro de intent
Para que seu aplicativo descubra um dispositivo USB específico, você pode especificar um filtro de intents para
filtro para a intent android.hardware.usb.action.USB_DEVICE_ATTACHED
. Junto com
filtro de intent, é preciso especificar um arquivo de recurso que especifique as propriedades do USB
dispositivo, como ID do produto e do fornecedor. Quando os usuários conectam um dispositivo que corresponde ao seu
filtro, o sistema apresentará a eles uma caixa de diálogo perguntando se eles desejam iniciar seu aplicativo.
Se os usuários aceitarem, seu aplicativo automaticamente terá permissão para acessar o dispositivo até que a
dispositivo está desconectado.
O exemplo a seguir mostra como declarar o filtro de intent:
<activity ...> ... <intent-filter> <action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" /> </intent-filter> <meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" android:resource="@xml/device_filter" /> </activity>
O exemplo a seguir mostra como declarar o arquivo de recurso correspondente que especifica os Dispositivos USB do seu interesse:
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-device vendor-id="1234" product-id="5678" /> </resources>
Na sua atividade, você pode acessar o UsbDevice
que representa
o dispositivo anexado pelo intent, desta forma:
Kotlin
val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
Java
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
Enumerar dispositivos
Se seu aplicativo estiver interessado em inspecionar todos os dispositivos USB conectados no momento
enquanto seu aplicativo está em execução, ele pode enumerar dispositivos no barramento. Use o método getDeviceList()
para acessar um mapa de hash de todos
os dispositivos USB conectados. O mapa de hash é codificado pelo nome do dispositivo USB, se você quiser
obter um dispositivo do mapa.
Kotlin
val manager = getSystemService(Context.USB_SERVICE) as UsbManager ... val deviceList = manager.getDeviceList() val device = deviceList.get("deviceName")
Java
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); ... HashMap<String, UsbDevice> deviceList = manager.getDeviceList(); UsbDevice device = deviceList.get("deviceName");
Se quiser, você também pode simplesmente obter um iterador do mapa hash e processar cada um do dispositivo por um:
Kotlin
val manager = getSystemService(Context.USB_SERVICE) as UsbManager .. val deviceList: HashMap<String, UsbDevice> = manager.deviceList deviceList.values.forEach { device -> // your code }
Java
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); ... HashMap<String, UsbDevice> deviceList = manager.getDeviceList(); Iterator<UsbDevice> deviceIterator = deviceList.values().iterator(); while(deviceIterator.hasNext()){ UsbDevice device = deviceIterator.next(); // your code }
Receber permissão para se comunicar com um dispositivo
Antes de se comunicar com o dispositivo USB, o aplicativo deve ter permissão do usuários.
Observação: caso seu aplicativo use uma filtro de intent para descobrir dispositivos USB à medida que eles estão conectados, ele recebe automaticamente permissão se o usuário permitir que seu aplicativo manipule a intent. Caso contrário, solicite explicitamente em seu aplicativo antes de se conectar ao dispositivo.
Pedir permissão explicitamente pode ser necessário em algumas situações, como quando seu aplicativo enumera os dispositivos USB que já estão conectados e depois querem se comunicar com um. Você precisa verificar a permissão para acessar um dispositivo antes de tentar se comunicar com ele. Se não, você receberá um erro de tempo de execução se o usuário negar a permissão para acessar o dispositivo.
Para receber permissão explicitamente, primeiro crie um broadcast receiver. Este receptor detecta
a intent que é transmitida quando você chama requestPermission()
. A chamada para requestPermission()
mostra uma caixa de diálogo para o
o usuário está pedindo permissão para se conectar ao dispositivo. O código de exemplo a seguir mostra como
crie o broadcast receiver:
Kotlin
private const val ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION" private val usbReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (ACTION_USB_PERMISSION == intent.action) { synchronized(this) { val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { device?.apply { // call method to set up device communication } } else { Log.d(TAG, "permission denied for device $device") } } } } }
Java
private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION"; private final BroadcastReceiver usbReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ACTION_USB_PERMISSION.equals(action)) { synchronized (this) { UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) { if(device != null){ // call method to set up device communication } } else { Log.d(TAG, "permission denied for device " + device); } } } } };
Para registrar o broadcast receiver, adicione isso ao método onCreate()
na sua
atividade:
Kotlin
private const val ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION" ... val manager = getSystemService(Context.USB_SERVICE) as UsbManager ... permissionIntent = PendingIntent.getBroadcast(this, 0, Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE) val filter = IntentFilter(ACTION_USB_PERMISSION) registerReceiver(usbReceiver, filter)
Java
UsbManager usbManager = (UsbManager) getSystemService(Context.USB_SERVICE); private static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION"; ... permissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE); IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION); registerReceiver(usbReceiver, filter);
Para exibir a caixa de diálogo que solicita aos usuários permissão para se conectar ao dispositivo, chame o método requestPermission()
:
Kotlin
lateinit var device: UsbDevice ... usbManager.requestPermission(device, permissionIntent)
Java
UsbDevice device; ... usbManager.requestPermission(device, permissionIntent);
Quando os usuários respondem à caixa de diálogo, seu broadcast receiver recebe a intent que contém o
EXTRA_PERMISSION_GRANTED
extra, que é um booleano
que representam a resposta. Verifique se esse extra tem um valor "true" antes de se conectar ao
dispositivo.
Comunicar-se com um dispositivo
A comunicação com um dispositivo USB pode ser síncrona ou assíncrona. Em ambos os casos,
deve criar uma nova sequência na qual realizar todas as transmissões de dados, de modo que você não bloqueie o
linha de execução de IU. Para configurar corretamente a comunicação com um dispositivo, é necessário ter o
UsbInterface
e UsbEndpoint
do
dispositivo em que você quer se comunicar e enviar solicitações nesse endpoint com um UsbDeviceConnection
. Em geral, seu código precisa:
- Verifique os atributos de um objeto
UsbDevice
, como o ID do produto, ID do fornecedor ou classe do dispositivo para descobrir se você quer ou não se comunicar com o dispositivo. - Quando tiver certeza de que deseja se comunicar com o dispositivo, encontre o
UsbInterface
que você queira usar para se comunicar com o oUsbEndpoint
apropriado dessa interface. As interfaces podem ter um ou mais endpoints e geralmente terão um endpoint de entrada e saída para comunicação. - Quando encontrar o endpoint correto, abra um
UsbDeviceConnection
nesse endpoint. - fornecer os dados que você quer transmitir no endpoint com o método
bulkTransfer()
oucontrolTransfer()
. Você deve realize essa etapa em outra linha de execução para evitar o bloqueio da linha de execução de interface principal. Para mais informações sobre o uso de linhas de execução no Android, consulte Processos e Linhas de execução.
O snippet de código a seguir é uma maneira fácil de fazer uma transferência de dados síncrona. Seu código deve ter mais lógica para encontrar corretamente a interface e os endpoints corretos para comunicação e também deve fazer qualquer transferência de dados em uma linha de execução diferente da linha de execução de interface principal:
Kotlin
private lateinit var bytes: ByteArray private val TIMEOUT = 0 private val forceClaim = true ... device?.getInterface(0)?.also { intf -> intf.getEndpoint(0)?.also { endpoint -> usbManager.openDevice(device)?.apply { claimInterface(intf, forceClaim) bulkTransfer(endpoint, bytes, bytes.size, TIMEOUT) //do in another thread } } }
Java
private Byte[] bytes; private static int TIMEOUT = 0; private boolean forceClaim = true; ... UsbInterface intf = device.getInterface(0); UsbEndpoint endpoint = intf.getEndpoint(0); UsbDeviceConnection connection = usbManager.openDevice(device); connection.claimInterface(intf, forceClaim); connection.bulkTransfer(endpoint, bytes, bytes.length, TIMEOUT); //do in another thread
Para enviar dados de forma assíncrona, use a classe UsbRequest
para initialize
e queue
uma solicitação assíncrona e aguarde o resultado
com requestWait()
.
Como encerrar a comunicação com um dispositivo
Quando você terminar de se comunicar com um dispositivo ou se ele tiver sido desconectado, feche o UsbInterface
e o UsbDeviceConnection
.
chamando releaseInterface()
e
close()
. Para detectar eventos independentes,
crie um broadcast receiver como abaixo:
Kotlin
var usbReceiver: BroadcastReceiver = object : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (UsbManager.ACTION_USB_DEVICE_DETACHED == intent.action) { val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE) device?.apply { // call your method that cleans up and closes communication with the device } } } }
Java
BroadcastReceiver usbReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) { UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (device != null) { // call your method that cleans up and closes communication with the device } } } };
Criar o broadcast receiver dentro do aplicativo, e não o manifesto, permite que seu para processar apenas eventos desconectados enquanto estiver em execução. Dessa forma, os eventos independentes são enviados somente para o aplicativo em execução no momento e não transmitidos para todos os aplicativos.