USB-Zubehör – Übersicht

Im USB-Zubehörmodus können Nutzer USB-Hosthardware verbinden, die speziell für Android-Geräte entwickelt wurde. Das Zubehör muss dem Android-Zubehörprotokoll entsprechen, das in der Dokumentation zum Android Accessory Development Kit beschrieben ist. So können Android-Geräte, die nicht als USB-Host fungieren, weiterhin mit USB-Hardware interagieren. Wenn sich ein Android-Gerät im USB-Zubehörmodus befindet, fungiert das angeschlossene Android-USB-Zubehör als Host, versorgt den USB-Bus mit Strom und listet die verbundenen Geräte auf. Android 3.1 (API-Level 12) unterstützt den USB-Zubehörmodus. Die Funktion wurde auch zu Android 2.3.4 (API-Level 10) zurückportiert, um eine größere Anzahl von Geräten unterstützen zu können.

Die richtigen USB-Zubehör-APIs auswählen

Die USB-Zubehör-APIs wurden zwar in Android 3.1 auf der Plattform eingeführt, sind aber auch in Android 2.3.4 über die Add-on-Bibliothek für Google APIs verfügbar. Da diese APIs mithilfe einer externen Bibliothek rückportiert wurden, können Sie zwei Pakete importieren, um den USB-Zubehörmodus zu unterstützen. Je nachdem, welche Android-Geräte du unterstützen möchtest, musst du möglicherweise eines der folgenden Geräte verwenden:

  • com.android.future.usb: Zur Unterstützung des USB-Zubehörmodus in Android 2.3.4 enthält die Google APIs-Add-on-Bibliothek die zurückportierten USB-Zubehör-APIs und sie sind in diesem Namespace enthalten. Android 3.1 unterstützt auch das Importieren und Aufrufen der Klassen in diesem Namespace, um Anwendungen zu unterstützen, die mit der Add-on-Bibliothek geschrieben wurden. Diese Add-on-Bibliothek ist ein Thin Wrapper um die android.hardware.usb-Zubehör-APIs und unterstützt den USB-Hostmodus nicht. Wenn Sie möglichst viele Geräte unterstützen möchten, die den USB-Zubehörmodus unterstützen, verwenden Sie die Add-on-Bibliothek und importieren Sie dieses Paket. Es ist wichtig zu beachten, dass nicht alle Geräte mit Android 2.3.4 die USB-Zubehörfunktion unterstützen. Jeder Gerätehersteller entscheidet, ob diese Funktion unterstützt wird oder nicht. Deshalb musst du sie in deiner Manifestdatei deklarieren.
  • android.hardware.usb: Dieser Namespace enthält die Klassen, die den USB-Zubehörmodus in Android 3.1 unterstützen. Da dieses Paket Teil der Framework-APIs ist, unterstützt Android 3.1 den USB-Zubehörmodus ohne die Verwendung einer Add-on-Bibliothek. Verwende dieses Paket, wenn du nur Geräte mit Android 3.1 oder höher mit Hardwareunterstützung für den USB-Zubehörmodus verwendest. Dies kannst du in deiner Manifestdatei angeben.

Add-on-Bibliothek für Google APIs installieren

Wenn Sie das Add-on installieren möchten, können Sie das Google APIs Android API 10-Paket mit dem SDK-Manager installieren. Weitere Informationen zum Installieren der Add-on-Bibliothek finden Sie unter Google APIs-Add-on installieren.

API-Übersicht

Da die Add-on-Bibliothek ein Wrapper für die Framework-APIs ist, sind die Klassen, die die USB-Zubehörfunktion unterstützen, ähnlich. Sie können die Referenzdokumentation für android.hardware.usb auch dann verwenden, wenn Sie die Add-on-Bibliothek verwenden.

Hinweis:Zwischen der Add-on-Bibliothek und den Framework-APIs gibt es jedoch einen geringen Nutzungsunterschied zwischen der Add-on-Bibliothek und den Framework-APIs, die Sie kennen sollten.

In der folgenden Tabelle werden die Klassen beschrieben, die die USB-Zubehör-APIs unterstützen:

Klasse Beschreibung
UsbManager Ermöglicht Ihnen, angeschlossenes USB-Zubehör aufzuzählen und zu kommunizieren.
UsbAccessory Stellt ein USB-Zubehör dar und enthält Methoden für den Zugriff auf die identifizierenden Informationen.

Nutzungsunterschiede zwischen der Add-on-Bibliothek und den Plattform-APIs

Bei der Verwendung der Google APIs-Add-on-Bibliothek und der Plattform-APIs gibt es zwei Nutzungsunterschiede.

Wenn Sie die Add-on-Bibliothek verwenden, müssen Sie das UsbManager-Objekt so abrufen:

Kotlin

val manager = UsbManager.getInstance(this)

Java

UsbManager manager = UsbManager.getInstance(this);

Wenn Sie die Add-on-Bibliothek nicht verwenden, müssen Sie das UsbManager-Objekt so abrufen:

Kotlin

val manager = getSystemService(Context.USB_SERVICE) as UsbManager

Java

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

Wenn Sie mit einem Intent-Filter nach einem verbundenen Zubehör filtern, ist das Objekt UsbAccessory im Intent enthalten, der an Ihre Anwendung übergeben wird. Wenn Sie die Add-on-Bibliothek verwenden, müssen Sie das UsbAccessory-Objekt so abrufen:

Kotlin

val accessory = UsbManager.getAccessory(intent)

Java

UsbAccessory accessory = UsbManager.getAccessory(intent);

Wenn Sie die Add-on-Bibliothek nicht verwenden, müssen Sie das UsbAccessory-Objekt so abrufen:

Kotlin

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

Java

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

Anforderungen an das Android-Manifest

In der folgenden Liste wird beschrieben, was du der Manifestdatei deiner App hinzufügen musst, bevor du mit den USB Access APIs arbeitest. Die Beispiele für Manifest- und Ressourcendateien zeigen, wie diese Elemente deklariert werden:

  • Da nicht alle Android-Geräte die USB-Zubehör-APIs unterstützen, musst du ein <uses-feature>-Element einfügen, das deklariert, dass deine App die Funktion android.hardware.usb.accessory verwendet.
  • Wenn Sie die Add-on-Bibliothek verwenden, fügen Sie das Element <uses-library> hinzu und geben Sie com.android.future.usb.accessory für die Bibliothek an.
  • Legen Sie das Mindest-SDK der Anwendung auf API-Level 10 fest, wenn Sie die Add-on-Bibliothek verwenden, oder auf 12, wenn Sie das Paket android.hardware.usb verwenden.
  • Wenn du möchtest, dass deine Anwendung über angeschlossenes USB-Zubehör benachrichtigt wird, gib ein <intent-filter>- und ein <meta-data>-Elementpaar für den Intent android.hardware.usb.action.USB_ACCESSORY_ATTACHED in deiner Hauptaktivität an. Das Element <meta-data> verweist auf eine externe XML-Ressourcendatei, in der Identifizierungsinformationen für das Zubehör angegeben sind, das Sie erkennen möchten.

    Deklarieren Sie in der XML-Ressourcendatei <usb-accessory>-Elemente für das Zubehör, das Sie filtern möchten. Jeder <usb-accessory> kann die folgenden Attribute haben:

    • manufacturer
    • model
    • version

    Filtern nach version wird nicht empfohlen. Ein Zubehör- oder Gerät gibt nicht immer einen Versionsstring an (absichtlich oder unabsichtlich). Wenn deine App ein Versionsattribut für die Filterung deklariert und das Zubehör oder Gerät keinen Versionsstring angibt, führt dies bei früheren Android-Versionen zu einem NullPointerException. Dieses Problem wurde in Android 12 behoben.

    Speichern Sie die Ressourcendatei im Verzeichnis res/xml/. Der Name der Ressourcendatei (ohne die Erweiterung „.xml“) muss mit dem übereinstimmen, den Sie im Element <meta-data> angegeben haben. Das Format für die XML-Ressourcendatei wird auch im Beispiel unten gezeigt.

Beispiele für Manifest- und Ressourcendateien

Das folgende Beispiel zeigt ein Beispielmanifest und die entsprechende Ressourcendatei:

<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 diesem Fall sollte die folgende Ressourcendatei in res/xml/accessory_filter.xml gespeichert werden. Sie gibt an, dass jedes Zubehör mit dem entsprechenden Modell, Hersteller und der entsprechenden Version gefiltert werden soll. Das Zubehör sendet die folgenden Attribute an das Android-Gerät:

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

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

Zubehör verwenden

Wenn Nutzer USB-Zubehör an ein Android-Gerät anschließen, kann das Android-System feststellen, ob das verbundene Zubehör für Ihre App interessant ist. In diesem Fall können Sie bei Bedarf die Kommunikation mit dem Zubehör einrichten. Dazu muss Ihre Anwendung folgende Voraussetzungen erfüllen:

  1. Verbundenes Zubehör lässt sich mit einem Intent-Filter ermitteln, der nach angehängten Ereignissen filtert. Alternativ kannst du verbundenes Zubehör auflisten und das passende Zubehör finden.
  2. Bitte den Nutzer um die Erlaubnis, mit dem Zubehör zu kommunizieren, falls du dies nicht bereits erhalten hast.
  3. Kommunizieren Sie mit dem Zubehör, indem Sie Daten auf den entsprechenden Schnittstellen-Endpunkten lesen und schreiben.

Zubehör entdecken

Ihre Anwendung kann Zubehör erkennen, indem sie entweder einen Intent-Filter verwendet, um benachrichtigt wird, wenn der Nutzer ein Zubehör verbindet, oder das bereits verbundene Zubehör aufzählt. Die Verwendung eines Intent-Filters ist nützlich, wenn Ihre Anwendung das gewünschte Zubehör automatisch erkennen soll. Das Auflisten von verbundenem Zubehör ist nützlich, wenn Sie eine Liste aller verbundenen Zubehörteile erhalten möchten oder wenn Ihre App nicht nach einem Intent gefiltert hat.

Intent-Filter verwenden

Damit Ihre Anwendung ein bestimmtes USB-Zubehör erkennt, können Sie einen Intent-Filter angeben, um nach dem Intent android.hardware.usb.action.USB_ACCESSORY_ATTACHED zu filtern. Zusammen mit diesem Intent-Filter musst du eine Ressourcendatei angeben, in der Eigenschaften des USB-Zubehörs angegeben werden, z. B. Hersteller, Modell und Version.

Das folgende Beispiel zeigt, wie der Intent-Filter deklariert wird:

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

Das folgende Beispiel zeigt, wie Sie die entsprechende Ressourcendatei deklarieren, in der das gewünschte USB-Zubehör angegeben ist:

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

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

In Ihrer Aktivität können Sie das UsbAccessory, das das angehängte Zubehör darstellt, vom Intent wie folgt abrufen (mit der Add-on-Bibliothek):

Kotlin

val accessory = UsbManager.getAccessory(intent)

Java

UsbAccessory accessory = UsbManager.getAccessory(intent);

oder so (mit den Plattform-APIs):

Kotlin

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

Java

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

Zubehör auflisten

Sie können festlegen, dass Ihre Anwendung Zubehör aufzählt, das sich während der Ausführung identifiziert hat.

Verwenden Sie die Methode getAccessoryList(), um ein Array mit allen verbundenen USB-Zubehör abzurufen:

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

Hinweis: Es wird jeweils nur ein verbundenes Zubehör unterstützt.

Berechtigung zur Kommunikation mit Zubehör einholen

Damit Sie mit dem USB-Zubehör kommunizieren können, muss die Anwendung über die entsprechende Berechtigung der Nutzer verfügen.

Hinweis:Wenn Ihre Anwendung einen Intent-Filter verwendet, um verbundenes Zubehör zu erkennen, erhält sie automatisch die Berechtigung, wenn der Nutzer zulässt, dass Ihre Anwendung den Intent verarbeitet. Ist dies nicht der Fall, müssen Sie die entsprechende Berechtigung in der Anwendung anfordern, bevor Sie eine Verbindung zum Zubehör herstellen.

