Descripción general sobre el modo de accesorio USB

El modo de accesorio USB permite a los usuarios conectarse Hardware de host USB diseñado específicamente para dispositivos con Android. Los accesorios deben cumplir al protocolo de accesorios de Android que se describe en la documentación del kit de desarrollo de accesorios de Android. Esto permite que los dispositivos con Android que no pueden funcionar como host USB puedan seguir interactuando con el USB. hardware. Cuando un dispositivo con Android está en modo de accesorio USB, el USB de Android conectado actúa como host, suministra energía al bus USB y enumera los dispositivos conectados. Android 3.1 (nivel de API 12) admite el modo de accesorio USB y la función también tiene portabilidad a versiones anteriores Android 2.3.4 (nivel de API 10) para permitir la compatibilidad con una gama más amplia de dispositivos

Cómo elegir las API del accesorio USB adecuadas

Si bien las APIs del accesorio USB se introdujeron en la plataforma en Android 3.1, también se incluyen disponible en Android 2.3.4 con la biblioteca de complementos de las APIs de Google. Dado que estas APIs portabilidad a versiones anteriores con una biblioteca externa, hay dos paquetes que puedes importar para admitir modo de accesorio. Según los dispositivos con Android que quieras admitir, es posible que tengas que usa una por sobre la otra:

  • com.android.future.usb: Para admitir el modo de accesorio USB en Android 2.3.4, la Complemento de API de Google incluye las APIs de accesorios USB con portabilidad a versiones anteriores que se encuentran en esta espacio de nombres. Android 3.1 también admite la importación y llamada a las clases de este espacio de nombres para admitir aplicaciones escritas con la biblioteca de complementos. Esta biblioteca de complementos es un wrapper delgado alrededor de las APIs del accesorio de android.hardware.usb y no es compatible con el modo de host USB Si Si quieres admitir la gama más amplia de dispositivos compatibles con el modo de accesorio USB, usa el complemento biblioteca e importa este paquete. Cabe destacar que no todos los dispositivos con Android 2.3.4 son compatibles necesarios para admitir la función de accesorio USB. Cada fabricante de dispositivos decide si admitirás o no esta capacidad, por lo que debes declararla en tu manifiesto .
  • android.hardware.usb: Este espacio de nombres contiene las clases compatibles con USB. modo de accesorio en Android 3.1. Este paquete se incluye como parte de las APIs del framework, así que Android 3.1 admite el modo de accesorio USB sin necesidad de una biblioteca de complementos. Usar este paquete si solo te interesan los dispositivos Android 3.1 o más nuevos que tienen compatibilidad de hardware con USB modo de accesorio, que puedes declarar en tu archivo de manifiesto.

Cómo instalar la biblioteca de complementos de las API de Google

Si quieres instalar el complemento, puedes instalar la API 10 de Android de las APIs de Google. con SDK Manager. Consulta Instala las APIs de Google Complemento para obtener más información sobre cómo instalar la biblioteca de complementos.

Descripción general de la API

Debido a que la biblioteca de complementos es un wrapper para las APIs del framework, las clases que admiten la La función del accesorio USB son similares. Puedes utilizar la documentación de referencia para el android.hardware.usb incluso aunque uses la biblioteca de complementos.

Nota: Sin embargo, hay un uso menor diferencia entre la biblioteca de complementos y las APIs del framework que debes conocer.

En la siguiente tabla, se describen las clases que admiten las API del accesorio USB:

Clase Descripción
UsbManager Te permite enumerar los accesorios USB conectados y establecer una comunicación con ellos.
UsbAccessory Representa un accesorio USB y contiene métodos para acceder a su identificación. información.

Diferencias de uso entre las API de la biblioteca de complementos y las de la plataforma

Existen dos diferencias de uso entre la biblioteca de complementos de las APIs de Google y la plataforma APIs

Si utilizas la biblioteca de complementos, debes obtener el objeto UsbManager de la siguiente manera:

Kotlin

val manager = UsbManager.getInstance(this)

Java

UsbManager manager = UsbManager.getInstance(this);

Si no utilizas la biblioteca de complementos, debes obtener el objeto UsbManager de la siguiente manera:

Kotlin

val manager = getSystemService(Context.USB_SERVICE) as UsbManager

Java

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

Cuando filtras por un accesorio conectado con un filtro de intents, el objeto UsbAccessory se encuentra dentro del intent que se pasa a tu y mantener la integridad de su aplicación. Si utilizas la biblioteca de complementos, debes obtener el objeto UsbAccessory de la siguiente manera:

Kotlin

val accessory = UsbManager.getAccessory(intent)

Java

UsbAccessory accessory = UsbManager.getAccessory(intent);

Si no utilizas la biblioteca de complementos, debes obtener el objeto UsbAccessory de la siguiente manera:

Kotlin

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

Java

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

Requisitos del manifiesto de Android

La siguiente lista describe lo que necesitas agregar al archivo de manifiesto de tu aplicación antes de que funcionan con las APIs de accesorios USB. El archivo de manifiesto y recursos Los ejemplos muestran cómo declarar estos elementos:

  • Como no se garantiza que todos los dispositivos con Android admitan las API del accesorio USB, incluir un elemento <uses-feature> que declare que tu aplicación usa la función android.hardware.usb.accessory.
  • Si utilizas el biblioteca de complementos, agrega el elemento <uses-library> que especifica com.android.future.usb.accessory para la biblioteca.
  • Establece la versión mínima del SDK de la aplicación en el nivel de API 10 si usas la biblioteca de complementos. o 12 si usas el paquete android.hardware.usb.
  • Si deseas que tu aplicación reciba notificaciones sobre un accesorio USB conectado, especifica un Par de elementos <intent-filter> y <meta-data> para el android.hardware.usb.action.USB_ACCESSORY_ATTACHED en tu actividad principal. El elemento <meta-data> apunta a un archivo de recursos XML externo que declara información de identificación sobre el accesorio que quieres detectar.

    En el archivo de recursos XML, declara elementos <usb-accessory> para el accesorios que quieres filtrar. Cada <usb-accessory> puede tener la siguientes atributos:

    • manufacturer
    • model
    • version

    No se recomienda filtrar en version. Un accesorio o que el dispositivo no especifique siempre una cadena de versión (de forma intencional o no). Cuando tu app declara un atributo de versión para filtrar y el accesorio o dispositivo no especifica una cadena de versión, esto provoca una NullPointerException en versiones anteriores de Android. Este problema se solucionó en Android 12.

    Guarda el archivo de recursos en el directorio res/xml/. El nombre del archivo del recurso (sin la extensión .xml) debe ser la misma que especificaste en el archivo <meta-data>. El formato para el archivo de recursos XML también se muestra en el ejemplo que aparece a continuación.

Ejemplos de archivos de manifiesto y de recursos

A continuación, se muestra un ejemplo del manifiesto y de su archivo de recursos correspondiente:

<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>

En este caso, el siguiente archivo de recursos se debe guardar en res/xml/accessory_filter.xml y especifica que cualquier accesorio que tenga las se deben filtrar el modelo, el fabricante y la versión correspondientes. El accesorio envía estas atribuye el dispositivo con Android:

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

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

Cómo trabajar con accesorios

Cuando los usuarios conectan accesorios USB a un dispositivo con Android, el sistema Android puede determinar si a tu aplicación le interesa el accesorio conectado. Si es así, puedes configurar la comunicación con el accesorio, si así lo deseas. Para hacerlo, la aplicación debe realizar lo siguiente:

  1. Descubrir accesorios conectados usando un filtro de intents que filtra por accesorios eventos adjuntos o enumerando los accesorios conectados y buscando el apropiado.
  2. Pedir permiso al usuario para comunicarse con el accesorio, si aún no lo hizo obtenidos.
  3. Debe comunicarse con el accesorio leyendo y escribiendo datos en la interfaz adecuada. en los extremos.

Cómo descubrir un accesorio

Tu aplicación puede descubrir accesorios usando un filtro de intents para recibir notificaciones cuando el usuario conecta un accesorio o enumeran los accesorios que ya están conectados. Con un es útil si deseas que tu aplicación detecte automáticamente el accesorio deseado. Enumerar los accesorios conectados es útil si quieres obtener una lista de todos accesorios conectados o si tu aplicación no filtró por un intent.

Cómo usar un filtro de intents

Para que tu aplicación descubra un accesorio USB en particular, puedes especificar un filtro de intents para filtrar según el intent android.hardware.usb.action.USB_ACCESSORY_ATTACHED. A lo largo con este filtro de intents, debes especificar un archivo de recursos que especifique las propiedades de la unidad USB accesorio, como el fabricante, el modelo y la versión.

En el siguiente ejemplo, se muestra cómo declarar el filtro de intents:

<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>

