USB-Host – Übersicht

Wenn sich Ihr Android-Gerät im USB-Hostmodus befindet, fungiert es als USB-Host, versorgt den Bus mit und listet verbundene USB-Geräte auf. Der USB-Hostmodus wird ab Android 3.1 unterstützt.

API-Übersicht

Bevor Sie beginnen, ist es wichtig, die Kurse zu kennen, mit denen Sie arbeiten müssen. Die In der folgenden Tabelle werden die USB-Host-APIs im Paket android.hardware.usb beschrieben.

Tabelle 1 USB-Host-APIs

Klasse Beschreibung
UsbManager Ermöglicht die Auflistung von verbundenen USB-Geräten und die Kommunikation mit ihnen.
UsbDevice Stellt ein verbundenes USB-Gerät dar und enthält Methoden für den Zugriff auf seine identifizierenden Informationen, Schnittstellen und Endpunkte.
UsbInterface Eine Schnittstelle eines USB-Geräts, die eine Reihe von Funktionen für den . Ein Gerät kann eine oder mehrere Schnittstellen haben, über die es kommunizieren kann.
UsbEndpoint Stellt einen Schnittstellenendpunkt dar, der ein Kommunikationskanal für diese Schnittstelle ist. Eine Schnittstelle kann einen oder mehrere Endpunkte haben und hat in der Regel Eingabe- und Ausgabeendpunkte für Zwei-Wege-Kommunikation mit dem Gerät.
UsbDeviceConnection Stellt eine Verbindung zum Gerät dar, das Daten auf Endpunkten überträgt. Dieser Kurs ermöglicht das synchrone oder asynchrone Senden von Daten.
UsbRequest Stellt eine asynchrone Anfrage zur Kommunikation mit einem Gerät über ein UsbDeviceConnection dar.
UsbConstants Definiert USB-Konstanten, die den Definitionen in linux/usb/ch9.h unter Linux entsprechen. Kernel.

In den meisten Fällen müssen Sie alle diese Klassen verwenden. UsbRequest ist nur bei asynchroner Kommunikation erforderlich. wenn Sie mit einem USB-Gerät kommunizieren. Im Allgemeinen rufen Sie einen UsbManager ab, um die gewünschte UsbDevice abzurufen. Wenn du das Gerät zur Hand hast, musst du die entsprechende UsbInterface und die UsbEndpoint dafür suchen. Schnittstelle für die Kommunikation. Sobald du den richtigen Endpunkt ermittelt hast, kannst du einen UsbDeviceConnection öffnen, um mit dem USB-Gerät zu kommunizieren.

Anforderungen an Android-Manifest

In der folgenden Liste wird beschrieben, was Sie der Manifestdatei Ihrer Anwendung hinzufügen müssen, bevor Sie mit den USB-Host-APIs:

  • Da nicht alle Android-Geräte die USB-Host-APIs unterstützen, Ein <uses-feature>-Element enthalten, das deklariert, dass deine App die Funktion android.hardware.usb.host.
  • Legen Sie für das SDK der Anwendung mindestens API-Level 12 fest. Die USB-Host-APIs sind nicht die auf früheren API-Ebenen vorhanden sind.
  • Wenn Sie möchten, dass Ihre Anwendung über ein angeschlossenes USB-Gerät benachrichtigt wird, geben Sie eine <intent-filter>- und <meta-data>-Element-Paar für die android.hardware.usb.action.USB_DEVICE_ATTACHED Intent in deiner Hauptaktivität. Die Das <meta-data>-Element verweist auf eine externe XML-Ressourcendatei, die deklariert, zur Identifizierung des Geräts, das erkannt werden soll.

    Deklariere in der XML-Ressourcendatei <usb-device>-Elemente für das USB- die Sie filtern möchten. In der folgenden Liste werden die Attribute der <usb-device> Verwenden Sie in der Regel die Anbieter- und Produkt-ID, wenn Sie für ein bestimmtes Gerät und verwenden Sie Klasse, Unterklasse und Protokoll, wenn Sie nach einer Gruppe von USB-Geräten wie Massenspeicher oder Digitalkameras. Sie können „Keine“ oder all diese Attribute. Wenn Sie keine Attribute angeben, stimmen dies nur mit jedem USB-Gerät überein. wenn dies für Ihre Anwendung erforderlich ist:

    • vendor-id
    • product-id
    • class
    • subclass
    • protocol (Gerät oder Benutzeroberfläche)

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

Beispiele für Manifest- und Ressourcendateien

Das folgende Beispiel zeigt ein Beispielmanifest und die zugehörige Ressourcendatei:

