Ringkasan aksesori USB

Mode aksesori USB memungkinkan pengguna terhubung Hardware host USB yang dirancang khusus untuk perangkat Android. Aksesori harus mematuhi ke protokol aksesori Android yang diuraikan dalam dokumentasi Android Accessory Development Kit. Hal ini memungkinkan perangkat Android yang tidak dapat bertindak sebagai host USB untuk tetap berinteraksi dengan USB perangkat keras. Saat perangkat Android berada dalam mode aksesori USB, USB Android yang terpasang aksesori bertindak sebagai {i>host<i}, menyediakan daya ke bus USB, dan memerinci perangkat yang terhubung. Android 3.1 (API level 12) mendukung mode aksesori USB dan fitur ini juga di-backport ke Android 2.3.4 (level API 10) yang memungkinkan dukungan bagi lebih banyak perangkat.

Memilih API aksesori USB yang tepat

Meskipun diperkenalkan ke platform di Android 3.1, API aksesori USB juga yang tersedia di Android 2.3.4 menggunakan library add-on API Google. Karena API ini di-backport menggunakan {i>library<i} eksternal, ada dua paket yang dapat Anda impor untuk mendukung mode aksesori. Bergantung pada perangkat berbasis Android yang ingin Anda dukung, Anda mungkin harus menggunakannya satu sama lain:

  • com.android.future.usb: Untuk mendukung mode aksesori USB di Android 2.3.4, Add-on Google API library ini menyertakan API aksesori USB yang di-backport dan terdapat dalam namespace. Android 3.1 juga mendukung pengimporan dan pemanggilan class dalam namespace ini untuk mendukung aplikasi yang ditulis dengan library add-on. Library add-on ini adalah wrapper tipis di sekitar API aksesori android.hardware.usb dan tidak mendukung mode host USB. Jika Anda ingin mendukung berbagai perangkat yang mendukung mode aksesori USB, gunakan add-on library dan mengimpor paket ini. Penting untuk dicatat bahwa tidak semua perangkat Android 2.3.4 adalah yang diperlukan untuk mendukung fitur aksesori USB. Setiap produsen perangkat memutuskan mendukung atau tidak mendukung kemampuan ini, itulah sebabnya Anda harus mendeklarasikannya dalam manifes .
  • android.hardware.usb: Namespace ini berisi class yang mendukung USB mode aksesori di Android 3.1. Paket ini disertakan sebagai bagian dari API framework, jadi Android 3.1 mendukung mode aksesori USB tanpa menggunakan library add-on. Gunakan paket ini jika Anda hanya mementingkan perangkat Android 3.1 atau yang lebih baru yang memiliki dukungan hardware untuk USB mode aksesori, yang dapat Anda deklarasikan dalam file manifes.

Menginstal library add-on Google API

Jika ingin menginstal add-on tersebut, Anda dapat melakukannya dengan menginstal Google API Android API 10 khusus dengan SDK Manager. Lihat Menginstal Google API Add-on untuk mengetahui informasi selengkapnya tentang cara menginstal library add-on.

Ringkasan API

Karena library add-on adalah wrapper untuk API framework, class yang mendukung Fitur aksesori USB juga serupa. Anda dapat menggunakan dokumentasi referensi untuk android.hardware.usb meskipun Anda menggunakan library add-on.

Catatan: Namun, ada penggunaan kecil perbedaan antara library add-on dan API framework yang harus Anda ketahui.

Tabel berikut menjelaskan class yang mendukung API aksesori USB:

Class Deskripsi
UsbManager Memungkinkan Anda memerinci dan berkomunikasi dengan aksesori USB yang tersambung.
UsbAccessory Menunjukkan aksesori USB dan berisi metode untuk mengakses identitasnya tidak akurat atau tidak sesuai.

Perbedaan penggunaan library add-on dan API platform

Ada dua perbedaan penggunaan antara penggunaan library add-on Google API dan platform Google Cloud Platform.

Jika menggunakan library add-on, Anda harus mendapatkan objek UsbManager dengan cara berikut:

Kotlin

val manager = UsbManager.getInstance(this)

Java

UsbManager manager = UsbManager.getInstance(this);

Jika tidak menggunakan library add-on, Anda harus mendapatkan objek UsbManager dengan cara berikut:

Kotlin

val manager = getSystemService(Context.USB_SERVICE) as UsbManager

Java

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

Saat Anda memfilter aksesori yang terhubung dengan filter intent, objek UsbAccessory dimuat di dalam intent yang diteruskan ke aplikasi. Jika menggunakan library add-on, Anda harus mendapatkan objek UsbAccessory dengan cara berikut:

Kotlin

val accessory = UsbManager.getAccessory(intent)

Java

UsbAccessory accessory = UsbManager.getAccessory(intent);

