Gdy urządzenie z Androidem jest w trybie hosta USB, działa jako host USB, zasila magistralę, i wylicza podłączone urządzenia USB. Tryb hosta USB jest obsługiwany w Androidzie 3.1 i nowszych.
Omówienie interfejsu API
Zanim zaczniesz, musisz wiedzieć, z którymi zajęciami musisz pracować.
W tej tabeli opisano interfejsy API hostów USB w pakiecie android.hardware.usb
.
Kategoria | Opis |
---|---|
UsbManager |
Umożliwia wyliczanie podłączonych urządzeń USB i komunikowanie się z nimi. |
UsbDevice |
Reprezentuje podłączone urządzenie USB i zawiera metody dostępu do jego danych identyfikacyjnych oraz informacji, interfejsów i punktów końcowych. |
UsbInterface |
Reprezentuje interfejs urządzenia USB, który definiuje zestaw funkcji urządzenia. Urządzenie może mieć jeden lub więcej interfejsów do komunikacji. |
UsbEndpoint |
Reprezentuje punkt końcowy interfejsu, który jest kanałem komunikacyjnym tego interfejsu. An interfejs może mieć jeden lub więcej punktów końcowych i zazwyczaj ma punkty wejściowe wejściowe i wyjściowe dla dwukierunkową komunikację z urządzeniem. |
UsbDeviceConnection |
Reprezentuje połączenie z urządzeniem, które przesyła dane w punktach końcowych. Te zajęcia umożliwia przesyłanie danych w obie strony synchronicznie lub asynchronicznie. |
UsbRequest |
Reprezentuje asynchroniczne żądanie komunikacji z urządzeniem za pomocą interfejsu UsbDeviceConnection . |
UsbConstants |
Definiuje stałe USB odpowiadające definicjom w linux/usb/ch9.h w systemie Linux jądro. |
W większości przypadków musisz używać wszystkich tych klas (pole UsbRequest
jest wymagane tylko w przypadku komunikacji asynchronicznej)
podczas komunikacji z urządzeniem USB. Zazwyczaj uzyskujesz taki element (UsbManager
), aby pobrać żądane UsbDevice
.
Gdy już masz urządzenie, znajdź odpowiednie UsbInterface
oraz UsbEndpoint
za pomocą którego chcesz się komunikować. Gdy uzyskasz prawidłowy punkt końcowy, otwórz UsbDeviceConnection
, aby skomunikować się z urządzeniem USB.
Wymagania dotyczące pliku manifestu na Androida
Na poniższej liście opisaliśmy, co musisz dodać do pliku manifestu aplikacji, zanim z interfejsami API hosta USB:
- Nie wszystkie urządzenia z Androidem będą obsługiwać interfejsy API hosta USB,
zawierać element
<uses-feature>
deklarujący, że aplikacja używa funkcjaandroid.hardware.usb.host
. - Ustaw minimalny pakiet SDK aplikacji na API na poziomie 12 lub wyższym. Interfejsy API hosta USB nie są dostępnych na wcześniejszych poziomach interfejsu API.
- Jeśli chcesz otrzymywać powiadomienia do aplikacji o podłączonym urządzeniu USB, określ
Para elementów
<intent-filter>
i<meta-data>
dlaandroid.hardware.usb.action.USB_DEVICE_ATTACHED
intencje w Twojej głównej aktywności. Element<meta-data>
wskazuje zewnętrzny plik zasobów XML z deklaracją umożliwiające identyfikację informacji o urządzeniu, które chcesz wykryć.W pliku zasobów XML zadeklaruj elementy
<usb-device>
dla USB. urządzenia, które chcesz filtrować. Poniższa lista opisuje atrybuty:<usb-device>
Ogólnie do filtrowania należy używać identyfikatora dostawcy i produktu dla konkretnego urządzenia oraz użyj klasy, podklasy i protokołu, jeśli chcesz przefiltrować dane według grupy. urządzeń USB, takich jak urządzenia pamięci masowej lub aparaty cyfrowe. Możesz wpisać brak lub wszystkich tych atrybutów. Jeśli nie określono atrybutów pasujących do każdego urządzenia USB, zrób to tylko jeśli aplikacja tego wymaga:vendor-id
product-id
class
subclass
protocol
(urządzenie lub interfejs)
Zapisz plik zasobów w katalogu
res/xml/
. Nazwa pliku zasobów (bez rozszerzenia .xml) musi być taki sam jak URL określony w<meta-data>
element. Plik zasobów XML ma format przykład poniżej.
Przykłady plików manifestu i plików zasobów
Poniżej znajduje się przykładowy plik manifestu i odpowiadający mu plik zasobów:
<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>
W tym przypadku należy zapisać poniższy plik zasobów w
res/xml/device_filter.xml
i określa, że każde urządzenie USB z
atrybuty powinny być filtrowane:
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-device vendor-id="1234" product-id="5678" class="255" subclass="66" protocol="1" /> </resources>
Praca z urządzeniami
Gdy użytkownik podłącza urządzenie USB do urządzenia z Androidem, system Android może określić, czy aplikacja jest zainteresowana połączonym urządzeniem. Jeśli tak, możesz skonfigurować w razie potrzeby komunikować się z urządzeniem. Aby to było możliwe, aplikacja musi:
- Wykrywanie podłączonych urządzeń USB za pomocą filtra intencji, który pozwala otrzymywać powiadomienia, gdy użytkownik służy do podłączenia urządzenia USB lub przez zliczenie już podłączonych urządzeń USB.
- Poproś użytkownika o zgodę na połączenie się z urządzeniem USB, jeśli jeszcze nie ma tej zgody.
- Komunikuj się z urządzeniem USB, odczytując i zapisuj dane w odpowiednim interfejsie i punktów końcowych.
Odkryj urządzenie
Aplikacja może wykrywać urządzenia USB, korzystając z filtra intencji i powiadamiając go, gdy użytkownik podłącza urządzenie lub wymienia urządzenia USB, które są już podłączone. Za pomocą jest przydatny, jeśli chcesz, aby aplikacja automatycznie wykrywała wybranego urządzenia. Wyliczanie podłączonych urządzeń USB jest przydatne, jeśli chcesz uzyskać listę wszystkich urządzeń połączonych urządzeń lub jeśli aplikacja nie odfiltrowała intencji.
Korzystanie z filtra intencji
Aby aplikacja wykrywała określone urządzenie USB, możesz ustawić filtr intencji
filtr do intencji android.hardware.usb.action.USB_DEVICE_ATTACHED
. Razem z
do tego filtra intencji, musisz określić plik zasobów określający właściwości dysku USB
urządzenia, takie jak produkt i identyfikator dostawcy. Gdy użytkownicy podłączą urządzenie zgodne z Twoim urządzeniem
system wyświetla mu okno z pytaniem, czy chce uruchomić aplikację.
Jeśli użytkownicy zaakceptują warunki, aplikacja automatycznie uzyska dostęp do urządzenia do czasu
urządzenie jest odłączone.
Ten przykład pokazuje, jak zadeklarować filtr intencji:
<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>
Poniższy przykład pokazuje, jak zadeklarować odpowiedni plik zasobów, który określa Urządzenia USB, które Cię interesują:
<?xml version="1.0" encoding="utf-8"?> <resources> <usb-device vendor-id="1234" product-id="5678" /> </resources>
W swojej aktywności możesz uzyskać UsbDevice
, który reprezentuje
podłączone urządzenie z intencji wygląda tak:
Kotlin
val device: UsbDevice? = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
Java
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
Wyliczanie urządzeń
Jeśli Twoja aplikacja chce sprawdzić wszystkie podłączone obecnie urządzenia USB
gdy aplikacja jest uruchomiona, może wyliczyć urządzenia magistrali. Użyj metody getDeviceList()
, aby uzyskać mapę skrótu wszystkich
podłączone urządzenia USB. Jeśli chcesz,
mapa haszująca jest osadzona w nazwie urządzenia USB
urządzenia z mapy.
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");
W razie potrzeby można również uzyskać iterację z mapy haszującej i przetworzyć dla każdego urządzenia przez:
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 }
Uzyskiwanie uprawnień do komunikowania się z urządzeniem
Przed komunikacją z urządzeniem USB aplikacja musi mieć pozwolenie użytkowników.
Uwaga: jeśli aplikacja korzysta z parametru filtra intencji, aby wykryć podłączone urządzenia USB, a następnie automatycznie odbierze jeśli użytkownik zezwala aplikacji na obsługę intencji. Jeśli nie, bezpośrednio w aplikacji przed nawiązaniem połączenia z urządzeniem.
Wyraźne proszenie o zgodę może być konieczne w niektórych sytuacjach, na przykład gdy Aplikacja wylicza urządzenia USB, które są już podłączone, a następnie chce się komunikować z jeden. Przed próbą skomunikowania się z urządzeniem musisz sprawdzić uprawnienia dostępu. Jeśli nie, jeśli użytkownik odmówi zgody na dostęp do urządzenia, wyświetli się błąd czasu działania.
Aby bezpośrednio uzyskać odpowiednie uprawnienia, najpierw utwórz odbiornik. Ten odbiornik nasłuchuje przez
intencję, która ma być transmitowana, gdy zadzwonisz do: requestPermission()
. W wywołaniu requestPermission()
pojawi się okno
użytkownik prosi o zgodę na połączenie z urządzeniem. Ten przykładowy kod pokazuje, jak
Utwórz odbiornik:
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); } } } } };
Aby zarejestrować odbiornik, dodaj ten parametr do metody onCreate()
w
aktywność:
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);
Aby wyświetlić okno z prośbą o pozwolenie na połączenie z urządzeniem, wywołaj metodę requestPermission()
:
Kotlin
lateinit var device: UsbDevice ... usbManager.requestPermission(device, permissionIntent)
Java
UsbDevice device; ... usbManager.requestPermission(device, permissionIntent);
Gdy użytkownik odpowie na okno, odbiornik wiadomości otrzyma intencję zawierającą
EXTRA_PERMISSION_GRANTED
dodatkowy, który jest wartością logiczną
reprezentująca odpowiedź. Sprawdź, czy ten dodatek ma wartość prawda, zanim połączysz się z
urządzenia.
Komunikacja z urządzeniem
Komunikacja z urządzeniem USB może być synchroniczna lub asynchroniczna. W obu przypadkach
należy utworzyć nowy wątek, w którym będą przeprowadzane wszystkie transmisje danych, aby nie blokować
Wątek interfejsu. Aby prawidłowo skonfigurować komunikację z urządzeniem, musisz uzyskać odpowiednie
UsbInterface
i UsbEndpoint
z
urządzenie, na którym chcesz się komunikować i wysyłać żądania w tym punkcie końcowym za pomocą UsbDeviceConnection
. Ogólnie kod powinien:
- Sprawdź atrybuty obiektu
UsbDevice
, takie jak identyfikator produktu, dostawcy lub klasy urządzenia w celu określenia, czy chcesz komunikować się z urządzenia. - Jeśli masz pewność, że chcesz skomunikować się z urządzeniem, znajdź odpowiednią
UsbInterface
, których chcesz używać do komunikowania się z odpowiedniUsbEndpoint
interfejsu. Interfejsy mogą mieć jeden lub większą liczbę punktów końcowych. Zwykle mają też punkt końcowy wejścia i wyjścia dla komunikacji między usługami. - Gdy znajdziesz właściwy punkt końcowy, otwórz
UsbDeviceConnection
w tym punkcie końcowym. - Dostarcz dane, które chcesz przesyłać w punkcie końcowym, za pomocą metody
bulkTransfer()
lubcontrolTransfer()
. Zalecenia wykonaj ten krok w innym wątku, aby uniknąć zablokowania głównego wątku interfejsu użytkownika. Więcej informacji na temat używania wątków w Androidzie można znaleźć w artykule Procesy i Wątki.
Poniższy fragment kodu to prosty sposób synchronicznego przesyłania danych. Twój kod powinno mieć więcej logiki umożliwiającej poprawne wyszukiwanie interfejsu i punktów końcowych do komunikacji oraz przenosić dane w innym wątku niż główny wątek UI:
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
Aby wysyłać dane asynchronicznie, użyj klasy UsbRequest
do żądania initialize
i queue
żądania asynchronicznego, a następnie poczekaj na wynik
dzięki requestWait()
.
Kończenie komunikacji z urządzeniem
Gdy skończysz komunikować się z urządzeniem lub jeśli zostało ono odłączone, zamknij UsbInterface
i UsbDeviceConnection
przez
dzwonię do: releaseInterface()
i
close()
Aby nasłuchiwać odłączonych zdarzeń,
utwórz odbiornik tak jak poniżej:
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 } } } };
Utworzenie odbiornika w aplikacji, a nie pliku manifestu, umożliwia do obsługi odłączonych zdarzeń tylko podczas działania aplikacji. Dzięki temu odłączone zdarzenia są wysyłane tylko do aktywnej aplikacji, a nie do wszystkich aplikacji.