Omówienie akcesoriów USB

Tryb akcesorium USB umożliwia użytkownikom Host USB zaprojektowany specjalnie dla urządzeń z Androidem. Akcesoria muszą spełniać wymagania z protokołem akcesoriów do Androida opisanym w dokumentacji pakietu Android Accessory Development Kit. Dzięki temu urządzenia z Androidem, które nie mogą działać jako host USB, mogą nadal wchodzić w interakcje z USB. sprzęt. Gdy urządzenie z Androidem działa w trybie akcesorium USB, podłączony port USB z Androidem akcesorium działa jako host, dostarcza zasilanie magistrali USB i wylicza połączone urządzenia. Android 3.1 (poziom interfejsu API 12) obsługuje tryb akcesoriów USB i funkcjonuje się również ponownie Androida 2.3.4 (poziom interfejsu API 10), aby zapewnić obsługę szerszej gamy urządzeń.

Wybierz odpowiednie interfejsy API akcesoriów USB

Chociaż interfejsy API akcesoriów USB zostały wprowadzone na platformie w Androidzie 3.1, są one również dostępne w Androidzie 2.3.4 przy użyciu biblioteki dodatku interfejsów API Google. Ponieważ te interfejsy API zostały z backportem przy użyciu zewnętrznej biblioteki. Są 2 pakiety, które możesz zaimportować, aby obsługiwać USB tryb akcesorium. W zależności od urządzeń z Androidem, które chcesz obsługiwać, konieczne może być używaj jednych i drugich:

  • com.android.future.usb: aby zapewnić obsługę trybu akcesorium USB w Androidzie 2.3.4, Dodatek do interfejsów API Google obejmuje portowe interfejsy API akcesoriów USB, które znajdują się w przestrzeni nazw. Android 3.1 obsługuje również importowanie i wywoływanie klas w tej przestrzeni nazw, do obsługi aplikacji napisanych za pomocą biblioteki dodatków. Ta biblioteka dodatków jest cienkim otoczeniem wokół interfejsów API akcesoriów android.hardware.usb i nie obsługuje trybu hosta USB. Jeśli Jeśli chcesz obsługiwać najszerszą gamę urządzeń obsługujących tryb akcesorium USB, użyj dodatku i zaimportuj ten pakiet. Należy pamiętać, że nie wszystkie urządzenia z Androidem 2.3.4 są wymagane do obsługi funkcji akcesorium USB. Każdy indywidualny producent urządzenia Ta funkcja jest obsługiwana, dlatego musisz zadeklarować ją w pliku manifestu. .
  • android.hardware.usb: ta przestrzeń nazw zawiera klasy obsługujące USB. trybu akcesorium w Androidzie 3.1. Ten pakiet wchodzi w skład interfejsów API platformy, dlatego Android 3.1 obsługuje tryb akcesorium USB bez konieczności korzystania z biblioteki dodatków. Użyj tego pakietu jeśli zależy Ci tylko na urządzeniach z Androidem 3.1 lub nowszym, które obsługują sprzętową obsługę USB trybu akcesorium, który można zadeklarować w pliku manifestu.

Instalowanie biblioteki dodatku do interfejsów API Google

Jeśli chcesz zainstalować dodatek, możesz to zrobić, instalując interfejsy API Google Android API 10 w pakiecie SDK w usłudze SDK Manager. Patrz: Instalowanie interfejsów API Google Dodatek, który zawiera więcej informacji o instalowaniu biblioteki dodatków.

Omówienie interfejsu API

Biblioteka dodatku jest otoką interfejsów API platformy, dlatego klasy, które obsługują Funkcja akcesorium USB jest podobna. Nawet jeśli korzystasz z biblioteki dodatków, możesz skorzystać z dokumentacji referencyjnej dotyczącej android.hardware.usb.

Uwaga: występuje jednak niewielkie użycie między biblioteką dodatku a interfejsami API platformy, o których warto wiedzieć.

W poniższej tabeli opisano klasy obsługujące interfejsy API akcesoriów USB:

Kategoria Opis
UsbManager Umożliwia wyliczanie podłączonych akcesoriów USB i komunikowanie się z nimi.
UsbAccessory Reprezentuje akcesorium USB i zawiera metody dostępu do jego identyfikacji i informacjami o nich.

Różnice w wykorzystaniu biblioteki dodatku i interfejsów API platformy

Istnieją 2 różnice w sposobie użytkowania między biblioteką dodatku do interfejsów API Google a korzystaniem z platformy API.

