ภาพรวมโฮสต์ USB

เมื่ออุปกรณ์ที่ใช้ระบบ Android อยู่ในโหมดโฮสต์ USB อุปกรณ์จะทำงานเป็นโฮสต์ USB จ่ายไฟให้รถบัส และแจกแจงอุปกรณ์ USB ที่เชื่อมต่ออยู่ ระบบรองรับโหมดโฮสต์ USB ใน Android 3.1 ขึ้นไป

ภาพรวมของ API

ก่อนที่จะเริ่มต้น คุณควรเข้าใจชั้นเรียนที่คุณต้องการทำงานด้วย ตารางต่อไปนี้อธิบาย API ของโฮสต์ USB ในแพ็กเกจ android.hardware.usb

ตาราง 1 API โฮสต์ USB

ชั้น คำอธิบาย
UsbManager ช่วยให้คุณสามารถแจกแจงและสื่อสารกับอุปกรณ์ USB ที่เชื่อมต่ออยู่
UsbDevice แสดงอุปกรณ์ USB ที่เชื่อมต่ออยู่และมีวิธีเข้าถึงการระบุอุปกรณ์ อินเทอร์เฟซ และอุปกรณ์ปลายทางต่างๆ
UsbInterface หมายถึงอินเทอร์เฟซของอุปกรณ์ USB ซึ่งกำหนดชุดฟังก์ชันการทำงานสำหรับ อุปกรณ์ อุปกรณ์สามารถมีอินเทอร์เฟซตั้งแต่ 1 รายการขึ้นไปสำหรับสื่อสาร
UsbEndpoint หมายถึงปลายทางของอินเทอร์เฟซ ซึ่งเป็นช่องทางการสื่อสารสำหรับอินเทอร์เฟซนี้ CANNOT TRANSLATE ของอินเทอร์เฟซมีปลายทางได้อย่างน้อย 1 รายการ และมักจะมีปลายทางอินพุตและเอาต์พุตสำหรับ เป็นการสื่อสารแบบ 2 ทางกับอุปกรณ์
UsbDeviceConnection หมายถึงการเชื่อมต่อกับอุปกรณ์ ซึ่งจะโอนข้อมูลบนปลายทาง ชั้นเรียนนี้ ช่วยให้คุณสามารถส่งข้อมูลกลับไปกลับมาแบบซิงโครนัสหรือแบบไม่พร้อมกัน
UsbRequest แสดงคำขอแบบไม่พร้อมกันเพื่อสื่อสารกับอุปกรณ์ผ่าน UsbDeviceConnection
UsbConstants กำหนดค่าคงที่ USB ที่ตรงกับคำจำกัดความใน linux/usb/ch9.h ของ Linux เคอร์เนล

โดยทั่วไป คุณจะต้องใช้ชั้นเรียนเหล่านี้ทั้งหมด (ต้องใช้ UsbRequest เฉพาะกรณีที่คุณสื่อสารแบบไม่พร้อมกันเท่านั้น) เมื่อสื่อสารกับอุปกรณ์ USB โดยทั่วไปแล้ว คุณจะได้รับ UsbManager เพื่อเรียกข้อมูล UsbDevice ที่ต้องการ เมื่อมีอุปกรณ์แล้ว คุณจะต้องค้นหา UsbInterface ที่เหมาะสมและ UsbEndpoint ของอุปกรณ์นั้น สำหรับการสื่อสาร เมื่อคุณได้ปลายทางที่ถูกต้องแล้ว ให้เปิด UsbDeviceConnection เพื่อสื่อสารกับอุปกรณ์ USB

ข้อกำหนดของไฟล์ Manifest สำหรับ Android