En el siguiente ejemplo, se muestra cómo declarar el archivo de recursos correspondiente que especifica la Accesorios USB que te interesan:

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

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

En tu actividad, puedes obtener el UsbAccessory que representa el accesorio conectado del intent de la siguiente manera (con la biblioteca de complementos):

Kotlin

val accessory = UsbManager.getAccessory(intent)

Java

UsbAccessory accessory = UsbManager.getAccessory(intent);

O de este modo (con las API de la plataforma):

Kotlin

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

Java

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

Cómo enumerar accesorios

Puedes hacer que tu aplicación enumere los accesorios que se identificaron mientras tu aplicación cuando ejecutes la aplicación.

Usa el método getAccessoryList() para obtener un array de todos los accesorios 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();

Nota: Solo se admite un accesorio conectado en una vez.

Cómo obtener permiso para establecer una comunicación con un accesorio

Antes de comunicarse con el accesorio USB, tu aplicación debe tener el permiso de tu usuarios.

Nota: Si tu aplicación usa un filtro de intents para descubrir los accesorios cuando se conectan, recibe automáticamente permiso si el usuario permite que tu aplicación maneje el intent. De lo contrario, debes solicitar permiso explícitamente en la aplicación antes de conectarte al accesorio.

Puede ser necesario pedir permiso de forma explícita en algunas situaciones, como cuando tu enumera los accesorios que ya están conectados y con los que se quiere establecer comunicación uno. Debes verificar que se haya otorgado permiso para acceder a un accesorio antes de intentar comunicarte con él. De lo contrario, recibirás un error de tiempo de ejecución si el usuario rechazó el permiso para acceder accesorio.

A fin de obtener permiso de manera explícita, primero crea un receptor de emisión. Este receptor escucha el intent que se emite cuando llamas a requestPermission(). La llamada a requestPermission() muestra un diálogo al un usuario que solicita permiso para conectarse al accesorio. En el siguiente código de muestra, se muestra la manera de Crea el receptor de emisión:

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 el receptor de emisión, coloca esto en el método onCreate() en la actividad:

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 que aparezca el cuadro de diálogo que solicita permiso a los usuarios para conectarse al accesorio, llama al Método requestPermission():

Kotlin

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

Java

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

Cuando los usuarios responden al diálogo, tu receptor de emisión recibe el intent que contiene el EXTRA_PERMISSION_GRANTED adicional, que es un valor booleano que representan la respuesta. Verifica este extra para un valor verdadero antes de conectarte al accesorio.

Cómo establecer una comunicación con un accesorio

Puedes comunicarte con el accesorio usando el UsbManager para obtén un descriptor de archivos en el que puedas configurar transmisiones de entrada y salida para leer y escribir datos descriptor de la aplicación. Las transmisiones representan los extremos masivos de entrada y salida del accesorio. Debes establecer la comunicación entre el dispositivo y el accesorio en otro subproceso, de modo que no se bloquee el subproceso de IU principal. En el siguiente ejemplo, se muestra cómo abrir un accesorio y establecer una comunicación con él:

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

En el método run() del subproceso, puedes leer y escribir en el accesorio usando los objetos FileInputStream o FileOutputStream Durante la lectura datos de un accesorio con un objeto FileInputStream, asegúrate de que el búfer que que uses sea lo suficientemente grande como para almacenar los datos del paquete USB. El protocolo de accesorios de Android admite búferes de paquetes de hasta 16,384 bytes, por lo que puedes optar por declarar siempre que tu búfer sea de esta para que sea más simple.

Nota: En un nivel inferior, los paquetes son de 64 bytes para USB. accesorios a máxima velocidad y 512 bytes para accesorios USB de alta velocidad. El accesorio de Android Agrupa los paquetes de ambas velocidades en un solo paquete lógico para hacerlo más simple.

Para obtener más información sobre el uso de subprocesos en Android, consulta Procesos y Subprocesos.

Cómo finalizar la comunicación con un accesorio

Cuando hayas terminado de comunicarte con un accesorio, o si este se desconectó, cierra el descriptor de archivos que abriste llamando a close(). Para escuchar eventos de desconexión, crea un receptor de emisión como se indica aquí:

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

Crear el receptor de emisión dentro de la aplicación, y no del manifiesto, permite que tu que solo controle eventos de desconexión mientras se ejecuta. De esta forma, los eventos desvinculados Solo se envían a la aplicación que está actualmente en ejecución y no se transmiten a todas las aplicaciones.