Visão geral de acessórios USB

O modo de acessório USB permite que os usuários se conectem ao hardware do host USB projetado especificamente para dispositivos com tecnologia Android. Os acessórios precisam aderir ao protocolo de acessórios Android descrito na documentação do Kit de desenvolvimento de acessórios Android. Isso permite que os dispositivos com tecnologia Android que não podem atuar como host USB ainda interajam com hardware USB. Quando um dispositivo com tecnologia Android está no modo de acessório USB, o acessório USB Android funciona como o host, fornece energia ao barramento USB e enumera os dispositivos conectados. O Android 3.1 (API de nível 12) é compatível com o modo de acessório USB, e o recurso também é compatível com o Android 2.3.4 (API nível de 10) para ativar o suporte a uma gama mais ampla de dispositivos.

Escolha as APIs de acessório USB certas

Embora as APIs de acessório USB tenham sido introduzidas na plataforma no Android 3.1, elas também estão disponíveis no Android 2.3.4 usando a biblioteca de complementos das APIs do Google. Como a compatibilidade dessas APIs com versões anteriores foi desenvolvida usando uma biblioteca externa, existem dois pacotes que podem ser importados para serem compatíveis com o modo de acessório USB. Dependendo para quais dispositivos com tecnologia Android você quer oferecer suporte, talvez seja necessário usar um em vez do outro:

  • com.android.future.usb: para ser compatível com o modo de acessório USB no Android 2.3.4, a biblioteca de complementos das APIs do Google inclui as APIs de acessórios USB compatíveis com versões anteriores e contidas neste namespace. O Android 3.1 também permite a importação e a chamada de classes dentro desse namespace para compatibilidade com apps gravados com a biblioteca de complementos. Essa biblioteca de complementos é um wrapper fino das APIs de acessórios android.hardware.usb e não é compatível com o modo de host USB. Se você quiser oferecer suporte à maior variedade de dispositivos compatíveis com o modo de acessório USB, use a biblioteca de complementos e importe esse pacote. É importante observar que nem todos os dispositivos Android 2.3.4 precisam ser compatíveis com o recurso de acessório USB. O fabricante de cada dispositivo decide se aceita ou não esse recurso, e é por isso que você precisa declará-lo no arquivo de manifesto.
  • android.hardware.usb: esse namespace contém as classes compatíveis com o modo de acessório USB no Android 3.1. Esse pacote está incluído como parte das APIs de framework, por isso o Android 3.1 é compatível com o modo de acessório USB sem o uso de uma biblioteca de complementos. Use esse pacote se você se preocupar apenas com dispositivos Android 3.1 ou mais recentes que tenham suporte de hardware para o modo de acessório USB, que podem ser declarados no arquivo de manifesto.

Instalar a biblioteca de complementos das APIs do Google

Para instalar o complemento, instale o pacote de APIs do Google para a API Android 10 com o SDK Manager. Consulte Instalar o complemento de APIs do Google para mais informações sobre como instalar a biblioteca de complementos.

Visão geral da API

Como a biblioteca de complementos é um wrapper para as APIs de framework, as classes que são compatíveis com o recurso de acessório USB são semelhantes. Você pode usar a documentação de referência do android.hardware.usb mesmo se estiver usando a biblioteca de complementos.

Observação: há, no entanto, uma pequena diferença de uso entre a biblioteca de complementos e as APIs de framework de que você precisa estar ciente.

A tabela a seguir descreve as classes compatíveis com as APIs de acessório USB:

Classe Descrição
UsbManager Permite que você enumere e se comunique com acessórios USB conectados.
UsbAccessory Representa um acessório USB e contém métodos para acessar as informações de identificação.

Diferenças de uso entre a biblioteca de complementos e as APIs de plataforma

Há duas diferenças de uso entre o uso da biblioteca de complementos das APIs do Google e as APIs de plataforma.

Se você estiver usando a biblioteca de complementos, precisará recuperar o objeto UsbManager da seguinte maneira:

Kotlin

    val manager = UsbManager.getInstance(this)
    

Java

    UsbManager manager = UsbManager.getInstance(this);
    

Se você não estiver usando a biblioteca de complementos, precisará recuperar o objeto UsbManager da seguinte maneira:

Kotlin

    val manager = getSystemService(Context.USB_SERVICE) as UsbManager
    

Java

    UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
    

Quando você aplica um filtro de intent para um acessório conectado, o objeto UsbAccessory está dentro do intent passado para seu app. Se você estiver usando a biblioteca de complementos, precisará recuperar o objeto UsbAccessory da seguinte maneira:

Kotlin

    val accessory = UsbManager.getAccessory(intent)
    

Java

    UsbAccessory accessory = UsbManager.getAccessory(intent);
    

Se você não estiver usando a biblioteca de complementos, precisará recuperar o objeto UsbAccessory da seguinte maneira:

Kotlin

    val accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) as UsbAccessory
    

Java

    UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
    

Requisitos de manifesto do Android

A lista a seguir descreve o que você precisa adicionar ao arquivo de manifesto do app antes de trabalhar com as APIs de acessório USB. Os exemplos de arquivo de manifesto e recurso mostram como declarar esses itens:

  • Como nem todos os dispositivos com tecnologia Android são compatíveis com as APIs de acessório USB, inclua um elemento <uses-feature> que declare que seu app usa o recurso android.hardware.usb.accessory.
  • Se você estiver usando a biblioteca de complementos, adicione o elemento <uses-library> especificando com.android.future.usb.accessory para a biblioteca.
  • Defina o SDK mínimo do app como a API de nível 10 se você estiver usando a biblioteca de complementos ou 12, se estiver usando o pacote android.hardware.usb.
  • Se você quiser que seu app seja notificado de um acessório USB conectado, especifique um par de elementos <intent-filter> e <meta-data> para o intent android.hardware.usb.action.USB_ACCESSORY_ATTACHED na sua atividade principal. O elemento <meta-data> aponta para um arquivo de recurso XML externo que declara informações de identificação sobre o acessório que você quer detectar.

    No arquivo de recurso XML, declare elementos <usb-accessory> para os acessórios que você quer filtrar. Cada <usb-accessory> pode ter os seguintes atributos:

    • manufacturer
    • model
    • version

    Salve o arquivo de recurso no diretório res/xml/. O nome do arquivo de recurso (sem a extensão .xml) precisa ser o mesmo que você especificou no elemento <meta-data>. O formato do arquivo de recurso XML também é mostrado no 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.accessory" />
        
        <uses-sdk android:minSdkVersion="<version>" />
        ...
        <application>
          <uses-library android:name="com.android.future.usb.accessory" />
            <activity ...>
                ...
                <intent-filter>
                    <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
                </intent-filter>

                <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
                    android:resource="@xml/accessory_filter" />
            </activity>
        </application>
    </manifest>
    

Nesse caso, o seguinte arquivo de recurso precisa ser salvo em res/xml/accessory_filter.xml e especifica que qualquer acessório que tenha o modelo, o fabricante e a versão correspondentes tem que ser filtrado. O acessório envia a esses atributos o dispositivo com tecnologia Android:

    <?xml version="1.0" encoding="utf-8"?>

    <resources>
        <usb-accessory model="DemoKit" manufacturer="Google" version="1.0"/>
    </resources>
    

Trabalhar com acessórios

Quando os usuários conectam acessórios USB a um dispositivo com tecnologia Android, o sistema Android pode determinar se seu app tem interesse no acessório conectado. Nesse caso, você pode configurar a comunicação com o acessório, se quiser. Para fazer isso, seu app precisa:

  1. descobrir acessórios conectados usando um filtro de intent para eventos anexados a acessórios ou enumerando acessórios conectados e encontrando o acessório apropriado;
  2. pedir permissão ao usuário para se comunicar com o acessório, se ainda não tiver recebido;
  3. comunicar-se com o acessório lendo e gravando dados nos endpoints da interface apropriados.

