Visão geral de acessórios USB

O modo de acessório USB permite que os usuários se conectem Hardware de host USB projetado especificamente para dispositivos com tecnologia Android. Os acessórios devem aderir ao protocolo de acessório Android descrito na documentação do Kit de desenvolvimento de acessórios Android. Isso permite que dispositivos com tecnologia Android que não podem atuar como host USB ainda interajam com USB ao hardware. Quando um dispositivo com tecnologia Android está no modo de acessório USB, o dispositivo USB Android conectado atua como host, fornece energia para o 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 tem backport para Android 2.3.4 (nível 10 da API) para oferecer suporte a uma variedade maior 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 são disponíveis no Android 2.3.4 usando a biblioteca de complementos das APIs do Google. Como essas APIs foram usando uma biblioteca externa, há dois pacotes que podem ser importados para oferecer suporte a USB modo acessório. Dependendo para quais dispositivos Android você quer oferecer suporte, talvez seja necessário use uma em vez da outra:

  • com.android.future.usb: para oferecer suporte ao modo de acessório USB no Android 2.3.4, a Complemento das APIs do Google inclui as APIs de acessórios USB com backport e elas estão contidas neste . O Android 3.1 também oferece suporte à importação e chamada de classes dentro desse namespace para oferecem suporte a aplicativos criados com a biblioteca de complementos. Esta biblioteca de complementos é um wrapper fino nas APIs de acessório android.hardware.usb e não oferece suporte ao modo host USB. Se Se quiser oferecer suporte à maior variedade de dispositivos com suporte ao modo de acessório USB, use o complemento e importar esse pacote. É importante observar que nem todos os dispositivos com Android 2.3.4 são necessário para oferecer suporte ao recurso de acessório USB. Cada fabricante de dispositivo decide oferecer ou não suporte para essa capacidade. É por isso que você precisa declará-la no manifesto. .
  • android.hardware.usb: esse namespace contém as classes com suporte a USB. modo acessório no Android 3.1. Esse pacote está incluído como parte das APIs do framework, portanto, O Android 3.1 é compatível com o modo de acessório USB sem o uso de uma biblioteca de complementos. Usar este pacote Se você só se preocupar com dispositivos Android 3.1 ou mais recentes que tenham suporte de hardware para USB modo acessório, que pode ser declarado no arquivo de manifesto.

Instalar a biblioteca de complementos das APIs do Google

Se você quiser instalar o complemento, instale as APIs do Google para Android API 10 com o SDK Manager. Consulte Como instalar as APIs do Google Complemento 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 estrutura, as classes compatíveis com a O recurso de acessório USB é semelhante. Você pode usar a documentação de referência do android.hardware.usb mesmo se estiver usando a biblioteca de complementos.

Observação:no entanto, há um pequeno uso diferença entre a biblioteca de complementos e as APIs de framework que você precisa conhecer.

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 o elemento de identificação informações imprecisas ou inadequadas.

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

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

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ê filtra um acessório conectado com um filtro de intent, o objeto UsbAccessory fica dentro da intent transmitida ao seu para o aplicativo. 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 aplicativo antes trabalhando com as APIs de acessórios USB. O arquivo de manifesto e recursos exemplos mostram como declarar esses itens:

  • Como nem todos os dispositivos com tecnologia Android são compatíveis com as APIs de acessórios USB, incluir um elemento <uses-feature> que declare que seu aplicativo usa o recurso android.hardware.usb.accessory.
  • Se você estiver usando o biblioteca de complementos, adicione o elemento <uses-library> especificando com.android.future.usb.accessory para a biblioteca.
  • Defina o SDK mínimo do aplicativo como o nível 10 da API se você estiver usando a biblioteca de complementos ou 12 se você estiver usando o pacote android.hardware.usb.
  • Se quiser que seu aplicativo seja notificado sobre um acessório USB conectado, especifique um Par de elementos <intent-filter> e <meta-data> para o 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 o acessórios que você quer filtrar. Cada <usb-accessory> pode ter seguintes atributos:

    • manufacturer
    • model
    • version

    A filtragem em version não é recomendada. Um acessório ou device nem sempre especifica uma string de versão (de maneira intencional ou não). Quando o app declara um atributo de versão para filtrar e o acessório ou dispositivo não especifica uma string de versão, isso faz com que uma NullPointerException seja exibida com as versões anteriores do Android. Esse problema foi corrigido no Android 12.

    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 também é mostrado em o 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 arquivo de recurso a seguir deve ser salvo res/xml/accessory_filter.xml e especifica que qualquer acessório que tenha o modelo, fabricante e versão correspondentes devem ser filtrados. O acessório envia essas atribui 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 determine se o aplicativo está interessado no acessório conectado. Nesse caso, é possível definir a comunicação com o acessório, se desejado. Para fazer isso, seu app precisa:

  1. Descubra acessórios conectados usando um filtro de intent que filtra por acessórios eventos anexados ou enumerando acessórios conectados e encontrando o apropriado.
  2. Pedir permissão ao usuário para se comunicar com o acessório, caso ainda não tenha feito isso. obtidos.
  3. Comunicar-se com o acessório lendo e gravando dados na interface adequada endpoints.