Jika tidak menggunakan library add-on, Anda harus mendapatkan objek UsbAccessory dengan cara berikut:

Kotlin

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

Java

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

Persyaratan manifes Android

Daftar berikut menjelaskan apa saja yang perlu Anda tambahkan ke file manifes aplikasi sebelum bekerja dengan API aksesori USB. File manifes dan resource contoh menunjukkan cara mendeklarasikan item ini:

  • Karena tidak semua perangkat Android dijamin mendukung API aksesori USB, menyertakan elemen <uses-feature> yang mendeklarasikan bahwa aplikasi Anda menggunakan fitur android.hardware.usb.accessory.
  • Jika Anda menggunakan library add-on, tambahkan elemen <uses-library> yang menentukan com.android.future.usb.accessory untuk library.
  • Tetapkan SDK minimum aplikasi ke API Level 10 jika Anda menggunakan library add-on atau 12 jika Anda menggunakan paket android.hardware.usb.
  • Jika ingin aplikasi Anda diberi tahu tentang aksesori USB yang terpasang, tentukan Pasangan elemen <intent-filter> dan <meta-data> untuk android.hardware.usb.action.USB_ACCESSORY_ATTACHED dalam aktivitas utama Anda. Elemen <meta-data> mengarah ke file resource XML eksternal yang mendeklarasikan informasi identitas tentang aksesori yang ingin Anda deteksi.

    Dalam file resource XML, deklarasikan elemen <usb-accessory> untuk aksesori yang ingin Anda filter. Setiap <usb-accessory> dapat memiliki atribut berikut:

    • manufacturer
    • model
    • version

    Tidak disarankan melakukan pemfilteran pada version. Aksesori atau perangkat mungkin tidak selalu menentukan string versi (disengaja atau tidak sengaja). Saat aplikasi Anda mendeklarasikan atribut versi yang akan difilter dan aksesori atau perangkat tidak menentukan string versi, hal ini menyebabkan NullPointerException di Android versi sebelumnya. Masalah ini telah diperbaiki di Android 12.

    Simpan file resource di direktori res/xml/. Nama file resource (tanpa ekstensi .xml) harus sama dengan yang Anda tentukan di Elemen <meta-data>. Format untuk file resource XML juga ditampilkan di contoh di bawah ini.

Contoh file resource dan manifes

Contoh berikut menunjukkan contoh manifes dan file resource yang sesuai:

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

Dalam hal ini, file resource berikut harus disimpan di res/xml/accessory_filter.xml dan menentukan bahwa aksesori apa pun yang memiliki model, produsen, dan versi yang sesuai harus difilter. Aksesori mengirimkan informasi menghubungkan perangkat Android:

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

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

Menggunakan aksesori

Saat pengguna menyambungkan aksesori USB ke perangkat Android, sistem Android dapat menentukan apakah aplikasi Anda tertarik dengan aksesori yang tersambung. Jika demikian, Anda dapat mengatur komunikasi dengan aksesori jika memang itu yang diinginkan. Untuk melakukannya, aplikasi Anda harus:

  1. Menemukan aksesori yang terhubung menggunakan filter intent yang memfilter aksesori atau dengan menghitung aksesori yang tersambung dan menemukan yang sesuai.
  2. Mintalah izin kepada pengguna untuk berkomunikasi dengan aksesori, jika belum diperoleh.
  3. Berkomunikasi dengan aksesori dengan membaca dan menulis data pada antarmuka yang sesuai endpoint.

Menemukan aksesori

Aplikasi Anda dapat menemukan aksesori dengan menggunakan filter intent agar diberi tahu saat pengguna menyambungkan aksesori atau dengan mencantumkan aksesori yang telah tersambung. Menggunakan filter intent berguna jika ingin agar aplikasi Anda dapat secara otomatis mendeteksi aksesori yang diinginkan. Menghitung aksesori yang terhubung berguna jika Anda ingin mendapatkan daftar semua aksesori yang terhubung atau jika aplikasi Anda tidak memfilter intent.

Menggunakan filter intent

Agar aplikasi Anda menemukan aksesori USB tertentu, Anda dapat menentukan filter intent untuk memfilter intent android.hardware.usb.action.USB_ACCESSORY_ATTACHED. Bersama dengan filter intent ini, Anda perlu menetapkan file sumber daya yang menetapkan properti USB aksesori, seperti produsen, model, dan versi.

Contoh berikut menunjukkan cara mendeklarasikan filter 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>

Contoh berikut menunjukkan cara mendeklarasikan file resource yang sesuai yang menentukan atribut Aksesori USB yang menarik bagi Anda:

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

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

Dalam aktivitas, Anda dapat memperoleh UsbAccessory yang mewakili aksesori yang terpasang dari intent seperti ini (dengan library add-on):

