USB 호스트 개요

Android 지원 기기가 USB 호스트 모드일 때 USB 호스트 역할을 하고 버스에 전원을 공급하며 연결되어 있는 USB 장치를 열거합니다. USB 호스트 모드는 Android 3.1 이상에서 지원됩니다.

API 개요

시작하기 전에 먼저 사용해야 하는 클래스를 이해하는 것이 중요합니다. 이 다음 표에서는 android.hardware.usb 패키지의 USB 호스트 API를 설명합니다.

표 1. USB 호스트 API

클래스 설명
UsbManager 이 클래스를 사용하면 연결된 USB 기기를 열거하고 기기와 통신할 수 있습니다.
UsbDevice 연결된 USB 기기를 나타내며 식별에 액세스하는 메서드가 포함되어 있습니다. IP 주소, 인터페이스, 엔드포인트가 있습니다
UsbInterface USB 장치의 인터페이스를 나타내며, 장치에 대한 기능 집합을 정의합니다. 있습니다. 기기에는 통신할 인터페이스가 하나 이상 있을 수 있습니다.
UsbEndpoint 인터페이스 엔드포인트를 나타내며 이 인터페이스의 통신 채널입니다. 인터페이스에는 하나 이상의 엔드포인트가 있을 수 있으며, 일반적으로 다음에 대한 입력 및 출력 엔드포인트가 있습니다. 장치와의 양방향 통신입니다.
UsbDeviceConnection 엔드포인트에서 데이터를 전송하는 기기와의 연결을 나타냅니다. 이 수업 를 사용하면 동기식 또는 비동기식으로 데이터를 주고받을 수 있습니다.
UsbRequest UsbDeviceConnection을 통해 기기와 통신하는 비동기식 요청을 나타냅니다.
UsbConstants Linux의 linux/usb/ch9.h에 있는 정의에 해당하는 USB 상수를 정의합니다. 커널입니다

대부분의 경우 이러한 클래스를 모두 사용해야 합니다 (UsbRequest는 비동기 통신을 하는 경우에만 필요). USB 장치와 통신할 때 사용됩니다. 일반적으로 원하는 UsbDevice를 검색하려면 UsbManager를 얻어야 합니다. 기기가 있으면 적절한 UsbInterface와 기기의 UsbEndpoint를 찾아야 합니다. 통신할 수 있습니다 적절한 엔드포인트를 얻었으면 USB 기기와 통신할 UsbDeviceConnection을 열어야 합니다.

Android manifest 요구사항

다음 목록은 선언하기 전에 애플리케이션의 매니페스트 파일에 추가해야 하는 항목을 설명합니다. USB 호스트 API로 작업하기:

  • 모든 Android 지원 기기가 USB 호스트 API를 지원한다고 보장할 수 없기 때문에 애플리케이션이 사용한다고 선언하는 <uses-feature> 요소를 포함해야 합니다. android.hardware.usb.host 기능
  • 애플리케이션의 최소 SDK를 API 레벨 12 이상으로 설정해야 합니다. USB 호스트 API는 API에 액세스할 수 있습니다
  • 애플리케이션이 연결된 USB 장치에 대한 알림을 받도록 하려면 <intent-filter><meta-data> 요소 쌍: android.hardware.usb.action.USB_DEVICE_ATTACHED 인텐트를 처리하세요. 이 <meta-data> 요소는 다음을 선언하는 외부 XML 리소스 파일을 가리킵니다. 감지하려는 기기에 관한 식별 정보

    XML 리소스 파일에서 USB의 <usb-device> 요소를 선언합니다. 선택할 수 있습니다. 다음 목록은 <usb-device> 일반적으로 필터링을 하려면 공급업체 및 제품 ID를 사용하세요. 특정 기기에만 사용할 수 있고 그룹을 필터링하려면 클래스, 서브클래스, 프로토콜을 사용하세요. 대량 저장 장치 또는 디지털 카메라와 같은 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로 설정하고, 지정된 속성을 필터링해야 합니다.

<?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 기기를 검색합니다. 이미 연결된 USB 장치를 열거하여 USB 장치를 연결합니다.
  2. USB 기기에 연결할 수 있는 권한이 아직 없다면 사용자에게 권한을 요청합니다.
  3. 적절한 인터페이스에서 데이터를 읽고 써서 USB 기기와 통신합니다. 엔드포인트가 있습니다

기기 찾기

애플리케이션은 인텐트 필터를 사용하여 USB 기기를 검색할 수 있으며 사용자가 장치를 연결하거나 이미 연결된 USB 장치를 열거하는 것입니다. 인텐트 필터는 애플리케이션이 잘못된 인텐트를 자동으로 감지하도록 하려는 경우에 유용합니다. 변경할 수 있습니다. 연결된 USB 장치를 열거하는 것은 모든 USB 장치의 목록을 가져오려는 경우에 애플리케이션이 인텐트를 필터링하지 않았는지 여부

인텐트 필터 사용