Descobrir um acessório

Seu app pode descobrir acessórios usando um filtro de intent para ser notificado quando o usuário conectar um acessório ou enumerando os acessórios que já estão conectados. Usar um filtro de intent será útil se você quiser que seu app detecte automaticamente um determinado acessório. Enumerar acessórios conectados será útil se você quiser uma lista de todos os acessórios conectados ou se o app não aplicar um filtro de intent.

Usar um filtro de intent

Para que seu app descubra um acessório USB específico, você pode especificar um filtro para o intent android.hardware.usb.action.USB_ACCESSORY_ATTACHED. Junto com esse filtro de intent, você precisa especificar um arquivo de recurso que especifique as propriedades do acessório USB, como fabricante, modelo e versão. Quando os usuários conectam um acessório que corresponde ao filtro de acessório,

O exemplo a seguir mostra como declarar o filtro de intent:

    <activity ...>
        ...
        <intent-filter>
            <action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
        </intent-filter>

        <meta-data android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"
            android:resource="@xml/accessory_filter" />
    </activity>
    

O exemplo a seguir mostra como declarar o arquivo de recurso correspondente que especifica os acessórios USB em que você tem interesse:

    <?xml version="1.0" encoding="utf-8"?>

    <resources>
        <usb-accessory manufacturer="Google, Inc." model="DemoKit" version="1.0" />
    </resources>
    

Na sua atividade, você pode ver o UsbAccessory que representa o acessório anexado do intent como esse (com a biblioteca de complementos):

Kotlin

    val accessory = UsbManager.getAccessory(intent)
    

Java

    UsbAccessory accessory = UsbManager.getAccessory(intent);
    

ou assim (com as APIs de plataforma):

Kotlin

    val accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) as UsbAccessory
    

Java

    UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
    

Enumerar acessórios

Você pode fazer com que seu app enumere acessórios que se identificaram enquanto seu app está em execução.

Use o método getAccessoryList() para ver uma matriz de todos os acessórios USB conectados:

Kotlin

    val manager = getSystemService(Context.USB_SERVICE) as UsbManager
    val accessoryList: Array<out UsbAccessory> = manager.accessoryList
    

Java

    UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
    UsbAccessory[] accessoryList = manager.getAccessoryList();
    

Observação: apenas um acessório conectado é aceito por vez.

Receber permissão para se comunicar com um acessório

Antes de se comunicar com o acessório USB, seu app precisa ter permissão dos usuários.

Observação: se seu app usar um filtro de intent para descobrir acessórios enquanto estão conectados, ele receberá automaticamente a permissão se o usuário permitir que seu app manipule o intent. Caso contrário, solicite a permissão explicitamente no seu app antes de se conectar ao acessório.

Solicitar explicitamente permissão pode ser necessário em algumas situações, como quando seu app enumera acessórios que já estão conectados e, em seguida, quer se comunicar com um deles. Você precisa verificar a permissão para acessar um acessório antes de tentar se comunicar com ele. Caso contrário, você receberá um erro de tempo de execução se o usuário tiver negado a permissão para acessar o acessório.

Para receber permissão explicitamente, primeiro crie um broadcast receiver. Esse receptor detecta o intent que recebe a transmissão quando você chama requestPermission(). A chamada para requestPermission() exibe uma caixa de diálogo para o usuário pedir permissão para se conectar ao acessório. O exemplo de código a seguir mostra como criar 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 accessory: UsbAccessory? = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY)

                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        accessory?.apply {
                            //call method to set up accessory communication
                        }
                    } else {
                        Log.d(TAG, "permission denied for accessory $accessory")
                    }
                }
            }
        }
    }
    

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) {
                    UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);

                    if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                        if(accessory != null){
                            //call method to set up accessory communication
                        }
                    }
                    else {
                        Log.d(TAG, "permission denied for accessory " + accessory);
                    }
                }
            }
        }
    };
    