In bestimmten Situationen kann eine explizite Berechtigungsanfrage erforderlich sein, z. B. wenn Ihre Anwendung Zubehör aufzählt, das bereits verbunden ist, und dann mit einem Gerät kommunizieren möchte. Du musst die Berechtigung für den Zugriff auf ein Zubehör prüfen, bevor du versuchst, mit ihm zu kommunizieren. Andernfalls wird ein Laufzeitfehler angezeigt, wenn der Nutzer die Berechtigung für den Zugriff auf das Zubehör verweigert hat.

Wenn Sie die Berechtigung explizit einholen möchten, erstellen Sie zuerst einen Übertragungsempfänger. Dieser Empfänger wartet auf den Intent, der gesendet wird, wenn Sie requestPermission() aufrufen. Durch den Aufruf von requestPermission() wird ein Dialogfeld angezeigt, in dem der Nutzer um die Berechtigung zum Herstellen einer Verbindung mit dem Zubehör gebeten wird. Der folgende Beispielcode zeigt, wie der Broadcast-Empfänger erstellt wird:

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

Fügen Sie dies in Ihre onCreate()-Methode in Ihrer Aktivität ein, um den Broadcast-Empfänger zu registrieren:

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

Wenn das Dialogfeld angezeigt werden soll, in dem Nutzer um die Berechtigung zum Herstellen einer Verbindung zum Zubehör gebeten werden, rufen Sie die Methode requestPermission() auf:

Kotlin

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

Java

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

Wenn Nutzer auf das Dialogfeld antworten, erhält der Empfänger des Rundfunks den Intent, der das zusätzliche EXTRA_PERMISSION_GRANTED-Element enthält. Dabei handelt es sich um einen booleschen Wert, der die Antwort darstellt. Prüfen Sie, ob dieser Wert auf „true“ gesetzt ist, bevor Sie eine Verbindung mit dem Zubehör herstellen.

Mit Zubehör kommunizieren

Du kannst mit dem Zubehör kommunizieren, indem du UsbManager verwendest, um einen Dateideskriptor abzurufen. Mit diesem kannst du Eingabe- und Ausgabestreams einrichten, um Daten in den Deskriptor zu lesen und zu schreiben. Die Streams stellen die Bulk-Endpunkte für die Eingabe und Ausgabe des Zubehörs dar. Sie sollten die Kommunikation zwischen dem Gerät und dem Zubehör in einem anderen Thread einrichten, damit Sie den UI-Hauptthread nicht sperren. Das folgende Beispiel zeigt, wie ein Zubehörteil für die Kommunikation geöffnet wird:

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

In der Methode run() des Threads können Sie mithilfe der Objekte FileInputStream oder FileOutputStream im Zubehör Lese- und Schreibzugriff durchführen. Achte beim Lesen von Daten von Zubehör mit einem FileInputStream-Objekt darauf, dass der verwendete Zwischenspeicher groß genug ist, um die USB-Paketdaten zu speichern. Das Android-Zubehörprotokoll unterstützt Paketpuffer von bis zu 16.384 Byte. Der Einfachheit halber können Sie den Zwischenspeicher also immer so deklarieren.

Hinweis: Die Pakete sind 64 Byte für USB-Vollgeschwindigkeitszubehör und 512 Byte für USB-Highspeed-Zubehör. Das Android-Zubehörprotokoll bündelt die Pakete für beide Geschwindigkeiten der Einfachheit halber in einem logischen Paket.

Weitere Informationen zur Verwendung von Threads in Android findest du unter Prozesse und Threads.

Kommunikation mit einem Zubehör beenden

Wenn die Kommunikation mit einem Zubehörteil abgeschlossen ist oder das Zubehör getrennt wurde, schließen Sie den Dateideskriptor, den Sie durch Aufrufen von close() geöffnet haben. Erstellen Sie einen Übertragungsempfänger wie unten, um auf getrennte Ereignisse zu warten:

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

Wenn Sie den Broadcast-Empfänger innerhalb der Anwendung und nicht im Manifest erstellen, kann Ihre Anwendung nur getrennte Ereignisse verarbeiten, während sie ausgeführt wird. Getrennte Ereignisse werden dann nur an die Anwendung gesendet, die gerade ausgeführt wird, und nicht an alle Anwendungen.