รายการต่อไปนี้อธิบายสิ่งที่คุณต้องเพิ่มลงในไฟล์ Manifest ของแอปพลิเคชันก่อน การทำงานกับ API ของโฮสต์ USB

  • เนื่องจากอุปกรณ์ที่ใช้ Android บางอุปกรณ์ไม่สามารถรับประกัน API ของโฮสต์ USB ได้ รวมเอลิเมนต์ <uses-feature> ที่ประกาศว่าแอปพลิเคชันของคุณใช้ ฟีเจอร์ android.hardware.usb.host
  • ตั้งค่า SDK ขั้นต่ำของแอปพลิเคชันเป็น API ระดับ 12 ขึ้นไป API ของโฮสต์ USB ไม่ใช่ ซึ่งมีอยู่ใน API ระดับก่อนหน้า
  • หากต้องการให้แอปพลิเคชันของคุณรับการแจ้งเตือนว่ามีอุปกรณ์ USB ที่เชื่อมต่ออยู่ ให้ระบุ คู่เอลิเมนต์ <intent-filter> และ <meta-data> สำหรับ Intent android.hardware.usb.action.USB_DEVICE_ATTACHED ในกิจกรรมหลัก เอลิเมนต์ <meta-data> ชี้ไปที่ไฟล์ทรัพยากร XML ภายนอกที่ประกาศ ข้อมูลที่ระบุตัวตนได้ เกี่ยวกับอุปกรณ์ที่คุณต้องการตรวจหา

    ในไฟล์ทรัพยากร XML ให้ประกาศองค์ประกอบ <usb-device> สำหรับ USB อุปกรณ์ที่คุณต้องการกรอง รายการต่อไปนี้จะอธิบายแอตทริบิวต์ของ <usb-device> โดยทั่วไป ให้ใช้ผู้ให้บริการและรหัสผลิตภัณฑ์หากต้องการกรอง สำหรับอุปกรณ์ที่เฉพาะเจาะจง และใช้คลาส คลาสย่อย และโปรโตคอลหากคุณต้องการกรองหากลุ่ม ของอุปกรณ์ USB เช่น อุปกรณ์จัดเก็บข้อมูลจำนวนมาก หรือกล้องดิจิทัล คุณสามารถระบุไม่มีหรือ แอตทริบิวต์ทั้งหมดนี้ การระบุไม่ให้แอตทริบิวต์ที่ตรงกับอุปกรณ์ USB ทุกเครื่อง ดังนั้นโปรดทําเฉพาะ หากแอปพลิเคชันของคุณต้องใช้งาน

    • vendor-id
    • product-id
    • class
    • subclass
    • protocol (อุปกรณ์หรืออินเทอร์เฟซ)

    บันทึกไฟล์ทรัพยากรในไดเรกทอรี res/xml/ ชื่อไฟล์ทรัพยากร (ไม่มีนามสกุล .xml) ต้องเป็นชื่อเดียวกับที่คุณระบุไว้ใน องค์ประกอบ <meta-data> รูปแบบสำหรับไฟล์ทรัพยากร XML อยู่ในรูปแบบ ตัวอย่างด้านล่าง

ตัวอย่างไฟล์ Manifest และไฟล์ทรัพยากร

ตัวอย่างต่อไปนี้แสดงตัวอย่างไฟล์ Manifest และไฟล์ทรัพยากรที่เกี่ยวข้อง

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

ในกรณีนี้ ไฟล์ทรัพยากรต่อไปนี้ควรได้รับการบันทึกไว้ใน res/xml/device_filter.xml และระบุว่าอุปกรณ์ USB ที่มี ควรกรองแอตทริบิวต์ต่อไปนี้

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

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

ทำงานกับอุปกรณ์

เมื่อผู้ใช้เชื่อมต่ออุปกรณ์ USB กับอุปกรณ์ Android ระบบ Android จะสามารถระบุ แอปพลิเคชันของคุณสนใจอุปกรณ์ที่เชื่อมต่อหรือไม่ หากใช่ คุณสามารถตั้งค่า สื่อสารกับอุปกรณ์ได้ หากต้องการ ในการดำเนินการดังกล่าว แอปพลิเคชันของคุณจะต้อง:

  1. สำรวจอุปกรณ์ USB ที่เชื่อมต่อโดยใช้ตัวกรอง Intent เพื่อรับการแจ้งเตือนเมื่อผู้ใช้ เชื่อมต่ออุปกรณ์ USB หรือแจกแจงอุปกรณ์ USB ที่เชื่อมต่ออยู่แล้ว
  2. ขออนุญาตจากผู้ใช้ในการเชื่อมต่อกับอุปกรณ์ USB หากยังไม่ได้ขอรับ
  3. สื่อสารกับอุปกรณ์ USB โดยการอ่านและเขียนข้อมูลบนอินเทอร์เฟซที่เหมาะสม ปลายทาง

สำรวจอุปกรณ์

แอปพลิเคชันของคุณสามารถค้นพบอุปกรณ์ USB โดยใช้ตัวกรอง Intent เพื่อรับการแจ้งเตือนเมื่อ ผู้ใช้เชื่อมต่ออุปกรณ์หรือแจกแจงอุปกรณ์ USB ที่เชื่อมต่ออยู่แล้ว การใช้ ตัวกรอง Intent จะมีประโยชน์ในกรณีที่คุณต้องการให้แอปพลิเคชันตรวจหา อุปกรณ์ที่ต้องการได้อีกด้วย การระบุอุปกรณ์ USB ที่เชื่อมต่อไว้มีประโยชน์หากต้องการดูรายการทั้งหมด อุปกรณ์ที่เชื่อมต่อ หรือหากแอปพลิเคชันของคุณไม่ได้กรองความตั้งใจออก

ใช้ตัวกรอง Intent

