Cuando tu dispositivo con Android está en modo de host USB, funciona como host USB, alimenta el bus y enumera los dispositivos USB conectados. El modo de host USB es compatible con Android 3.1 y versiones posteriores.
Descripción general de la API
Antes de comenzar, es importante comprender las clases con las que necesitas trabajar. El
En la siguiente tabla, se describen las APIs del host USB en el paquete android.hardware.usb
.
Clase | Descripción |
---|---|
UsbManager |
Te permite enumerar los dispositivos USB conectados y establecer una comunicación con ellos. |
UsbDevice |
Representa un dispositivo USB conectado y contiene métodos para acceder a su identificación información, interfaces y extremos. |
UsbInterface |
Representa una interfaz de un dispositivo USB, que define un conjunto de funcionalidades para la dispositivo. Un dispositivo puede tener una o más interfaces para comunicarse. |
UsbEndpoint |
Representa un extremo de la interfaz, que es un canal de comunicación para esta. Los puede tener uno o más extremos, y generalmente tiene extremos de entrada y salida para y bidireccional con el dispositivo. |
UsbDeviceConnection |
Representa una conexión con el dispositivo, que transfiere datos en los extremos. Esta clase te permite enviar y recibir datos de forma síncrona o asíncrona. |
UsbRequest |
Representa una solicitud asíncrona para comunicarse con un dispositivo a través de una UsbDeviceConnection . |
UsbConstants |
Define las constantes USB que corresponden a las definiciones en linux/usb/ch9.h de Linux kernel. |
En la mayoría de las situaciones, debes usar todas estas clases (UsbRequest
solo es necesario si realizas una comunicación asíncrona).
cuando te comuniques con un dispositivo USB. En general, obtienes un UsbManager
para recuperar el UsbDevice
deseado.
Una vez que tengas el dispositivo, deberás encontrar el UsbInterface
adecuado y el UsbEndpoint
de ese
interfaz de usuario para comunicarnos. Una vez que obtengas el extremo correcto, abre una UsbDeviceConnection
para comunicarte con el dispositivo USB.
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 del host USB:
- Como no se garantiza que todos los dispositivos con Android
admitan las APIs del host USB,
incluir un elemento
<uses-feature>
que declare que tu aplicación usa la funciónandroid.hardware.usb.host
. - Establece la versión mínima del SDK de la aplicación en API nivel 12 o posterior. Las APIs del host USB no están en niveles de API anteriores.
- Si deseas que tu aplicación reciba notificaciones sobre un dispositivo USB conectado, especifica un
Par de elementos
<intent-filter>
y<meta-data>
para elandroid.hardware.usb.action.USB_DEVICE_ATTACHED
en tu actividad principal. El El elemento<meta-data>
apunta a un archivo de recursos XML externo que declara información de identificación sobre el dispositivo que deseas detectar.En el archivo de recursos XML, declara elementos
<usb-device>
para la unidad USB. dispositivos que quieres filtrar. La siguiente lista describe los atributos de<usb-device>
En general, usa el ID del producto y el proveedor si deseas filtrar para un dispositivo específico y usa la clase, la subclase y el protocolo si deseas filtrar por grupo de dispositivos USB, como dispositivos de almacenamiento masivo o cámaras digitales. Puedes especificar ninguno o todos estos atributos. Si no se especifica ningún atributo, se recomienda hacerlo con todos los dispositivos USB. si tu aplicación lo requiere:vendor-id
product-id
class
subclass
protocol
(dispositivo o interfaz)
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 del archivo de recursos XML se encuentra en el ejemplo 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.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>
En este caso, el siguiente archivo de recursos se debe guardar en
res/xml/device_filter.xml
y especifica que cualquier dispositivo USB con el valor
atributos deben filtrarse:
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-device vendor-id="1234" product-id="5678" class="255" subclass="66" protocol="1" /> </resources>
Cómo trabajar con dispositivos
Cuando los usuarios conectan dispositivos USB a un dispositivo con Android, el sistema Android puede determinar si tu aplicación está interesada en el dispositivo conectado. Si es así, puedes configurar y comunicación con el dispositivo, si así lo deseas. Para hacerlo, la aplicación debe realizar lo siguiente:
- Descubrir dispositivos USB conectados usando un filtro de intents para recibir notificaciones cuando el usuario conecta un dispositivo USB o enumera los dispositivos USB que ya están conectados.
- Si aún no lo tiene, debe solicitar permiso al usuario para conectarse al dispositivo USB.
- Debe comunicarse con el dispositivo USB leyendo y escribiendo datos en la interfaz adecuada. en los extremos.
Cómo descubrir un dispositivo
Tu aplicación puede descubrir dispositivos USB utilizando un filtro de intents para recibir notificaciones cuando el usuario conecta un dispositivo o enumerando los dispositivos USB que ya están conectados. Con un es útil si deseas que tu aplicación detecte automáticamente dispositivo deseado. La enumeración de los dispositivos USB conectados es útil si quieres obtener una lista de todos 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 dispositivo USB en particular, puedes especificar un filtro de intents para
filtro para el intent android.hardware.usb.action.USB_DEVICE_ATTACHED
. Junto con
este filtro de intents, debes especificar un archivo de recursos que especifique las propiedades de la interfaz USB
dispositivo, como el ID del producto y del proveedor. Cuando los usuarios conectan un dispositivo que coincide con el tuyo
filtro, el sistema les presenta un diálogo que les pregunta si desean iniciar su aplicación.
Si los usuarios aceptan, tu aplicación automáticamente tiene permiso para acceder al dispositivo hasta que
dispositivo está desconectado.
En el siguiente ejemplo, se muestra cómo declarar el filtro de intents:
<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>
En el siguiente ejemplo, se muestra cómo declarar el archivo de recursos correspondiente que especifica la Dispositivos USB que te interesan:
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-device vendor-id="1234" product-id="5678" /> </resources>
En tu actividad, puedes obtener el UsbDevice
que representa
el dispositivo conectado del intent de la siguiente manera:
Kotlin
val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
Java
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
Cómo enumerar dispositivos
Si tu aplicación está interesada en inspeccionar todos los dispositivos USB conectados actualmente
mientras se ejecuta tu aplicación, puede enumerar los dispositivos en el bus. Usa el método getDeviceList()
para obtener un mapa hash de todos los elementos
los dispositivos USB que están conectados. Si quieres
obtener un dispositivo desde el 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");
Si lo deseas, también puedes obtener un iterador del mapa hash y procesar cada uno de los dispositivos por uno:
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 }
Cómo obtener permiso para comunicarse con un dispositivo
Antes de comunicarse con el dispositivo USB, tu aplicación debe tener permiso de tu usuarios.
Nota: Si tu aplicación usa un filtro de intents para detectar dispositivos USB cuando se conectan, recibe automáticamente permiso si el usuario permite que tu aplicación maneje el intent. De lo contrario, debes solicitar explícitamente en tu aplicación antes de conectarte al dispositivo.
Puede ser necesario pedir permiso de forma explícita en algunas situaciones, como cuando tu En la aplicación, se enumeran los dispositivos USB que ya están conectados y con los que se desea comunicarse. uno. Debes verificar que se haya otorgado permiso para acceder a un dispositivo antes de intentar comunicarte con él. Si de lo contrario, recibirás un error de tiempo de ejecución si el usuario rechazó el permiso para acceder al dispositivo.
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 dispositivo. 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 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 el receptor de emisión, agrega esto al método onCreate()
en tu
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), 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 que aparezca el diálogo en el que se solicita permiso de acceso al dispositivo, llama al método requestPermission()
:
Kotlin
lateinit var device: UsbDevice ... usbManager.requestPermission(device, permissionIntent)
Java
UsbDevice device; ... usbManager.requestPermission(device, 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
dispositivo.
Cómo comunicarse con un dispositivo
La comunicación con un dispositivo USB puede ser síncrona o asíncrona. En cualquier caso,
debes crear un nuevo subproceso en el que se lleven a cabo todas las transmisiones de datos, de modo que no bloquees el
subproceso de IU. Para configurar la comunicación con un dispositivo correctamente, debes obtener las
UsbInterface
y UsbEndpoint
de las
dispositivo en el que quieras comunicarte y enviar solicitudes en este extremo con un UsbDeviceConnection
. En general, debes realizar lo siguiente:
- Verifica los atributos de un objeto
UsbDevice
, como el ID del producto, ID de proveedor o clase de dispositivo para averiguar si deseas comunicarte o no con el dispositivo. - Cuando sepas con certeza que deseas comunicarte con el dispositivo, busca la
UsbInterface
que desees usar para comunicarte con el laUsbEndpoint
apropiada de esa interfaz. Las interfaces pueden tener o más endpoints, y comúnmente tendrá un extremo de entrada y uno de salida para la comunicación. - Cuando encuentres el extremo correcto, abre un
UsbDeviceConnection
. en ese extremo. - Proporciona los datos que deseas transmitir en el extremo con el método
bulkTransfer()
ocontrolTransfer()
. Deberías realiza este paso en otro subproceso para evitar que se bloquee el subproceso de IU principal. Para ver más información sobre el uso de subprocesos en Android, consulta Procesos y Subprocesos.
El siguiente fragmento de código es una forma trivial de realizar una transferencia de datos síncrona. Tu código deberían tener más lógica para encontrar correctamente la interfaz y los extremos correctos para comunicarse Además, debes realizar las transferencias de datos en un subproceso diferente al subproceso de IU 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
Si quieres enviar datos de forma asíncrona, usa la clase UsbRequest
para initialize
y queue
una solicitud asíncrona y, luego, espera el resultado.
con requestWait()
.
Cómo finalizar la comunicación con un dispositivo
Cuando hayas terminado de comunicarte con un dispositivo, o si este se desconectó, cierra UsbInterface
y UsbDeviceConnection
.
llamando a releaseInterface()
y
close()
Para escuchar eventos de desconexión,
Crea un receptor de emisión como se muestra a continuación:
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 } } } };
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.