Panoramica degli accessori USB

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 accessorie android.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> specificando com.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'intent android.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 errore NullPointerException. 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:

  1. 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.
  2. Chiedi all'utente l'autorizzazione per comunicare con l'accessorio, se non l'hai già ottenuto.
  3. 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.