Jeśli korzystasz z biblioteki dodatku, musisz uzyskać obiekt UsbManager w ten sposób:

Kotlin

val manager = UsbManager.getInstance(this)

Java

UsbManager manager = UsbManager.getInstance(this);

Jeśli nie korzystasz z biblioteki dodatku, musisz uzyskać obiekt UsbManager w ten sposób:

Kotlin

val manager = getSystemService(Context.USB_SERVICE) as UsbManager

Java

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

Gdy filtrujesz dane pod kątem połączonego akcesorium za pomocą filtra intencji, obiekt UsbAccessory jest zawarty w intencji przekazywanej do aplikacji. Jeśli korzystasz z biblioteki dodatku, musisz uzyskać obiekt UsbAccessory w ten sposób:

Kotlin

val accessory = UsbManager.getAccessory(intent)

Java

UsbAccessory accessory = UsbManager.getAccessory(intent);

Jeśli nie korzystasz z biblioteki dodatku, musisz uzyskać obiekt UsbAccessory w ten sposób:

Kotlin

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

Java

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

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 akcesoriów USB. Plik manifestu i plik zasobów Przykłady pokazują, jak zadeklarować te produkty:

  • Nie wszystkie urządzenia z Androidem będą obsługiwać interfejsy API akcesoriów USB, zawierać element <uses-feature> deklarujący, że aplikacja używa funkcja android.hardware.usb.accessory.
  • Jeśli używasz tagu bibliotekę dodatków, dodaj element <uses-library> określający com.android.future.usb.accessory.
  • Jeśli używasz biblioteki dodatku, ustaw minimalny pakiet SDK aplikacji na API poziomu 10 lub 12, jeśli używasz pakietu android.hardware.usb.
  • Jeśli chcesz otrzymywać powiadomienia o podłączonych akcesorium USB, określ Para elementów <intent-filter> i <meta-data> dla android.hardware.usb.action.USB_ACCESSORY_ATTACHED intencje w Twojej głównej aktywności. Element <meta-data> wskazuje zewnętrzny plik zasobów XML, który deklaruje informacje identyfikujące akcesorium, które chcesz wykryć.

    W pliku zasobów XML zadeklaruj elementy <usb-accessory> dla akcesoria, które chcesz filtrować. Każdy element <usb-accessory> może zawierać parametr następujące atrybuty:

    • manufacturer
    • model
    • version

    Nie zalecamy filtrowania według zakresu dat version. Akcesorium lub urządzenie nie zawsze może określać ciąg znaków z wersją (celowo lub przypadkowo). Gdy aplikacja deklaruje atrybut wersji, według którego ma być filtrowany produkt, oraz akcesorium lub urządzenie nie określa ciągu wersji, to powoduje, że NullPointerException jest włączone starszych wersji Androida. Ten problem został rozwiązany w Androidzie 12.

    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. Format pliku zasobów XML jest też widoczny w 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.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>

W tym przypadku należy zapisać poniższy plik zasobów w res/xml/accessory_filter.xml i określa, że każde akcesorium zawierające atrybuty należy odfiltrować odpowiedni model, producenta i wersję. Akcesorium wysyła te określa urządzenie z systemem Android:

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

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

Praca z akcesoriami

Gdy użytkownicy podłączają akcesoria USB do urządzenia z Androidem, system Android może określić, czy aplikacja jest zainteresowana połączonym akcesorium. Jeśli tak, możesz ustawić w razie potrzeby komunikuje się z akcesoriami. Aby to było możliwe, aplikacja musi:

  1. Odkryj połączone akcesoria za pomocą filtra intencji, który filtruje akcesoria powiązane z nim wydarzenia lub policzając połączone akcesoria i znajdując te, które Ci odpowiadają.
  2. W razie potrzeby poproś użytkownika o pozwolenie na skomunikowanie się z akcesoriami. uzyskane.
  3. Komunikuj się z akcesoriami, odczytując i zapisuj dane w odpowiednim interfejsie. i punktów końcowych.

Odkryj akcesorium

Aplikacja może wykrywać akcesoria, korzystając z filtra intencji, który będzie powiadamiany, gdy użytkownik podłącza akcesorium lub wymienia akcesoria, które są już połączone. Za pomocą jest przydatny, jeśli chcesz, aby aplikacja automatycznie wykrywała wybranego akcesorium. Wyliczanie połączonych akcesoriów przydaje się, gdy chcesz zobaczyć listę wszystkich połączonych akcesoriów lub jeśli aplikacja nie przefiltrowała danych pod kątem intencji.

Korzystanie z filtra intencji

