Ringkasan host USB

Saat berada dalam mode host USB, perangkat Android berfungsi sebagai host USB, mengaktifkan bus, dan memerinci perangkat USB yang tersambung. Mode host USB didukung di Android 3.1 dan yang lebih tinggi.

Ringkasan API

Sebelum memulai, penting untuk memahami semua class yang perlu Anda gunakan. Tabel berikut menjelaskan API host USB dalam paket android.hardware.usb.

Tabel 1. API Host USB

Class Deskripsi
UsbManager Memungkinkan Anda memerinci dan berkomunikasi dengan perangkat USB yang tersambung.
UsbDevice Menunjukkan perangkat USB yang tersambung dan berisi metode untuk mengakses informasi identitas, antarmuka, dan endpointnya.
UsbInterface Menunjukkan antarmuka perangkat USB, yang menentukan serangkaian fungsi untuk perangkat. Perangkat dapat memiliki satu atau beberapa antarmuka yang dapat digunakan untuk berkomunikasi.
UsbEndpoint Menunjukkan antarmuka endpoint, yang merupakan saluran komunikasi untuk antarmuka ini. Antarmuka dapat memiliki satu atau beberapa endpoint, serta biasanya memiliki endpoint input dan output untuk komunikasi dua arah dengan perangkat.
UsbDeviceConnection Menunjukkan koneksi ke perangkat, yang mentransfer data pada endpoint. Class ini memungkinkan Anda mengirim data bolak-balik secara sinkron atau asinkron.
UsbRequest Menunjukkan konten asinkron untuk berkomunikasi dengan perangkat melalui UsbDeviceConnection.
UsbConstants Menentukan konstanta USB yang sesuai dengan definisi dalam linux/usb/ch9.h di kernel Linux.

Biasanya, Anda perlu menggunakan semua class ini (UsbRequest hanya diperlukan jika Anda melakukan komunikasi asinkron) saat berkomunikasi dengan perangkat USB. Secara umum, Anda perlu mendapatkan UsbManager untuk mengambil UsbDevice yang diinginkan. Setelah perangkat tersedia, Anda harus menemukan UsbInterface dan UsbEndpoint antarmuka tersebut yang tepat untuk berkomunikasi. Setelah mendapatkan endpoint yang benar, buka UsbDeviceConnection untuk berkomunikasi dengan perangkat USB.

Persyaratan manifes Android

Daftar berikut menjelaskan item yang perlu Anda tambahkan ke file manifes aplikasi sebelum menggunakan API host USB:

  • Karena tidak semua perangkat Android dijamin mendukung API host USB, sertakan elemen <uses-feature> yang mendeklarasikan bahwa aplikasi Anda menggunakan fitur android.hardware.usb.host.
  • Tetapkan SDK minimum aplikasi ke API Level 12 atau yang lebih tinggi. API host USB tidak tersedia pada level API sebelumnya.
  • Jika ingin aplikasi Anda mendapatkan pemberitahuan tentang perangkat USB yang terpasang, tentukan pasangan elemen <intent-filter> and <meta-data> untuk intent android.hardware.usb.action.USB_DEVICE_ATTACHED dalam aktivitas utama Anda. Elemen <meta-data> mengarah ke file resource XML eksternal yang mendeklarasikan informasi identitas tentang perangkat yang ingin dideteksi.

    Dalam file resource XML deklarasikan elemen <usb-device> untuk perangkat USB yang ingin difilter. Daftar berikut menjelaskan atribut <usb-device>. Secara umum, gunakan ID produk dan vendor jika ingin memfilter perangkat tertentu, serta gunakan class, subclass, dan protokol jika ingin memfilter sekelompok perangkat USB, seperti perangkat penyimpanan massal atau kamera digital. Anda dapat menentukan semua atribut ini atau tidak sama sekali. Tidak menentukan atribut akan menyesuaikan setiap perangkat USB, jadi hanya lakukan hal ini jika aplikasi Anda memerlukannya:

    • vendor-id
    • product-id
    • class
    • subclass
    • protocol (perangkat atau antarmuka)

    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 ada dalam contoh di bawah.

Contoh file resource dan manifes

Contoh berikut menunjukkan contoh manifes dan file resource yang sesuai:

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

Dalam kasus ini, file resource berikut harus disimpan di res/xml/device_filter.xml dan menentukan bahwa setiap perangkat USB dengan atribut yang ditentukan harus difilter:

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

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

Menggunakan perangkat

Saat pengguna menyambungkan perangkat USB ke perangkat Android, sistem Android dapat menentukan apakah aplikasi Anda sesuai dengan perangkat yang tersambung. Jika demikian, Anda dapat menyiapkan komunikasi dengan perangkat jika diinginkan. Untuk melakukannya, aplikasi Anda harus:

  1. Menemukan perangkat USB yang tersambung dengan menggunakan filter intent agar diberi tahu saat pengguna menyambungkan perangkat USB atau dengan memerinci perangkat USB yang telah tersambung.
  2. Meminta izin kepada pengguna untuk tersambung ke perangkat USB, jika belum diizinkan.
  3. Berkomunikasi dengan perangkat USB dengan membaca dan menulis data pada endpoint antarmuka yang sesuai.

