Quando il dispositivo Android è in modalità host USB, funge da host USB, alimenta il bus ed elenca i dispositivi USB connessi. La modalità host USB è supportata in Android 3.1 e versioni successive.
Panoramica dell'API
Prima di iniziare, è importante comprendere i corsi con cui devi lavorare. La
la seguente tabella descrive le API host USB nel pacchetto android.hardware.usb
.
Classe | Descrizione |
---|---|
UsbManager |
Consente di elencare e comunicare con i dispositivi USB connessi. |
UsbDevice |
Rappresenta un dispositivo USB collegato e contiene metodi per accedere alla sua identificazione informazioni, interfacce ed endpoint. |
UsbInterface |
Rappresenta un'interfaccia di un dispositivo USB che definisce un insieme di funzionalità per il dispositivo. Un dispositivo può avere una o più interfacce su cui comunicare. |
UsbEndpoint |
Rappresenta un endpoint dell'interfaccia, che è un canale di comunicazione per questa interfaccia. Un può avere uno o più endpoint e di solito ha endpoint di input e output comunicazione bidirezionale con il dispositivo. |
UsbDeviceConnection |
Rappresenta una connessione al dispositivo che trasferisce i dati sugli endpoint. Questo corso consente di inviare e ricevere dati in modo sincrono o asincrono. |
UsbRequest |
Rappresenta una richiesta asincrona di comunicare con un dispositivo mediante un UsbDeviceConnection . |
UsbConstants |
Definisce le costanti USB che corrispondono alle definizioni in linux/usb/ch9.h dell'ambiente Linux in un kernel. |
Nella maggior parte dei casi, devi utilizzare tutte queste classi (UsbRequest
è necessaria solo se esegui una comunicazione asincrona)
durante la comunicazione con un dispositivo USB. In generale, ottieni un UsbManager
per recuperare il UsbDevice
desiderato.
Una volta in possesso del dispositivo, dovrai trovare il UsbInterface
appropriato e i UsbEndpoint
di quel dispositivo
su cui comunicare. Una volta ottenuto l'endpoint corretto, apri un UsbDeviceConnection
per comunicare con il dispositivo USB.
Requisiti per il file manifest di Android
Nell'elenco seguente viene descritto ciò che è necessario aggiungere al file manifest della tua applicazione prima utilizzo delle API host USB:
- Poiché non è garantito che tutti i dispositivi Android supportino le API host USB,
includi un elemento
<uses-feature>
che dichiara che l'applicazione utilizza la funzionalitàandroid.hardware.usb.host
. - Imposta l'SDK minimo dell'applicazione sul livello API 12 o superiore. Le API host USB non sono presenti nei livelli API precedenti.
- Se desideri che la tua applicazione riceva notifiche relative a un dispositivo USB collegato, specifica
Coppia di elementi
<intent-filter>
e<meta-data>
perandroid.hardware.usb.action.USB_DEVICE_ATTACHED
intent nell'attività principale. La L'elemento<meta-data>
rimanda a un file di risorse XML esterno che dichiara informazioni identificative sul dispositivo che vuoi rilevare.Nel file di risorse XML, dichiara gli elementi
<usb-device>
per l'USB dispositivi che vuoi filtrare. Nell'elenco che segue vengono descritti gli attributi di<usb-device>
. In generale, utilizza il fornitore e l'ID prodotto se vuoi filtrare per un dispositivo specifico e utilizza classe, sottoclasse e protocollo se vuoi filtrare in base a un gruppo di dispositivi USB, quali fotocamere digitali o dispositivi di archiviazione di massa. Non puoi specificare nessuno o tutti questi attributi. Se non specifichi nessun attributo corrisponde a tutti i dispositivi USB, quindi esegui questa operazione se la tua applicazione lo richiede:vendor-id
product-id
class
subclass
protocol
(dispositivo o interfaccia)
Salva il file della risorsa nella directory
res/xml/
. Il nome del file della risorsa (senza l'estensione .xml) deve essere uguale a quello specificato nel Elemento<meta-data>
. Il formato del file di risorse XML è esempio riportato di seguito.
Esempi di file manifest e di risorse
L'esempio seguente mostra un manifest di esempio e il file di risorse corrispondente:
<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>
In questo caso, il seguente file di risorse deve essere salvato
res/xml/device_filter.xml
e specifica che qualsiasi dispositivo USB con
devono essere filtrati:
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-device vendor-id="1234" product-id="5678" class="255" subclass="66" protocol="1" /> </resources>
Lavora con i dispositivi
Quando gli utenti collegano i dispositivi USB a un dispositivo Android, il sistema Android è in grado di determinare se la tua applicazione è interessata al dispositivo connesso. In tal caso, puoi configurare comunicare con il dispositivo, se lo desideri. Per farlo, la tua applicazione deve:
- Individuare i dispositivi USB connessi utilizzando un filtro per intent per ricevere notifiche quando l'utente collega un dispositivo USB o elenca i dispositivi USB già connessi.
- Chiedi all'utente l'autorizzazione a connettersi al dispositivo USB, se non l'hai già ottenuta.
- Comunica con il dispositivo USB leggendo e scrivendo i dati nell'interfaccia appropriata endpoint.
Scopri un dispositivo
La tua applicazione può rilevare i dispositivi USB utilizzando un filtro per intent per ricevere una notifica quando l'utente connette un dispositivo o indica i dispositivi USB già connessi. L'utilizzo di un è utile se vuoi che la tua applicazione rilevi automaticamente dispositivo desiderato. L'enumerazione dei dispositivi USB connessi è utile se vuoi ottenere un elenco di tutti dispositivi connessi o se la tua applicazione non ha filtrato un intent.
Utilizzare un filtro per intent
Per fare in modo che l'applicazione rilevi un determinato dispositivo USB, puoi specificare un filtro per intent
filtro per l'intent android.hardware.usb.action.USB_DEVICE_ATTACHED
. Insieme a
questo filtro per intent, devi specificare un file di risorse che specifichi le proprietà dell'interfaccia
dispositivo, come l'ID prodotto e l'ID del fornitore. Quando gli utenti connettono un dispositivo che corrisponde al tuo
di Google, il sistema visualizza una finestra di dialogo che chiede se desiderano avviare l'applicazione.
Se gli utenti accettano, la tua applicazione avrà automaticamente l'autorizzazione ad accedere al dispositivo finché il
dispositivo disconnesso.
L'esempio seguente mostra come dichiarare il filtro per 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>
L'esempio seguente mostra come dichiarare il file di risorse corrispondente che specifica Dispositivi USB che ti interessano:
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-device vendor-id="1234" product-id="5678" /> </resources>
Nella tua attività, puoi ottenere il UsbDevice
che rappresenta
il dispositivo collegato per questo scopo:
Kotlin
val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
Java
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
Enumera i dispositivi
Se la tua applicazione è interessata a controllare tutti i dispositivi USB attualmente collegati
mentre l'applicazione è in esecuzione, può enumerare i dispositivi sul bus. Utilizza il metodo getDeviceList()
per ottenere una mappa hash di tutti
i dispositivi USB collegati. La mappa hash è digitata in base al nome del dispositivo USB, se desideri
ottenere un dispositivo dalla mappa.
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 vuoi, puoi anche ottenere un iteratore dalla mappa hash ed elaborare ogni singolo dispositivo per 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 }
Ottenere l'autorizzazione per comunicare con un dispositivo
Prima di comunicare con il dispositivo USB, l'applicazione deve avere l'autorizzazione del tuo utenti.
Nota: se la tua applicazione utilizza una filtro per intent per rilevare i dispositivi USB quando sono connessi, riceve automaticamente se l'utente consente alla tua applicazione di gestire l'intent. In caso contrario, devi richiedere esplicitamente nella tua applicazione prima di connetterti al dispositivo.
Potrebbe essere necessario richiedere esplicitamente l'autorizzazione in alcune situazioni, ad esempio quando che elenca i dispositivi USB che sono già connessi e che successivamente vuole comunicare uno. Prima di provare a comunicare con un dispositivo, devi verificare l'autorizzazione di accesso. Se no, riceverai un errore di runtime se l'utente ha negato l'autorizzazione ad accedere al dispositivo.
Per ottenere l'autorizzazione esplicitamente, crea prima un broadcast receiver. Questo ricevitore è in ascolto
l'intent che viene trasmesso quando chiami requestPermission()
. La chiamata a requestPermission()
mostra una finestra di dialogo al
utente che chiede l'autorizzazione per connettersi al dispositivo. Il seguente codice di esempio mostra come
crea il 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); } } } } };
Per registrare il broadcast receiver, aggiungi questo nel metodo onCreate()
nel tuo
attività:
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);
Per visualizzare la finestra di dialogo che chiede agli utenti l'autorizzazione per connettersi al dispositivo, chiama il metodo requestPermission()
:
Kotlin
lateinit var device: UsbDevice ... usbManager.requestPermission(device, permissionIntent)
Java
UsbDevice device; ... usbManager.requestPermission(device, permissionIntent);
Quando gli utenti rispondono alla finestra di dialogo, il tuo broadcast receiver riceve l'intent che contiene la
EXTRA_PERMISSION_GRANTED
extra, che è un valore booleano
che rappresentano la risposta. Controlla questo extra per verificare se è presente il valore true prima di eseguire la connessione al
dispositivo.
Comunicare con un dispositivo
La comunicazione con un dispositivo USB può essere sincrona o asincrona. In entrambi i casi,
dovresti creare un nuovo thread su cui eseguire tutte le trasmissioni dati, in modo da non bloccare
Thread UI. Per configurare correttamente la comunicazione con un dispositivo, devi ottenere l'appropriata
UsbInterface
e UsbEndpoint
del
dispositivo su cui vuoi comunicare e inviare richieste su questo endpoint con un UsbDeviceConnection
. In generale, il codice deve:
- Controlla gli attributi di un oggetto
UsbDevice
, come ID prodotto, dell'ID del fornitore o della classe del dispositivo per capire se vuoi comunicare dispositivo. - Quando hai la certezza di voler comunicare con il dispositivo, trova l'app appropriata
UsbInterface
che vuoi utilizzare per comunicare insieme alUsbEndpoint
appropriata dell'interfaccia. Le interfacce possono avere uno o più endpoint e di solito hanno un endpoint di input e output per comunicazione. - Quando trovi l'endpoint corretto, apri un
UsbDeviceConnection
su quell'endpoint. - Fornisci i dati che vuoi trasmettere sull'endpoint con il metodo
bulkTransfer()
ocontrolTransfer()
. Dovresti esegui questo passaggio in un altro thread per evitare il blocco del thread dell'interfaccia utente principale. Per ulteriori informazioni informazioni sull'utilizzo dei thread in Android, consulta l'articolo Processi e Thread.
Il seguente snippet di codice rappresenta un modo semplice per eseguire un trasferimento di dati sincrono. Il tuo codice devono avere più logica per trovare correttamente l'interfaccia e gli endpoint corretti su cui comunicare Inoltre, deve eseguire il trasferimento di dati in un thread diverso rispetto al thread principale dell'interfaccia utente:
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
Per inviare i dati in modo asincrono, utilizza la classe UsbRequest
a initialize
e queue
una richiesta asincrona, quindi attendi il risultato
con requestWait()
.
Interruzione della comunicazione con un dispositivo
Quando hai finito di comunicare con un dispositivo o se il dispositivo è stato scollegato, chiudi UsbInterface
e UsbDeviceConnection
chiamata a releaseInterface()
e
close()
. Per rimanere in ascolto degli eventi scollegati,
crea un broadcast receiver come quello riportato di seguito:
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 } } } };
La creazione del broadcast receiver all'interno dell'applicazione, non del manifest, consente per gestire gli eventi scollegati solo mentre è in esecuzione. In questo modo, gli eventi scollegati vengono inviate solo all'applicazione attualmente in esecuzione e non trasmesse a tutte le applicazioni.