Aby aplikacja wykrywała określone akcesorium USB, możesz określić filtr intencji aby filtrować według intencji android.hardware.usb.action.USB_ACCESSORY_ATTACHED. Wraz z tym filtrem intencji, musisz określić plik zasobów określający właściwości dysku USB akcesorium, takie jak producent, model i wersja.

Ten przykład pokazuje, jak zadeklarować filtr intencji:

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

Poniższy przykład pokazuje, jak zadeklarować odpowiedni plik zasobów, który określa Akcesoria USB, które Cię interesują:

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

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

W swojej aktywności możesz uzyskać UsbAccessory, który reprezentuje dołączone akcesorium z intencji podobnego do tego (z biblioteką dodatków):

Kotlin

val accessory = UsbManager.getAccessory(intent)

Java

UsbAccessory accessory = UsbManager.getAccessory(intent);

lub podobny (z interfejsami API platformy):

Kotlin

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

Java

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

Wyliczaj akcesoria

Możesz skonfigurować aplikację tak, aby wymieniała akcesoria, które zidentyfikowały się podczas aplikacja jest uruchomiona.

Użyj metody getAccessoryList() aby uzyskać tablicę wszystkich podłączonych akcesoriów USB:

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

Uwaga: obsługiwane jest tylko jedno połączone akcesorium za jednym razem.

Uzyskiwanie zgody na komunikację z akcesoriami

Przed komunikacją z akcesorium USB aplikacja musi mieć pozwolenie od użytkowników.

Uwaga: jeśli aplikacja korzysta z parametru , aby wykryć podłączone akcesoria, automatycznie otrzymują jeśli użytkownik zezwala aplikacji na obsługę intencji. Jeśli nie, w aplikacji przed nawiązaniem połączenia z akcesorium.

Wyraźne proszenie o zgodę może być konieczne w niektórych sytuacjach, na przykład gdy Aplikacja wymienia akcesoria, które są już połączone, a następnie chce się z nimi komunikować jeden. Zanim spróbujesz skomunikować się z akcesoriami, musisz sprawdzić uprawnienia dostępu. W przeciwnym razie, jeśli użytkownik odmówił dostępu do aplikacji, wyświetli się błąd czasu działania. akcesorium.

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 pozwolenie na połączenie się z akcesoriami. 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 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);
                }
            }
        }
    }
};

Aby zarejestrować odbiornik, umieść go w metodzie 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), 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);

Aby wyświetlić okno z prośbą o pozwolenie na połączenie się z akcesoriami, wywołaj funkcję Metoda requestPermission():

Kotlin

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

Java

UsbAccessory accessory;
...
usbManager.requestPermission(accessory, 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 akcesorium.

Komunikacja z akcesoriami

Możesz komunikować się z akcesoriami za pomocą UsbManager, aby: uzyskać deskryptor pliku, w którym możesz skonfigurować strumienie wejściowe i wyjściowe do odczytu i zapisu danych; deskryptor. Strumienie reprezentują zbiorcze wejściowe i wyjściowe punkty końcowe akcesorium. Należy ustawić nawiązać komunikację między urządzeniem a akcesoriami w innym wątku, aby nie blokować głównym wątku UI. Ten przykład pokazuje, jak otworzyć akcesorium, z którym chcesz się komunikować:

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

W metodzie run() wątku możesz odczytywać i zapisywać w akcesorium za pomocą polecenia obiekty FileInputStream lub FileOutputStream. Podczas czytania z akcesorium z obiektem FileInputStream, upewnij się, że bufor jest wystarczająco duży, aby zapisać dane pakietów USB. Protokół akcesoriów z Androidem obsługuje buforuje pakiety do 16 384 bajtów, możesz więc zadeklarować, że to ten bufor i rozmiaru dla prostoty.

Uwaga: na niższym poziomie pakiety mają 64 bajty w przypadku danych USB akcesorium zapewniające pełną prędkość i 512 bajtów w przypadku akcesoriów USB o dużej szybkości. Akcesorium z Androidem dla uproszczenia łączy pakiety dla obu tych szybkości w jeden pakiet logiczny.

Więcej informacji o używaniu wątków na Androidzie znajdziesz w artykule Procesy Wątki.

Zakończ komunikację z akcesoriami

Gdy skończysz komunikować się z akcesoriami lub jeśli zostało ono odłączone, zamknij deskryptor pliku otwarty przez wywołanie funkcji close(). Aby nasłuchiwać odłączonego zdarzenia, utwórz odbiornik w ten sposób:

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

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.