หากต้องการให้แอปพลิเคชันพบอุปกรณ์ USB ที่ต้องการ คุณสามารถระบุตัวกรอง Intent เพื่อ กรองหา Intent android.hardware.usb.action.USB_DEVICE_ATTACHED พร้อมกับ ตัวกรอง Intent นี้ คุณจะต้องระบุไฟล์ทรัพยากรที่ระบุคุณสมบัติของ USB เช่น รหัสผลิตภัณฑ์และผู้ให้บริการ เมื่อผู้ใช้เชื่อมต่ออุปกรณ์ที่ตรงกับอุปกรณ์ของคุณ ระบบจะแสดงกล่องโต้ตอบที่ถามผู้ใช้ว่าต้องการเริ่มแอปพลิเคชันหรือไม่ หากผู้ใช้ยอมรับ แอปพลิเคชันของคุณจะได้รับอนุญาตให้เข้าถึงอุปกรณ์โดยอัตโนมัติจนกว่า อุปกรณ์ไม่ได้เชื่อมต่ออยู่

ตัวอย่างต่อไปนี้แสดงวิธีประกาศตัวกรอง 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>

ตัวอย่างต่อไปนี้แสดงวิธีประกาศไฟล์ทรัพยากรที่เกี่ยวข้องที่ระบุ อุปกรณ์ USB ที่คุณสนใจ:

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

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

ในกิจกรรม คุณสามารถรับ UsbDevice ที่แสดงถึง อุปกรณ์ที่เชื่อมต่อจาก Intent ดังนี้

Kotlin

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

Java

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

แจกแจงอุปกรณ์

หากแอปพลิเคชันของคุณสนใจที่จะตรวจสอบอุปกรณ์ USB ทั้งหมดที่เชื่อมต่ออยู่ในปัจจุบัน ขณะที่แอปพลิเคชันทำงานอยู่ จะสามารถแจกแจงอุปกรณ์บนรถประจำทางได้ ใช้เมธอด getDeviceList() เพื่อรับแมปแฮชของทั้งหมด อุปกรณ์ USB ที่เชื่อมต่ออยู่ ระบบจะคีย์แมปแฮชโดยใช้ชื่ออุปกรณ์ USB หากคุณต้องการ รับอุปกรณ์จากแผนที่

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

นอกจากนี้ คุณยังใช้ตัววนซ้ำจากแฮชแมปและประมวลผลแต่ละอุปกรณ์ได้หากต้องการ หนึ่ง:

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
}

ขออนุญาตสื่อสารกับอุปกรณ์

ก่อนที่จะสื่อสารกับอุปกรณ์ USB แอปพลิเคชันของคุณต้องได้รับอนุญาตจาก ผู้ใช้

หมายเหตุ: หากแอปพลิเคชันของคุณใช้ Intent ของตัวกรองเพื่อค้นหาอุปกรณ์ USB เมื่อเชื่อมต่ออยู่ อุปกรณ์จะตรวจจับ หากผู้ใช้อนุญาตให้แอปพลิเคชันของคุณจัดการ Intent หากไม่ใช่ คุณต้องส่งคำขอ ได้รับสิทธิ์ในแอปพลิเคชันของคุณอย่างชัดเจนก่อนที่จะเชื่อมต่อกับอุปกรณ์

การขอสิทธิ์อย่างชัดแจ้งในบางสถานการณ์อาจจำเป็น เช่นเมื่อคุณ แอปพลิเคชันแจกแจงอุปกรณ์ USB ที่เชื่อมต่อแล้ว จากนั้นต้องการสื่อสารด้วย ข้อแรก คุณต้องตรวจสอบสิทธิ์ในการเข้าถึงอุปกรณ์ก่อนที่จะพยายามสื่อสารกับอุปกรณ์นั้น ถ้า ไม่ใช่ คุณจะได้รับข้อผิดพลาดเกี่ยวกับรันไทม์หากผู้ใช้ปฏิเสธสิทธิ์การเข้าถึงอุปกรณ์

หากต้องการได้รับอนุญาตอย่างชัดแจ้ง ให้สร้าง Broadcast Receiver ก่อน รีซีฟเวอร์นี้จะคอยฟัง Intent ที่จะประกาศเมื่อคุณโทรหา requestPermission() การโทรไปยัง requestPermission() แสดงกล่องโต้ตอบสำหรับ ผู้ใช้ที่ขออนุญาตเชื่อมต่อกับอุปกรณ์ โค้ดตัวอย่างต่อไปนี้จะแสดงวิธีการ สร้าง Broadcast Receiver:

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

หากต้องการลงทะเบียน Broadcast Receiver ให้เพิ่มค่านี้ในเมธอด onCreate() ใน กิจกรรม:

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