<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 diesem Fall sollte die folgende Ressourcendatei res/xml/device_filter.xml und gibt an, dass jedes USB-Gerät mit der angegebenen sollten gefiltert werden:

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

<resources>
    <usb-device vendor-id="1234" product-id="5678" class="255" subclass="66" protocol="1" />
</resources>

Mit Geräten arbeiten

Wenn Nutzer USB-Geräte an ein Android-Gerät anschließen, kann das Android-System ermitteln, ob Ihre App an dem verbundenen Gerät interessiert ist. In diesem Fall können Sie Kommunikation mit dem Gerät. Dazu muss Ihre Anwendung folgende Voraussetzungen erfüllen:

  1. Du kannst verbundene USB-Geräte mithilfe eines Intent-Filters erkennen und benachrichtigt werden, wenn der Nutzer ein USB-Gerät verbindet oder die bereits verbundenen USB-Geräte auflistet.
  2. Bitten Sie den Nutzer um die Berechtigung, eine Verbindung zum USB-Gerät herzustellen, falls Sie das noch nicht getan haben.
  3. Kommunizieren Sie mit dem USB-Gerät durch Lesen und Schreiben von Daten auf der entsprechenden Schnittstelle Endpunkten.

Gerät finden

Ihre App kann USB-Geräte erkennen, indem sie entweder einen Intent-Filter verwendet, um benachrichtigt zu werden, der Nutzer ein Gerät anschließt oder die bereits verbundenen USB-Geräte auflistet. Bei Verwendung eines Intent-Filter ist nützlich, wenn Sie möchten, dass Ihre Anwendung automatisch ein gewünschtes Gerät. Die Auflistung aller verbundenen USB-Geräte ist nützlich, wenn Sie eine Liste aller verbundenen Geräten oder wenn Ihre App nicht nach einem Intent gefiltert hat.

Intent-Filter verwenden

Damit Ihre App ein bestimmtes USB-Gerät erkennt, können Sie einen Intent-Filter angeben, um Filter für den Intent android.hardware.usb.action.USB_DEVICE_ATTACHED. Zusammen mit Bei diesem Intent-Filter müssen Sie eine Ressourcendatei angeben, in der die Attribute der USB- z. B. die Produkt- und Anbieter-ID. Wenn Nutzer ein mit Ihrem Gerät verknüpftes Gerät verbinden Filter anwenden, werden sie in einem Dialogfeld gefragt, ob sie Ihre Anwendung starten möchten. Wenn die Nutzer zustimmen, hat Ihre Anwendung automatisch die Berechtigung für den Zugriff auf das Gerät, bis die Gerät nicht verbunden ist.

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

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

Das folgende Beispiel zeigt, wie die entsprechende Ressourcendatei deklariert wird, die die Interessante USB-Geräte:

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

<resources>
    <usb-device vendor-id="1234" product-id="5678" />
</resources>

In Ihrer Aktivität können Sie die UsbDevice abrufen, die für das angeschlossene Gerät aus:

Kotlin

val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)

Java

UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);

Geräte auflisten

Wenn Ihre Anwendung alle derzeit verbundenen USB-Geräte prüfen möchte: während Ihre Anwendung ausgeführt wird, kann sie Geräte auf dem Bus auflisten. Verwenden Sie die Methode getDeviceList(), um eine Hash-Zuordnung aller den verbundenen USB-Geräten. Die Hash-Zuordnung wird durch den Namen des USB-Geräts eingegeben, wenn Sie ein Gerät aus der Karte zu erhalten.

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

Bei Bedarf können Sie auch einfach einen Iterator aus der Hash-Zuordnung abrufen und jedes Gerät verarbeiten. nach 1:

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
}

Berechtigung zur Kommunikation mit einem Gerät einholen

Bevor Sie mit dem USB-Gerät kommunizieren können, muss Ihre App über die Berechtigung Ihres Nutzenden.

Hinweis: Wenn Ihre Anwendung einen Intent-Filter, um USB-Geräte zu erkennen, während sie verbunden sind, empfängt sie automatisch Berechtigung, wenn der Nutzer der Anwendung erlaubt, den Intent zu verarbeiten. Falls nicht, müssen Sie in Ihrer App ausdrücklich bestätigen, bevor Sie eine Verbindung zum Gerät herstellen.

In einigen Situationen kann es notwendig sein, ausdrücklich um Erlaubnis zu bitten, z. B. wenn deine listet die bereits verbundenen USB-Geräte auf, mit denen sie kommunizieren möchte, eins. Sie müssen Ihre Berechtigung für den Zugriff auf ein Gerät prüfen, bevor Sie versuchen, mit ihm zu kommunizieren. Wenn nicht, erhalten Sie einen Laufzeitfehler, wenn der Nutzer den Zugriff auf das Gerät verweigert hat.

