La modalità accessorio USB consente agli utenti di collegare l'hardware host USB progettato appositamente per i dispositivi Android. Gli accessori devono rispettare il protocollo degli accessori Android descritto nella documentazione del Accessory Development Kit per Android. In questo modo i dispositivi Android che non possono fungere da host USB possono comunque interagire con l'hardware USB. Quando un dispositivo Android è in modalità accessorio USB, l'accessorio USB Android collegato funge da host, fornisce l'alimentazione al bus USB e elenca i dispositivi connessi. Android 3.1 (livello API 12) supporta la modalità accessori USB ed è inoltre eseguito il backporting della funzionalità su Android 2.3.4 (livello API 10) per consentire il supporto di una gamma più ampia di dispositivi.
Scegli le API accessorie USB giuste
Sebbene le API accessorie USB siano state introdotte sulla piattaforma in Android 3.1, sono disponibili anche in Android 2.3.4 utilizzando la libreria dei componenti aggiuntivi delle API di Google. Poiché queste API sono state sottoposte a backup mediante una libreria esterna, puoi importare due pacchetti per supportare la modalità accessorio USB. A seconda dei dispositivi con piattaforma Android che vuoi supportare, potresti dover usare uno piuttosto che l'altro:
com.android.future.usb
: per supportare la modalità accessori USB in Android 2.3.4, la libreria dei componenti aggiuntivi delle API di Google include le API accessorie USB sottoposte a backport e queste sono contenute in questo spazio dei nomi. Android 3.1 supporta anche l'importazione e la chiamata delle classi all'interno di questo spazio dei nomi per supportare le applicazioni scritte con la libreria dei componenti aggiuntivi. Questa libreria aggiuntiva è un wrapper sottile intorno alle API accessorieandroid.hardware.usb
e non supporta la modalità host USB. Se vuoi supportare la più ampia gamma di dispositivi che supportano la modalità accessorio USB, utilizza la libreria dei componenti aggiuntivi e importa questo pacchetto. È importante notare che non tutti i dispositivi Android 2.3.4 devono supportare la funzionalità accessori USB. Ogni singolo produttore del dispositivo decide se supportare o meno questa funzionalità, motivo per cui devi dichiararla nel file manifest.android.hardware.usb
: questo spazio dei nomi contiene le classi che supportano la modalità accessorio USB in Android 3.1. Questo pacchetto è incluso nelle API del framework, pertanto Android 3.1 supporta la modalità accessorio USB senza l'uso di una libreria di componenti aggiuntivi. Utilizza questo pacchetto se ti interessano solo i dispositivi Android 3.1 o versioni successive che supportano hardware per la modalità accessorio USB, che puoi dichiarare nel file manifest.
Installa la libreria dei componenti aggiuntivi delle API di Google
Per installare il componente aggiuntivo, installa il pacchetto API Android 10 delle API di Google con SDK Manager. Per ulteriori informazioni sull'installazione della libreria dei componenti aggiuntivi, consulta Installazione del componente aggiuntivo API di Google.
Panoramica dell'API
Poiché la libreria dei componenti aggiuntivi è un wrapper per le API del framework, le classi che supportano la funzionalità dell'accessorio USB sono simili. Puoi utilizzare la documentazione di riferimento per android.hardware.usb
anche se usi la libreria dei componenti aggiuntivi.
Nota: esiste, tuttavia, una piccola differenza di utilizzo tra le API della libreria dei componenti aggiuntivi e del framework che dovresti conoscere.
Nella tabella seguente vengono descritte le classi che supportano le API accessorie USB:
Classe | Descrizione |
---|---|
UsbManager |
Consente di enumerare e comunicare con gli accessori USB collegati. |
UsbAccessory |
Rappresenta un accessorio USB e contiene metodi per accedere alle relative informazioni di identificazione. |
Differenze nell'utilizzo delle API della libreria dei componenti aggiuntivi e delle API della piattaforma
Esistono due differenze di utilizzo tra l'uso della libreria dei componenti aggiuntivi delle API di Google e le API della piattaforma.
Se utilizzi la libreria dei componenti aggiuntivi, devi ottenere l'oggetto UsbManager
nel seguente modo:
Kotlin
val manager = UsbManager.getInstance(this)
Java
UsbManager manager = UsbManager.getInstance(this);
Se non utilizzi la libreria dei componenti aggiuntivi, devi ottenere l'oggetto UsbManager
nel seguente modo:
Kotlin
val manager = getSystemService(Context.USB_SERVICE) as UsbManager
Java
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
Quando filtri per un accessorio connesso con un filtro per intent, l'oggetto UsbAccessory
è contenuto all'interno dell'intent trasmesso all'applicazione. Se utilizzi la libreria dei componenti aggiuntivi, devi ottenere l'oggetto UsbAccessory
nel seguente modo:
Kotlin
val accessory = UsbManager.getAccessory(intent)
Java
UsbAccessory accessory = UsbManager.getAccessory(intent);
Se non utilizzi la libreria dei componenti aggiuntivi, devi ottenere l'oggetto UsbAccessory
nel seguente modo:
Kotlin
val accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) as UsbAccessory
Java
UsbAccessory accessory = (UsbAccessory) intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
Requisiti per i file manifest Android
Il seguente elenco descrive gli elementi da aggiungere al file manifest dell'applicazione prima di utilizzare le API accessorie USB. Gli esempi di file manifest e risorse mostrano come dichiarare questi elementi:
- Poiché non è garantito che tutti i dispositivi Android supportino le API accessorie USB, includi un elemento
<uses-feature>
che dichiari che l'applicazione utilizza la funzionalitàandroid.hardware.usb.accessory
. - Se utilizzi la
libreria dei componenti aggiuntivi,
aggiungi l'elemento
<uses-library>
specificandocom.android.future.usb.accessory
per la libreria. - Imposta l'SDK minimo dell'applicazione sul livello API 10 se utilizzi la libreria dei componenti aggiuntivi
o 12 se utilizzi il pacchetto
android.hardware.usb
. -
Se vuoi che la tua applicazione riceva una notifica relativa a un accessorio USB collegato, specifica una coppia di elementi
<intent-filter>
e<meta-data>
per l'intentandroid.hardware.usb.action.USB_ACCESSORY_ATTACHED
nell'attività principale. L'elemento<meta-data>
rimanda a un file di risorse XML esterno che dichiara le informazioni di identificazione sull'accessorio che vuoi rilevare.Nel file di risorse XML, dichiara gli elementi
<usb-accessory>
per gli accessori che vuoi filtrare. Ogni<usb-accessory>
può avere i seguenti attributi:manufacturer
model
version
Non è consigliabile applicare il filtro su
version
. Un accessorio o un dispositivo potrebbe non specificare sempre una stringa di versione (intenzionalmente o involontariamente). Se la tua app dichiara un attributo di versione in base al quale applicare il filtro e l'accessorio o il dispositivo non specifica una stringa di versione, nelle versioni precedenti di Android si verifica un erroreNullPointerException
. Questo problema è stato risolto in Android 12.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 nell'elemento<meta-data>
. Il formato del file di risorse XML è mostrato anche nell'esempio di seguito.
Esempi di file manifest e risorse
L'esempio seguente mostra un manifest di esempio e il file di risorse corrispondente:
<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>
In questo caso, il seguente file di risorse deve essere salvato in res/xml/accessory_filter.xml
e specificare che qualsiasi accessorio con modello, produttore e versione corrispondenti deve essere filtrato. L'accessorio invia i seguenti attributi al dispositivo Android:
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-accessory model="DemoKit" manufacturer="Google" version="1.0"/> </resources>
Utilizzare gli accessori
Quando gli utenti collegano accessori USB a un dispositivo Android, il sistema Android può determinare se la tua applicazione è interessata all'accessorio collegato. In tal caso, puoi configurare la comunicazione con l'accessorio, se vuoi. Per farlo, l'applicazione deve:
- Scopri gli accessori connessi utilizzando un filtro per intent che filtra gli eventi collegati agli accessori o enumerando gli accessori connessi e trovando quello appropriato.
- Chiedi all'utente l'autorizzazione per comunicare con l'accessorio, se non l'hai già ottenuto.
- Comunicazione con l'accessorio leggendo e scrivendo dati sugli endpoint dell'interfaccia appropriati.
Scopri un accessorio
L'applicazione può rilevare gli accessori utilizzando un filtro per intent che consente di ricevere notifiche quando l'utente connette un accessorio o enumerando gli accessori già connessi. L'utilizzo di un filtro per intent è utile se vuoi che l'applicazione rilevi automaticamente l'accessorio desiderato. L'elenco degli accessori connessi è utile se vuoi ottenere un elenco di tutti gli accessori connessi o se la tua applicazione non ha filtrato un intent.
Utilizzare un filtro per intent
Per fare in modo che la tua applicazione rilevi un particolare accessorio USB, puoi specificare un filtro per intent
da filtrare per l'intent android.hardware.usb.action.USB_ACCESSORY_ATTACHED
. Insieme a questo filtro per intent, devi specificare un file di risorse che specifichi le proprietà dell'accessorio USB, come produttore, modello e versione.
L'esempio seguente mostra come dichiarare il filtro per 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>
L'esempio seguente mostra come dichiarare il file di risorse corrispondente che specifica gli accessori USB che ti interessano:
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-accessory manufacturer="Google, Inc." model="DemoKit" version="1.0" /> </resources>
Nella tua attività, puoi ottenere il UsbAccessory
che rappresenta l'accessorio allegato dall'intent in questo modo (con la libreria dei componenti aggiuntivi):
Kotlin
val accessory = UsbManager.getAccessory(intent)
Java
UsbAccessory accessory = UsbManager.getAccessory(intent);
o simile (con le API della piattaforma):
Kotlin
val accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY) as UsbAccessory
Java
UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
Elenca accessori
Puoi fare in modo che la tua applicazione enumeri gli accessori che si sono identificati durante l'esecuzione dell'applicazione.
Utilizza il metodo getAccessoryList()
per ottenere un array di tutti gli accessori USB collegati:
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 : è supportato un solo accessorio connesso alla volta.
Ottenere l'autorizzazione per comunicare con un accessorio
Prima di comunicare con l'accessorio USB, l'applicazione deve avere l'autorizzazione degli utenti.
Nota:se l'applicazione utilizza un filtro per intent per rilevare gli accessori mentre sono connessi, riceverà automaticamente l'autorizzazione se l'utente consente alla tua applicazione di gestire l'intent. In caso contrario, devi richiedere esplicitamente l'autorizzazione nell'applicazione prima di connetterti all'accessorio.
In alcune situazioni potrebbe essere necessario chiedere esplicitamente l'autorizzazione, ad esempio quando l'applicazione enumera accessori già connessi e poi vuole comunicare con uno di questi dispositivi. Devi verificare l'autorizzazione per accedere a un accessorio prima di provare a comunicare con l'accessorio. In caso contrario, riceverai un errore di runtime se l'utente ha negato l'autorizzazione ad accedere all'accessorio.
Per ottenere esplicitamente l'autorizzazione, crea prima un ricevitore broadcast. Questo ricevitore ascolta l'intent che viene trasmesso quando chiami requestPermission()
. La chiamata a requestPermission()
mostra all'utente una finestra di dialogo che chiede l'autorizzazione a connettersi all'accessorio. Il seguente codice campione mostra come creare il ricevitore di trasmissione:
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); } } } } };
Per registrare il ricevitore di trasmissione, inserisci questo nel tuo metodo onCreate()
nella tua
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), 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);
Per visualizzare la finestra di dialogo che chiede agli utenti l'autorizzazione per la connessione all'accessorio, chiama il
metodo requestPermission()
:
Kotlin
lateinit var accessory: UsbAccessory ... usbManager.requestPermission(accessory, permissionIntent)
Java
UsbAccessory accessory; ... usbManager.requestPermission(accessory, permissionIntent);
Quando gli utenti rispondono alla finestra di dialogo, il ricevitore della trasmissione riceve l'intent che contiene l'elemento EXTRA_PERMISSION_GRANTED
aggiuntivo, che è un valore booleano che rappresenta la risposta. Seleziona questo valore aggiuntivo per il valore true prima di eseguire la connessione all'accessorio.
Comunicazione con un accessorio
Puoi comunicare con l'accessorio utilizzando UsbManager
per ottenere un descrittore di file in cui configurare flussi di input e di output per leggere e scrivere dati nel descrittore. I flussi rappresentano gli endpoint collettivi di input e di output dell'accessorio. Dovresti configurare la comunicazione tra il dispositivo e l'accessorio in un altro thread, in modo da non bloccare il thread dell'interfaccia utente principale. L'esempio seguente mostra come aprire un accessorio con cui comunicare:
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(); } }
Nel metodo run()
del thread, puoi leggere e scrivere sull'accessorio utilizzando
gli oggetti FileInputStream
o FileOutputStream
. Quando leggi i dati da un accessorio con un oggetto FileInputStream
, assicurati che il buffer utilizzato sia abbastanza grande per archiviare i dati del pacchetto USB. Il protocollo degli accessori Android supporta buffer di pacchetto fino a 16384 byte, quindi puoi scegliere di dichiarare sempre che il buffer sia di queste dimensioni per semplicità.
Nota: a un livello inferiore, i pacchetti sono di 64 byte per gli accessori USB ad alta velocità e 512 byte per gli accessori USB ad alta velocità. Per semplicità, il protocollo accessori Android raggruppa i pacchetti per entrambe le velocità in un unico pacchetto logico.
Per ulteriori informazioni sull'utilizzo dei thread in Android, consulta Processi e Thread.
Interrompere la comunicazione con un accessorio
Quando hai finito di comunicare con un accessorio o se quest'ultimo è stato scollegato, chiudi il descrittore del file che hai aperto chiamando close()
.
Per ascoltare eventi scollegati, crea un ricevitore di trasmissione come di seguito:
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 } } } };
La creazione del ricevitore di trasmissione all'interno dell'applicazione e non del manifest consente all'applicazione di gestire gli eventi scollegati solo mentre è in esecuzione. In questo modo, gli eventi scollegati vengono inviati solo all'applicazione attualmente in esecuzione e non trasmessi a tutte le applicazioni.