Para registrar o broadcast receiver, coloque-o no 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), 0)
    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), 0);
    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 acessório, chame o método requestPermission():

Kotlin

    lateinit var accessory: UsbAccessory
    ...
    usbManager.requestPermission(accessory, permissionIntent)
    

Java

    UsbAccessory accessory;
    ...
    usbManager.requestPermission(accessory, permissionIntent);
    

Quando os usuários respondem à caixa de diálogo, o destinatário do broadcast receiver recebe o intent que contém o EXTRA_PERMISSION_GRANTED extra, que é um booleano que representa a resposta. Verifique se esse extra tem um valor "true" antes de se conectar ao acessório.

Comunicar-se com um acessório

Você pode se comunicar com o acessório usando UsbManager para ver um descritor de arquivo que pode configurar streams de entrada e saída para ler e gravar dados no descritor. Os streams representam os endpoints em massa de entrada e saída do acessório. Você precisa configurar a comunicação entre o dispositivo e o acessório em outra linha de execução para não bloquear a linha de execução de IU principal. O exemplo a seguir mostra como abrir um acessório para comunicação:

Kotlin

    private lateinit var accessory: UsbAccessory
    private var fileDescriptor: ParcelFileDescriptor? = null
    private var inputStream: FileInputStream? = null
    private var outputStream: FileOutputStream? = null
    ...

    private fun openAccessory() {
        Log.d(TAG, "openAccessory: $mAccessory")
        fileDescriptor = usbManager.openAccessory(accessory)
        fileDescriptor?.fileDescriptor?.also { fd ->
            inputStream = FileInputStream(fd)
            outputStream = FileOutputStream(fd)
            val thread = Thread(null, this, "AccessoryThread")
            thread.start()
        }
    }
    

Java

    UsbAccessory accessory;
    ParcelFileDescriptor fileDescriptor;
    FileInputStream inputStream;
    FileOutputStream outputStream;
    ...

    private void openAccessory() {
        Log.d(TAG, "openAccessory: " + accessory);
        fileDescriptor = usbManager.openAccessory(accessory);
        if (fileDescriptor != null) {
            FileDescriptor fd = fileDescriptor.getFileDescriptor();
            inputStream = new FileInputStream(fd);
            outputStream = new FileOutputStream(fd);
            Thread thread = new Thread(null, this, "AccessoryThread");
            thread.start();
        }
    }
    

No método run() da linha de execução, você pode ler e gravar no acessório usando os objetos FileInputStream ou FileOutputStream. Ao ler dados de um acessório com um objeto FileInputStream, verifique se o buffer usado é grande o suficiente para armazenar os dados do pacote USB. O protocolo de acessório Android é compatível com buffers de pacote de até 16.384 bytes. Assim, você pode optar por sempre declarar seu buffer como desse tamanho para simplificar.

Observação: em um nível inferior, os pacotes são de 64 bytes para acessórios USB de velocidade máxima e 512 bytes para acessórios USB de alta velocidade. O protocolo de acessório Android agrupa os pacotes das duas velocidades em pacote lógico para simplificar.

Para mais informações sobre o uso de linhas de execução no Android, consulte Processos e linhas de execução.

Encerrar a comunicação com um acessório

Quando terminar de se comunicar com um acessório ou se o acessório tiver sido desanexado, feche o descritor de arquivo que você abriu chamando close(). Para ouvir 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_ACCESSORY_DETACHED == intent.action) {
                val accessory: UsbAccessory? = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY)
                accessory?.apply {
                    // call your method that cleans up and closes communication with the accessory
                }
            }
        }
    }
    

Java

    BroadcastReceiver usbReceiver = new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();

            if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
                UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
                if (accessory != null) {
                    // call your method that cleans up and closes communication with the accessory
                }
            }
        }
    };
    

Criar o broadcast receiver dentro do app, e não o manifesto, permite que seu app manipule apenas eventos independentes enquanto está em execução. Dessa forma, os eventos independentes são enviados apenas para o app em execução no momento e não transmitidos para todos os apps.