Ringkasan aksesori USB

Mode aksesori USB memungkinkan pengguna menyambungkan hardware host USB yang khusus dirancang untuk perangkat Android. Aksesori harus mematuhi protokol aksesori Android yang dijelaskan dalam dokumentasi Accessory Development Kit Android. Hal ini memungkinkan perangkat Android yang tidak berkemampuan host tetap dapat berinteraksi dengan hardware USB. Saat perangkat Android berada dalam mode aksesori USB, aksesori USB Android yang tersambung akan berfungsi sebagai host, memberikan daya ke bus USB, dan memerinci perangkat yang tersambung. Android 3.1 (API level 12) mendukung mode aksesori USB dan fitur ini juga di-backport ke Android 2.3.4 (API level 10) untuk mengaktifkan dukungan bagi perangkat yang lebih beragam.

Memilih API aksesori USB yang tepat

Meskipun diperkenalkan ke platform di Android 3.1, API aksesori USB juga tersedia di Android 2.3.4 menggunakan library add-on Google API. Karena API ini di-backport menggunakan library eksternal, ada dua paket yang dapat diimpor untuk mendukung mode aksesori USB. Bergantung pada perangkat Android yang ingin didukung, Anda mungkin harus menggunakan salah satu paket tersebut.

  • com.android.future.usb: Untuk mendukung mode aksesori USB di Android 2.3.4, library add-on Google API menyertakan API aksesori USB yang di-backport dan memuatnya dalam namespace ini. Android 3.1 juga mendukung pengimporan dan panggilan class dalam namespace ini untuk mendukung aplikasi yang ditulis dengan library add-on. Library add-on ini adalah wrapper tipis di seluruh API aksesori android.hardware.usb dan tidak mendukung mode host USB. Jika ingin mendukung berbagai perangkat yang mendukung mode aksesori USB, gunakan library add-on dan impor paket ini. Penting diperhatikan bahwa tidak semua perangkat Android 2.3.4 perlu mendukung fitur aksesori USB. Setiap produsen perangkat dapat menentukan untuk mendukung kemampuan ini atau tidak. Oleh karena itu, Anda harus mendeklarasikannya dalam file manifes.
  • android.hardware.usb: Namespace ini berisi class yang mendukung mode aksesori USB di Android 3.1. Paket ini disertakan sebagai bagian dari API framework, sehingga Android 3.1 mendukung mode aksesori USB tanpa perlu menggunakan library add-on. Gunakan paket ini jika Anda hanya ingin menggunakan Android 3.1 atau yang lebih baru yang memiliki dukungan hardware untuk mode aksesori USB, yang dapat dideklarasikan dalam file manifes.

Menginstal library add-on Google API

Jika ingin menginstal add-on tersebut, Anda dapat melakukannya dengan menginstal paket Android API 10 dari Google API menggunakan SDK Manager. Lihat Menginstal Add-on Google API 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 akan serupa. Anda dapat menggunakan dokumentasi referensi untuk android.hardware.usb meskipun Anda menggunakan library add-on.

Catatan: Namun, ada perbedaan kecil dalam penggunaan library add-on dan API framework yang perlu 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 informasi identitasnya.

Perbedaan penggunaan library add-on dan API platform

Ada dua perbedaan dalam penggunaan library add-on Google API dan API 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 memfilter aksesori yang terhubung dengan filter intent, objek UsbAccessory dimuat di dalam intent yang diteruskan ke aplikasi Anda. 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 item yang perlu Anda tambahkan ke file manifes aplikasi sebelum menggunakan API aksesori USB. Contoh file resource dan manifes menjelaskan cara mendeklarasikan item berikut:

  • Karena tidak semua perangkat Android dijamin mendukung API aksesori USB, sertakan 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 ke Level 12 jika Anda menggunakan paket android.hardware.usb.
  • Jika ingin aplikasi Anda mendapatkan pemberitahuan tentang aksesori USB yang terpasang, tentukan pasangan elemen <intent-filter> and <meta-data> untuk intent 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 dideteksi.

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

    • manufacturer
    • model
    • version

    Simpan file resource di direktori res/xml/. Nama file resource (tanpa ekstensi .xml) harus sama dengan nama yang Anda tentukan di elemen <meta-data>. Format untuk file resource XML juga ditunjukkan dalam 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 kasus ini, file resource berikut harus disimpan di res/xml/accessory_filter.xml serta menentukan bahwa aksesori yang memiliki model, produsen, dan versi yang sesuai harus difilter. Aksesori mengirim atribut ini ke 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 sesuai dengan aksesori yang tersambung. Jika demikian, Anda dapat menyiapkan komunikasi dengan aksesori jika diinginkan. Untuk melakukannya, aplikasi Anda harus:

  1. Menemukan aksesori yang terhubung dengan menggunakan filter intent yang memfilter peristiwa aksesori terkait atau dengan memerinci aksesori yang tersambung, serta menemukan aksesori yang sesuai.
  2. Meminta izin kepada pengguna untuk berkomunikasi dengan aksesori, jika belum diizinkan.
  3. Berkomunikasi dengan aksesori dengan membaca dan menulis data pada endpoint antarmuka yang sesuai.