หากต้องการแสดงกล่องโต้ตอบที่ขอสิทธิ์เชื่อมต่อกับอุปกรณ์ ให้เรียกเมธอด requestPermission() ดังนี้

Kotlin

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

Java

UsbDevice device;
...
usbManager.requestPermission(device, permissionIntent);

เมื่อผู้ใช้ตอบกลับกล่องโต้ตอบ เครื่องรับการออกอากาศจะได้รับ Intent ที่มี เกินมา EXTRA_PERMISSION_GRANTED ซึ่งเป็นบูลีน ที่แสดงถึงคำตอบ ตรวจสอบส่วนเพิ่มเติมนี้เพื่อดูค่า "จริง" ก่อนเชื่อมต่อกับ อุปกรณ์

สื่อสารกับอุปกรณ์

การสื่อสารกับอุปกรณ์ USB อาจเป็นแบบซิงโครนัสหรืออะซิงโครนัสก็ได้ ไม่ว่าจะเป็นกรณีใด คุณ ควรสร้างชุดข้อความใหม่ สำหรับรับส่งข้อมูลทั้งหมด เพื่อไม่ให้บล็อก ชุดข้อความ UI ในการตั้งค่าการสื่อสารกับอุปกรณ์อย่างเหมาะสม คุณต้องขอรับ UsbInterface และ UsbEndpoint ของ อุปกรณ์ที่คุณต้องการสื่อสารและส่งคำขอในอุปกรณ์ปลายทางนี้ด้วย UsbDeviceConnection โดยทั่วไป โค้ดของคุณควร:

  • ตรวจสอบแอตทริบิวต์ของออบเจ็กต์ UsbDevice เช่น รหัสผลิตภัณฑ์ รหัสผู้ให้บริการ หรือคลาสของอุปกรณ์เพื่อพิจารณาว่าคุณต้องการสื่อสารกับผู้ให้บริการหรือไม่ อุปกรณ์
  • เมื่อแน่ใจแล้วว่าคุณต้องการสื่อสารกับอุปกรณ์ ให้ค้นหาอุปกรณ์ที่เหมาะสม UsbInterfaceที่คุณต้องการใช้เพื่อสื่อสารควบคู่กับ UsbEndpoint ของอินเทอร์เฟซนั้น อินเทอร์เฟซสามารถมี อุปกรณ์ปลายทางเพิ่มเติม และโดยทั่วไปจะมีอุปกรณ์ปลายทางอินพุตและเอาต์พุตสำหรับการสื่อสารแบบ 2 ทาง การสื่อสาร
  • เมื่อคุณพบปลายทางที่ถูกต้องแล้ว ให้เปิด UsbDeviceConnection ที่ปลายทางนั้น
  • ระบุข้อมูลที่ต้องการส่งบนปลายทางด้วยเมธอด bulkTransfer() หรือ controlTransfer() คุณควร ทําตามขั้นตอนนี้ในเทรดอื่นเพื่อป้องกันการบล็อกเทรด UI หลัก สำหรับข้อมูลเพิ่มเติม ข้อมูลเกี่ยวกับการใช้ชุดข้อความใน Android ได้ที่หัวข้อกระบวนการและ Threads

ข้อมูลโค้ดต่อไปนี้เป็นวิธีที่ไม่สำคัญในการโอนข้อมูลแบบซิงโครนัส รหัสของคุณ ควรมีตรรกะมากกว่านี้ในการค้นหาอินเทอร์เฟซและปลายทางที่ถูกต้องเพื่อสื่อสาร และควรทำการโอนข้อมูลในเทรดอื่นที่ไม่ใช่เทรด 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

หากต้องการส่งข้อมูลแบบไม่พร้อมกัน ให้ใช้คลาส UsbRequest เพื่อinitializeและqueueคำขอแบบไม่พร้อมกัน แล้วรอผลลัพธ์ ด้วย requestWait()

กำลังยุติการสื่อสารกับอุปกรณ์

เมื่อสื่อสารกับอุปกรณ์เสร็จแล้วหรือหากมีการถอดอุปกรณ์ออก ให้ปิด UsbInterface และ UsbDeviceConnection ภายใน กำลังโทรหา releaseInterface() และ close() หากต้องการฟังเหตุการณ์ที่แยกจากกัน สร้าง Broadcast Receiver ดังด้านล่าง

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

การสร้าง Broadcast Receiver ภายในแอปพลิเคชัน ไม่ใช่ไฟล์ Manifest เพื่อจัดการเหตุการณ์ที่ถูกตัดออกขณะทำงานอยู่เท่านั้น วิธีนี้จะทำให้เหตุการณ์ที่แยกจากกัน ส่งไปยังแอปพลิเคชันที่ทำงานอยู่ในปัจจุบันเท่านั้นและไม่ได้ประกาศไปยังแอปพลิเคชันทั้งหมด