Erstellen Sie zuerst einen Übertragungsempfänger, um die Berechtigung explizit einzuholen. Dieser Empfänger wartet auf Intent, der beim Aufrufen von requestPermission() übertragen wird. Beim Aufruf von requestPermission() wird ein Dialogfeld für die Nutzer bittet um Erlaubnis, sich mit dem Gerät zu verbinden. Der folgende Beispielcode zeigt, wie Sie den Übertragungsempfänger erstellen:

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

Fügen Sie zum Registrieren des Übertragungsempfängers Folgendes in die onCreate()-Methode in Ihrem Aktivität:

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

Rufen Sie die Methode requestPermission() auf, um das Dialogfeld aufzurufen, in dem Nutzer um die Berechtigung gebeten werden, eine Verbindung mit dem Gerät herzustellen:

Kotlin

lateinit var device: UsbDevice
...
usbManager.requestPermission(device, permissionIntent)

Java

UsbDevice device;
...
usbManager.requestPermission(device, permissionIntent);

Wenn Nutzer auf das Dialogfeld antworten, empfängt der Übertragungsempfänger den Intent mit dem Ereignis EXTRA_PERMISSION_GRANTED-Extra, ein boolescher Wert die die Antwort darstellt. Überprüfen Sie diese Zusatzinformationen auf den Wert "true", bevor Sie eine Verbindung zum .

Mit einem Gerät kommunizieren

Die Kommunikation mit einem USB-Gerät kann entweder synchron oder asynchron erfolgen. In beiden Fällen sollte einen neuen Thread erstellen, über den alle Datenübertragungen durchgeführt werden. UI-Thread. Um die Kommunikation mit einem Gerät richtig einzurichten, müssen Sie die entsprechenden UsbInterface und UsbEndpoint der Gerät, mit dem Sie kommunizieren möchten und mit dem Sie Anfragen an diesen Endpunkt mit einem UsbDeviceConnection senden möchten. Im Allgemeinen sollte Ihr Code:

  • Prüfe die Attribute eines UsbDevice-Objekts, z. B. Produkt-ID, Anbieter-ID oder Geräteklasse, um zu entscheiden, ob Sie mit dem .
  • Wenn Sie sicher sind, dass Sie mit dem Gerät kommunizieren möchten, suchen Sie das entsprechende UsbInterface, die für die Kommunikation mit dem der richtigen UsbEndpoint dieser Schnittstelle. Schnittstellen können Folgendes aufweisen: oder mehr Endpunkte. Normalerweise haben sie einen Eingabe- und Ausgabeendpunkt für bidirektionale Kommunikation.
  • Wenn Sie den richtigen Endpunkt gefunden haben, öffnen Sie eine UsbDeviceConnection an diesen Endpunkt.
  • Geben Sie mit der Methode bulkTransfer() oder controlTransfer() die Daten an, die am Endpunkt übertragen werden sollen. Sie sollten führen Sie diesen Schritt in einem anderen Thread aus, damit der UI-Hauptthread nicht blockiert wird. Weitere Informationen Informationen zur Verwendung von Threads in Android finden Sie unter Prozesse und Threads:

Das folgende Code-Snippet stellt eine einfache Möglichkeit zur Durchführung einer synchronen Datenübertragung dar. Ihr Code sollte über mehr Logik verfügen, um die richtigen Schnittstellen und Endpunkte für die Kommunikation zu finden. Außerdem sollten Daten in einem anderen Thread als dem UI-Hauptthread übertragen werden:

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

Wenn Sie Daten asynchron senden möchten, verwenden Sie die Klasse UsbRequest, um initialize und queue eine asynchrone Anfrage zu senden. Warten Sie dann auf das Ergebnis. mit requestWait().

Kommunikation mit einem Gerät beenden

Wenn die Kommunikation mit einem Gerät abgeschlossen ist oder das Gerät getrennt wurde, schließen Sie UsbInterface und UsbDeviceConnection wie folgt: releaseInterface() anrufen und close(). Um auf getrennte Ereignisse zu warten, Erstellen Sie einen Übertragungsempfänger wie folgt:

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

Wenn Sie den Übertragungsempfänger in der Anwendung und nicht im Manifest erstellen, können Sie -Anwendung so ab, dass getrennte Ereignisse nur während ihrer Ausführung verarbeitet werden. So werden getrennte Ereignisse wird nur an die aktuell ausgeführte Anwendung gesendet und nicht an alle Anwendungen gesendet.