Kotlin

val accessory = UsbManager.getAccessory(intent)

Java

UsbAccessory accessory = UsbManager.getAccessory(intent);

atau seperti ini (dengan API platform):

Kotlin

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

Java

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

Memerinci aksesori

Anda bisa meminta aplikasi Anda menghitung aksesori yang telah mengidentifikasi dirinya sendiri saat aplikasi berjalan.

Gunakan metode getAccessoryList() untuk mendapatkan susunan semua aksesori USB yang tersambung:

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

Catatan: Hanya satu aksesori terhubung yang didukung di waktu tertentu.

Mendapatkan izin untuk berkomunikasi dengan aksesori

Sebelum berkomunikasi dengan aksesori USB, aplikasi Anda harus memiliki izin dari pengguna.

Catatan: Jika aplikasi Anda menggunakan filter intent untuk menemukan aksesori yang terhubung, secara otomatis perangkat akan menerima jika pengguna mengizinkan aplikasi Anda menangani intent tersebut. Jika tidak, Anda harus meminta secara eksplisit dalam aplikasi sebelum menyambungkan ke aksesori.

Meminta izin secara eksplisit mungkin diperlukan dalam beberapa situasi, seperti saat aplikasi menghitung aksesori yang sudah terhubung dan kemudian ingin berkomunikasi dengan satu. Anda harus memeriksa izin untuk mengakses aksesori sebelum mencoba berkomunikasi dengannya. Jika tidak, Anda akan menerima pesan error runtime jika pengguna menolak izin untuk mengakses aksesori.

Untuk mendapatkan izin secara eksplisit, buat penerima siaran terlebih dahulu. Penerima ini mendengarkan intent yang akan disiarkan saat Anda memanggil requestPermission(). Panggilan ke requestPermission() menampilkan dialog ke pengguna yang meminta izin untuk tersambung ke aksesori. Kode contoh berikut menunjukkan cara buat penerima siaran:

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

Untuk mendaftarkan penerima siaran, masukkan penerima siaran ini ke metode onCreate() di aktivitas:

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

Untuk menampilkan dialog yang meminta izin kepada pengguna untuk tersambung ke aksesori, panggil Metode requestPermission():

Kotlin

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

Java

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

Saat pengguna membalas dialog, penerima siaran Anda akan menerima intent yang berisi Tambahan EXTRA_PERMISSION_GRANTED, yang merupakan boolean merepresentasikan jawaban. Periksa nilai true (benar) tambahan ini sebelum menyambungkan ke aksesori.

Berkomunikasi dengan aksesori

Anda dapat berkomunikasi dengan aksesori menggunakan UsbManager untuk mendapatkan deskriptor file tempat Anda bisa menyiapkan aliran input dan {i>output<i} untuk membaca dan menulis data deskripsi. Stream tersebut menunjukkan endpoint massal input dan output aksesori. Anda harus mengatur mengatur komunikasi antara perangkat dan aksesori di thread lain, sehingga Anda tidak mengunci thread UI utama Anda. Contoh berikut menunjukkan cara membuka aksesori untuk berkomunikasi:

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

Dalam metode run() thread, Anda dapat membaca dan menulis ke aksesori dengan menggunakan objek FileInputStream atau FileOutputStream. Saat membaca data dari aksesori dengan objek FileInputStream, pastikan bahwa buffer yang Anda gunakan cukup besar untuk menyimpan data paket USB. Protokol aksesori Android mendukung paket {i>buffer<i} hingga 16384 byte, jadi Anda bisa memilih untuk selalu mendeklarasikan {i>buffer<i} Anda agar lebih mudah.

Catatan: Pada tingkat yang lebih rendah, paketnya adalah 64 byte untuk USB aksesori kecepatan penuh dan 512 byte untuk aksesori USB berkecepatan tinggi. Aksesori Android demi kemudahan, protokol ini menggabungkan paket untuk kedua kecepatan menjadi satu paket logis.

Untuk informasi selengkapnya tentang penggunaan thread di Android, lihat Proses dan Rangkaian pesan.

Menghentikan komunikasi dengan aksesori

Setelah selesai berkomunikasi dengan aksesori atau jika aksesori terlepas, tutup deskriptor file yang telah dibuka dengan memanggil close(). Untuk memproses aktivitas terpisah, buat penerima siaran seperti di bawah ini:

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

Membuat penerima siaran dalam aplikasi, dan bukan manifes, memungkinkan aplikasi apa pun untuk hanya menangani peristiwa terpisah saat sedang berjalan. Dengan cara ini, peristiwa terpisah menjadi hanya dikirim ke aplikasi yang sedang berjalan dan tidak disiarkan ke semua aplikasi.