애플리케이션이 특정 USB 기기를 검색하도록 하려면 인텐트 필터를 지정하여 android.hardware.usb.action.USB_DEVICE_ATTACHED 인텐트 필터 또한 이 인텐트 필터를 사용하려면 USB의 속성을 지정하는 리소스 파일을 지정해야 합니다. 기기(예: 제품 및 공급업체 ID) 사용자가 내 기기와 일치하는 기기를 연결하는 경우 필터를 사용하면 애플리케이션을 시작할지 묻는 대화상자가 표시됩니다. 사용자가 수락하면 애플리케이션은 기기의 연결이 끊어졌습니다.

다음 예는 인텐트 필터를 선언하는 방법을 보여줍니다.

<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를 가져올 수 있습니다. 연결된 기기를 삭제할 수 있습니다.

Kotlin

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

자바

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

기기 열거

애플리케이션이 현재 연결된 모든 USB 장치를 검사하려는 경우 애플리케이션이 실행되는 동안 버스의 장치를 열거할 수 있습니다. getDeviceList() 메서드를 사용하여 모든 항목의 해시 맵 가져오기 연결되어 있는 것을 볼 수 있습니다. 원하는 경우 해시 맵은 USB 기기의 이름으로 키가 지정됩니다. 가져올 수 있습니다.

Kotlin

val manager = getSystemService(Context.USB_SERVICE) as UsbManager
...
val deviceList = manager.getDeviceList()
val device = deviceList.get("deviceName")

자바

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
}

자바

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 기기와 통신하려면 애플리케이션에 먼저 있습니다.

참고: 애플리케이션에서 인텐트 필터를 사용하여 연결된 USB 기기를 찾으면 자동으로 권한을 부여할 수 있습니다. 그렇지 않은 경우 명시적으로 권한을 부여해야 합니다.

다음과 같은 일부 상황에서는 명시적으로 권한을 요청해야 할 수 있습니다. 애플리케이션은 이미 연결된 USB 장치를 열거하고 있습니다 기기와 통신하려고 하기 전에 기기에 액세스할 수 있는 권한이 있는지 확인해야 합니다. 만약 사용자가 기기 액세스 권한을 거부하면 런타임 오류가 발생합니다.

명시적으로 권한을 얻으려면 먼저 broadcast receiver를 생성하세요. 이 수신기는 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")
                }
            }
        }
    }
}

자바

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)

자바

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)

자바

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

사용자가 대화상자에 답장하면 broadcast receiver는 불리언인 추가 EXTRA_PERMISSION_GRANTED개 는 답변을 나타냅니다. 있습니다.

기기와 통신

USB 기기와의 통신은 동기적 또는 비동기적일 수 있습니다. 두 경우 모두 사용자는 모든 데이터 전송을 수행할 새 스레드를 생성해야 하므로 UI 스레드 기기와의 통신을 올바르게 설정하려면 UsbInterfaceUsbEndpoint UsbDeviceConnection를 사용하여 이 엔드포인트에서 통신하고 요청을 보내려는 기기의 ID입니다. 일반적으로 코드에서 다음과 같이 해야 합니다.

  • UsbDevice 객체의 속성(예: 제품 ID, 공급업체 ID, 또는 장치 클래스를 사용하여 있습니다.
  • 기기와 통신해야 할 확신이 있다면 커뮤니케이션에 사용할 UsbInterface: 해당 인터페이스의 적절한 UsbEndpoint를 사용해야 합니다. 인터페이스는 두 개 이상의 엔드포인트에 연결할 수 있으며, 일반적으로 양방향 전송 트래픽을 위한 커뮤니케이션에 사용할 수 있습니다.
  • 올바른 엔드포인트를 찾으면 UsbDeviceConnection을 엽니다. kube-APIserver로 전송합니다
  • bulkTransfer() 또는 controlTransfer() 메서드를 사용하여 엔드포인트에서 전송하려는 데이터를 제공해야 합니다. 해야 할 일 다른 스레드에서 이 단계를 수행하여 기본 UI 스레드를 차단하지 못하도록 합니다. 자세한 내용은 Android에서 스레드 사용에 관한 자세한 내용은 프로세스 및 스레드.

다음 코드 스니펫은 동기식 데이터 전송을 실행하는 간단한 방법입니다. 코드 통신해야 할 올바른 인터페이스와 엔드포인트를 정확하게 찾을 수 있는 더 많은 논리가 있어야 합니다 기본 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
        }
    }
}

자바

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 클래스를 사용하여 비동기식 요청을 initializequeue한 후 결과를 기다립니다. requestWait().

기기와의 통신 종료

기기와의 통신이 완료되었거나 기기가 분리되었다면 다음과 같이 UsbInterfaceUsbDeviceConnection를 닫습니다. 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
            }
        }
    }
}

자바

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를 생성하면 애플리케이션이 실행되는 동안 분리된 이벤트만 처리하도록 할 수 있습니다. 이렇게 하면 분리된 이벤트가 현재 실행 중인 애플리케이션으로만 전송되며 모든 애플리케이션에 브로드캐스트되지는 않습니다.