Descobrir um acessório

Seu aplicativo pode descobrir acessórios usando um filtro de intent para ser notificado quando o usuário conecta um acessório ou enumera acessórios que já estão conectados. Usar um o filtro de intent é útil quando você quer que seu aplicativo detecte automaticamente o acessório desejado. Enumerar acessórios conectados é útil se você quer receber uma lista de todos os acessórios conectados ou se o aplicativo não foi filtrado por uma intent.

Usar um filtro de intent

Para que seu aplicativo descubra um acessório USB específico, é possível especificar um filtro de intent. para filtrar a intent android.hardware.usb.action.USB_ACCESSORY_ATTACHED. Junto com esse filtro de intents, é preciso especificar um arquivo de recurso que especifique as propriedades do USB acessório, como fabricante, modelo e versão.

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 do seu 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 acessar o UsbAccessory que representa ao acessório anexado da intent como esta (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 o aplicativo enumere acessórios que se identificaram enquanto seu aplicativo está em execução.

Usar o método getAccessoryList() para gerar uma matriz com 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 : somente um acessório conectado é compatível com por vez.

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

Antes de se comunicar com o acessório USB, o aplicativo precisa ter permissão do usuários.

Observação: caso seu aplicativo use uma filtro de intent para descobrir acessórios conforme 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 no aplicativo antes de se conectar ao acessório.

Pedir permissão explicitamente pode ser necessário em algumas situações, como quando seu o aplicativo enumera os acessórios que já estão conectados e depois querem se comunicar com um. 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 negar a permissão para acessar o acessório.

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 solicita permissão para se conectar ao acessório. 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 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() no seu 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 permissão aos usuários para se conectar ao acessório, chame o método 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, 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 acessório.

Comunicar-se com um acessório

É possível se comunicar com o acessório usando o UsbManager para conseguir um descritor de arquivo em que possa configurar streams de entrada e saída para ler e gravar dados descritor. Os streams representam os endpoints em massa de entrada e saída do acessório. Você deve definir a comunicação entre o dispositivo e o acessório em outra linha de execução, para não bloquear o linha de execução de interface 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, é possível ler e gravar no acessório usando os objetos FileInputStream ou FileOutputStream. Durante a leitura dados de um acessório com um objeto FileInputStream, garanta que o buffer que que você usa é grande o suficiente para armazenar os dados do pacote USB. O protocolo de acessório Android oferece suporte buffers de pacote de até 16.384 bytes, ou seja, você pode optar por sempre declarar esse buffer de tamanho para simplificar.

Observação:em um nível inferior, os pacotes são de 64 bytes para USB. acessórios de alta velocidade e 512 bytes para acessórios USB de alta velocidade. O acessório do Android agrupa os pacotes das duas velocidades em um 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 ele tiver sido desconectado, 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 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.