Menemukan perangkat

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

Menggunakan filter intent

Agar aplikasi menemukan perangkat USB tertentu, Anda dapat menentukan filter intent untuk memfilter intent android.hardware.usb.action.USB_DEVICE_ATTACHED. Bersama dengan filter intent ini, Anda harus menentukan file resource yang menentukan properti perangkat USB, seperti ID produk dan vendor. Setelah pengguna menyambungkan perangkat yang sesuai dengan filter perangkat Anda, sistem akan menampilkan dialog yang menanyakan apakah mereka ingin memulai aplikasi Anda. Jika disetujui oleh pengguna, aplikasi Anda secara otomatis memiliki izin untuk mengakses perangkat hingga koneksi perangkat terputus.

Contoh berikut menunjukkan cara mendeklarasikan filter intent:

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

Contoh berikut menunjukkan cara mendeklarasikan file resource yang sesuai yang menentukan perangkat USB yang diinginkan:

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

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

Dalam aktivitas, Anda dapat memperoleh UsbDevice yang menunjukkan perangkat yang terpasang dari intent seperti ini:

Kotlin

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

Java

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

Memerinci perangkat

Jika aplikasi Anda ingin memeriksa semua perangkat USB yang saat ini tersambung selagi berjalan, aplikasi dapat memerinci perangkat pada bus. Gunakan metode getDeviceList() untuk mendapatkan peta hash semua perangkat USB yang tersambung. Peta hash disertakan oleh nama perangkat USB jika Anda ingin mendapatkan perangkat dari peta.

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

Jika diinginkan, Anda juga dapat memperoleh iterator dari peta hash dan memproses setiap perangkat satu per satu:

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
    }
    

Mendapatkan izin untuk berkomunikasi dengan perangkat

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

Catatan: Jika aplikasi Anda menggunakan filter intent untuk menemukan perangkat USB 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 perangkat.

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

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

Untuk mendaftarkan penerima siaran, tambahkan 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 perangkat, panggil requestPermission() metode:

Kotlin

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

Java

    UsbDevice device;
    ...
    usbManager.requestPermission(device, 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 perangkat.

Berkomunikasi dengan perangkat

Komunikasi dengan perangkat USB dapat bersifat tersinkron atau asinkron. Dalam hal ini, Anda harus membuat thread baru untuk melakukan semua transmisi data, sehingga UI thread tidak diblokir. Untuk menyiapkan komunikasi dengan perangkat secara tepat, Anda harus mendapatkan UsbInterface dan UsbEndpoint perangkat yang sesuai yang ingin digunakan untuk berkomunikasi dan mengirim permintaan pada endpoint ini dengan UsbDeviceConnection. Secara umum, kode Anda harus:

  • Periksa atribut objek UsbDevice, seperti ID produk, ID vendor, atau class perangkat untuk menentukan apakah Anda ingin berkomunikasi dengan perangkat atau tidak.
  • Setelah memastikan bahwa Anda ingin berkomunikasi dengan perangkat, temukan UsbInterface yang sesuai yang ingin digunakan untuk berkomunikasi, serta UsbEndpoint antarmuka tersebut yang sesuai. Antarmuka dapat memiliki satu atau beberapa endpoint, serta biasanya memiliki endpoint input dan output untuk komunikasi dua arah.
  • Setelah menemukan endpoint yang benar, buka UsbDeviceConnection pada endpoint tersebut.
  • Sediakan metode bulkTransfer() atau controlTransfer() untuk data yang ingin dikirim pada endpoint. Anda harus melakukan langkah ini di thread lain untuk mencegah pemblokiran thread UI utama. Untuk mengetahui informasi selengkapnya tentang cara menggunakan thread di Android, lihat Proses dan Thread.

Cuplikan kode berikut menunjukkan cara mudah untuk melakukan transfer data sinkron. Kode Anda harus memiliki logika lain untuk menemukan antarmuka dan endpoint yang benar untuk berkomunikasi, serta melakukan transfer data dalam thread yang berbeda dari thread UI utama:

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
    

Untuk mengirim data secara asinkron, gunakan class UsbRequest untuk initialize dan queue konten asinkron, lalu tunggu hasilnya dengan requestWait().

Untuk mengetahui informasi selengkapnya, lihat contoh AdbTest, yang menunjukkan cara melakukan transfer massal asinkron, dan contoh MissileLauncher, yang menunjukkan cara memproses endpoint gangguan secara asinkron.

Menghentikan komunikasi dengan perangkat

Jika sudah selesai berkomunikasi dengan perangkat atau jika perangkat dilepas, tutup UsbInterface dan UsbDeviceConnection dengan memanggil releaseInterface() dan 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_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
                }
            }
        }
    };
    

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.