Menemukan aksesori

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

Menggunakan filter intent

Agar aplikasi 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 harus menentukan file resource yang menentukan properti aksesori USB, seperti produsen, model, dan versi. Setelah pengguna menyambungkan aksesori yang sesuai dengan filter aksesori Anda,

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 aksesori USB yang diinginkan:

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

Aplikasi Anda dapat memerinci aksesori yang telah diidentifikasi saat sedang berjalan.

Gunakan metode getAccessoryList() untuk mendapatkan array 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 tersambung yang didukung dalam satu waktu.

Mendapatkan izin untuk berkomunikasi dengan aksesori

Agar dapat berkomunikasi dengan aksesori USB, aplikasi Anda harus mendapatkan izin dari pengguna.

Catatan: Jika aplikasi Anda menggunakan filter intent untuk menemukan aksesori saat tersambung, aplikasi secara otomatis akan menerima izin jika pengguna mengizinkannya menangani intent. Jika tidak, Anda harus meminta izin secara eksplisit di aplikasi agar dapat tersambung ke aksesori.

Dalam beberapa situasi, Anda mungkin perlu meminta izin secara eksplisit, seperti ketika aplikasi Anda memerinci aksesori yang sudah tersambung, lalu ingin berkomunikasi dengan salah satunya. Anda harus memeriksa izin untuk mengakses aksesori sebelum mencoba berkomunikasi dengannya. Jika tidak, error waktu proses akan muncul jika pengguna menolak izin untuk mengakses aksesori.

Untuk mendapatkan izin secara eksplisit, buat penerima siaran terlebih dahulu. Penerima ini memproses intent yang disiarkan saat Anda memanggil requestPermission(). Panggilan ke requestPermission() menampilkan dialog kepada pengguna yang meminta izin untuk tersambung ke aksesori. Contoh kode berikut menunjukkan cara membuat 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 di metode onCreate() dalam aktivitas Anda:

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 requestPermission() metode:

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 berupa boolean yang menunjukkan jawabannya. Periksa nilai true dalam tambahan ini sebelum menyambungkan ke aksesori.

Berkomunikasi dengan aksesori

Anda dapat berkomunikasi dengan aksesori menggunakan UsbManager untuk memperoleh deskriptor file yang dapat disiapkan stream input dan outputnya untuk membaca dan menulis data ke deskriptor. Stream tersebut menunjukkan endpoint massal input dan output aksesori. Anda harus menyiapkan komunikasi antara perangkat dan aksesori di thread lain agar thread UI utama tidak terkunci. 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 ini, Anda dapat membaca dan menulis ke aksesori dengan menggunakan objek FileInputStream atau FileOutputStream. Saat membaca data dari aksesori dengan objek FileInputStream, pastikan buffer yang digunakan cukup besar untuk menyimpan data paket USB. Protokol aksesori Android mendukung buffer paket hingga 16384 byte, sehingga Anda dapat memilih untuk selalu mendeklarasikan buffer dengan ukuran ini agar lebih mudah.

Catatan: Pada level lebih rendah, paket berukuran 64 byte untuk aksesori USB berkecepatan penuh dan 512 byte untuk aksesori USB berkecepatan tinggi. Demi kemudahan, protokol aksesori Android menggabungkan paket untuk kedua kecepatan tersebut dalam satu paket logis.

Untuk mengetahui informasi selengkapnya tentang cara menggunakan thread di Android, lihat Proses dan Thread.

Menghentikan komunikasi dengan aksesori

Jika sudah selesai berkomunikasi dengan aksesori atau jika aksesori dilepas, 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
                }
            }
        }
    };
    

Dengan membuat penerima siaran dalam aplikasi, bukan manifes, aplikasi Anda dapat menangani peristiwa terpisah hanya saat sedang berjalan. Dengan begitu, aktivitas terpisah hanya dikirim ke aplikasi yang sedang berjalan dan tidak disiarkan ke semua aplikasi.