『翻译』Android USB Host
USB Host
When your Android-powered device is in USB host mode, it acts as the USB host, powers the bus, and enumerates connected USB devices. USB host mode is supported in Android 3.1 and higher.
当您的Android设备处于USB主机模式时,它充当USB主机,为总线供电,并枚举连接的USB设备。Android 3.1及更高版本支持USB主机模式。
API Overview
Before you begin, it is important to understand the classes that you need to work with. The following table describes the USB host APIs in the android.hardware.usb
package.
在开始之前,你要理解你所要使用的类是非常重要的。以下表格描述了 android.hardware.usb 包中的
USB主机模式API。
Table 1. USB Host APIs
Class | Description |
---|---|
UsbManager |
Allows you to enumerate and communicate with connected USB devices. 允许你枚举和通信与已连接的USB设备。 |
UsbDevice |
Represents a connected USB device and contains methods to access its identifying information, interfaces, and endpoints. 表示一个已连接的USB设备,包含访问其标识信息,接口和端点的方法。 |
UsbInterface |
Represents an interface of a USB device, which defines a set of functionality for the device. A device can have one or more interfaces on which to communicate on. 表示USB设备的接口,它为设备定义了一组功能。设备可以有一个或多个接口在其上进行通信。 |
UsbEndpoint |
Represents an interface endpoint, which is a communication channel for this interface. An interface can have one or more endpoints, and usually has input and output endpoints for two-way communication with the device. 表示一个接口端点,它是该接口的通信通道。接口可以具有一个或多个端点,通常具有用于与设备进行双向通信的输入和输出端点。 |
UsbDeviceConnection |
Represents a connection to the device, which transfers data on endpoints. This class allows you to send data back and forth sychronously or asynchronously. 表示与设备的连接,该设备在端点上传输数据。该类允许您以异步方式或异步方式来回发送数据。 |
UsbRequest |
Represents an asynchronous request to communicate with a device through a 表示通过 |
UsbConstants |
Defines USB constants that correspond to definitions in linux/usb/ch9.h of the Linux kernel. 定义了 与Linux内核的linux / usb / ch9.h中的定义相对应的 USB常量。 |
In most situations, you need to use all of these classes (UsbRequest
is only required if you are doing asynchronous communication) when communicating with a USB device. In general, you obtain a UsbManager
to retrieve the desired UsbDevice
. When you have the device, you need to find the appropriate UsbInterface
and the UsbEndpoint
of that interface to communicate on. Once you obtain the correct endpoint, open a UsbDeviceConnection
to communicate with the USB device.
在大多数情况下,你要与设备通信时要使用所有这些类(UsbRequest 只是在进行异步通信时需要使用),一般来说,你获得一个UsbManager
来检索所需的UsbDevice
。当您拥有该设备时,您需要找到适当的UsbInterface
和该UsbEndpoint 接口
进行通信。一旦获得正确的端点,打开一个UsbDeviceConnection
与USB设备进行通信。
Android Manifest Requirements
The following list describes what you need to add to your application's manifest file before working with the USB host APIs:
以下列表描述了在使用USB主机API之前,你需要添加到应用程序的清单文件中:
- Because not all Android-powered devices are guaranteed to support the USB host APIs, include a
<uses-feature>
element that declares that your application uses theandroid.hardware.usb.host
feature. - 因为并非所有Android设备都能保证支持USB主机API,包括
<uses-feature> 元素,
声明应用程序使用该android.hardware.usb.host
功能。 - Set the minimum SDK of the application to API Level 12 or higher. The USB host APIs are not present on earlier API levels.
- 设置应用的最小SDK API为12或这更高。在早期API上不存在USB主机API。
- If you want your application to be notified of an attached USB device, specify an
<intent-filter>
and<meta-data>
element pair for theandroid.hardware.usb.action.USB_DEVICE_ATTACHED
intent in your main activity. The<meta-data>
element points to an external XML resource file that declares identifying information about the device that you want to detect. - 如果你希望你的应用程序在USB设备连接时,被通知,指定
<intent-filter>
和<meta-data>
元素对用于android.hardware.usb.action.USB_DEVICE_ATTACHED
在你的main activity。该<meta-data>
元素指向一个外部XML资源文件,该文件声明了要检测的设备的信息。
In the XML resource file, declare <usb-device>
elements for the USB devices that you want to filter. The following list describes the attributes of <usb-device>
. In general, use vendor and product ID if you want to filter for a specific device and use class, subclass, and protocol if you want to filter for a group of USB devices, such as mass storage devices or digital cameras. You can specify none or all of these attributes. Specifying no attributes matches every USB device, so only do this if your application requires it:
在XML资源文件中,声明<usb-device>
要过滤的USB设备的元素。以下列表描述了属性 <usb-device>
。一般来说,如果要过滤特定设备使用VID,PID即可,如果要过滤一组USB设备(如大容量存储设备或数码相机)要使用并使用类,子类和协议进行过滤。您可以指定这些属性中的任何一个或全部。指定没有属性匹配每个USB设备,所以只有在您的应用程序需要它时才需要做:
vendor-id
product-id
class
subclass
protocol
(device or interface)
Save the resource file in the res/xml/
directory. The resource file name (without the .xml extension) must be the same as the one you specified in the <meta-data>
element. The format for the XML resource file is in the example below.
将资源文件保存在res/xml/
目录中。资源文件名(不含.xml扩展名)必须与<meta-data>
元素中指定的文件名相同 。XML资源文件的格式在下面的 示例中。
Manifest and resource file examples 示例
The following example shows a sample manifest and its corresponding resource file:
<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>
In this case, the following resource file should be saved in res/xml/device_filter.xml
and specifies that any USB device with the specified attributes should be filtered:
以下资源文件应保存为 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>
Working with Devices
When users connect USB devices to an Android-powered device, the Android system can determine whether your application is interested in the connected device. If so, you can set up communication with the device if desired. To do this, your application has to:
当用户将USB设备连接到Android设备时,Android系统可以确定您的应用程序是否对连接的设备感兴趣。如果是,您可以建立与设备的通信。为此,您的应用程序必须:
- Discover connected USB devices by using an intent filter to be notified when the user connects a USB device or by enumerating USB devices that are already connected.通过使用过滤器通知,当用户连接一个USB设备时,发现设备,或者枚举已经连接的USB设备。
- Ask the user for permission to connect to the USB device, if not already obtained. 要求用户给连接的USB设备授权,在没有获取授权时。
- Communicate with the USB device by reading and writing data on the appropriate interface endpoints.使用指定的终端接口与设备通信读写数据
Discovering a device
Your application can discover USB devices by either using an intent filter to be notified when the user connects a device or by enumerating USB devices that are already connected. Using an intent filter is useful if you want to be able to have your application automatically detect a desired device. Enumerating connected USB devices is useful if you want to get a list of all connected devices or if your application did not filter for an intent.
您的应用程序可以通过使用 过滤器来发现USB设备,以便在用户连接设备时进行通知,或枚举已连接的USB设备。如果您希望能够让应用程序自动检测到所需的设备,则使用过滤器很有用。枚举所连接的USB设备如果要获取所有连接的设备,或者应用程序没有进行任何过滤。
Using an intent filter 使用intent过滤器
To have your application discover a particular USB device, you can specify an intent filter to filter for the android.hardware.usb.action.USB_DEVICE_ATTACHED
intent. Along with this intent filter, you need to specify a resource file that specifies properties of the USB device, such as product and vendor ID. When users connect a device that matches your device filter, the system presents them with a dialog that asks if they want to start your application. If users accept, your application automatically has permission to access the device until the device is disconnected.
要使您的应用程序发现一个特定的USB设备,您可以指定一个intent过滤器 来过滤android.hardware.usb.action.USB_DEVICE_ATTACHED
intent。除了此intent过滤器之外,还需要指定一个资源文件,该资源文件指定USB设备的属性,例如VID和PID。当用户连接的设备与过滤器匹配时,系统会向他们显示一个对话框,询问他们是否要启动您的应用程序。如果用户允许,您的应用程序自动具有访问设备的权限,直到设备断开连接。
The following example shows how to declare the intent filter:示例
<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>
The following example shows how to declare the corresponding resource file that specifies the USB devices that you're interested in:资源文件示例
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-device vendor-id="1234" product-id="5678" />
</resources>
In your activity, you can obtain the UsbDevice
that represents the attached device from the intent like this:在您的activity中,您可以获得UsbDevice,
从intent表示连接的设备:
UsbDevice device = (UsbDevice) intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
Enumerating devices 枚举
If your application is interested in inspecting all of the USB devices currently connected while your application is running, it can enumerate devices on the bus. Use the getDeviceList()
method to get a hash map of all the USB devices that are connected. The hash map is keyed by the USB device's name if you want to obtain a device from the map.
如果您的应用程序有兴趣检查应用程序运行时当前连接的所有USB设备,它可以枚举总线上的设备。使用getDeviceList()
方法获取所有连接的USB设备的hash map。如果你想获取一个设备从hash map,hash map是以USB设备名为KEY进行存储的。
UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
...
HashMap<String, UsbDevice> deviceList = manager.getDeviceList();
UsbDevice device = deviceList.get("deviceName");
If desired, you can also just obtain an iterator from the hash map and process each device one by one:
你也可以获取一个迭代器从hash map,一个一个的遍历一遍。
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
}
Obtaining permission to communicate with a device 获取设备通信权限
Before communicating with the USB device, your application must have permission from your users.
在与USB设备通信之前,你的App要获取用户授权。
Note: If your application uses an intent filter to discover USB devices as they're connected, it automatically receives permission if the user allows your application to handle the intent. If not, you must request permission explicitly in your application before connecting to the device.
如果你的app使用intent过滤器,在设备连接来发现设备,如果用户允许控制intent,你的app会自动获取权限。如果没有使用intent过滤器,你必须要在设备连接之前获取权限。
Explicitly asking for permission might be neccessary in some situations such as when your application enumerates USB devices that are already connected and then wants to communicate with one. You must check for permission to access a device before trying to communicate with it. If not, you will receive a runtime error if the user denied permission to access the device.
在某些情况下,显式请求许可可能是必需的,例如当您的应用程序枚举已连接的USB设备,然后要与其进行通信时。在尝试与之通信之前,您必须检查访问设备的权限。如果不是 ,或者,如果用户拒绝访问设备的权限,将收到运行时错误。
To explicitly obtain permission, first create a broadcast receiver. This receiver listens for the intent that gets broadcast when you call requestPermission()
. The call to requestPermission()
displays a dialog to the user asking for permission to connect to the device. The following sample code shows how to create the broadcast receiver:
要显式获取权限,首先创建广播接收者。该接收器侦听intent,获取广播,当调用requestPermission()时
。调用requestPermission()
显示一个对话框给用户,要求连接到设备的权限。以下示例代码显示如何创建广播接收器:
private static final String ACTION_USB_PERMISSION =
"com.android.example.USB_PERMISSION";
private final BroadcastReceiver mUsbReceiver = 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);
}
}
}
}
};
To register the broadcast receiver, add this in your onCreate()
method in your activity:
为注册广播接收者,需要在你app的activity的onCreate()方法中添加如下代码:
UsbManager mUsbManager = (UsbManager) getSystemService(Context.USB_SERVICE);
private static final String ACTION_USB_PERMISSION =
"com.android.example.USB_PERMISSION";
...
mPermissionIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
registerReceiver(mUsbReceiver, filter);
To display the dialog that asks users for permission to connect to the device, call the requestPermission()
method:
显示窗口要求用户获取连接设备的权限。
UsbDevice device;
...
mUsbManager.requestPermission(device, mPermissionIntent);
When users reply to the dialog, your broadcast receiver receives the intent that contains the EXTRA_PERMISSION_GRANTED
extra, which is a boolean representing the answer. Check this extra for a value of true before connecting to the device.
当用户回复对话框时,广播接收者将收到包含EXTRA_PERMISSION_GRANTED
extra 的intent ,这是表示答案的布尔值。在连接到设备之前,请检查这个extra的值是否为true。
Communicating with a device 设备通信
Communication with a USB device can be either synchronous or asynchronous. In either case, you should create a new thread on which to carry out all data transmissions, so you don't block the UI thread. To properly set up communication with a device, you need to obtain the appropriate UsbInterface
and UsbEndpoint
of the device that you want to communicate on and send requests on this endpoint with a UsbDeviceConnection
. In general, your code should:
与USB设备通信可以使用同步或者异步方式。你需要创建一个线程来管理数据传输,这样就不会阻塞UI线程。正确的建立与设备的通信,你需要获取设备合适的UsbInterface
和 UsbEndpoint 用以与设备通信和发送请求端点用 UsbDeviceConnection。一般来说,你的代码应该这样:
- Check a
UsbDevice
object's attributes, such as product ID, vendor ID, or device class to figure out whether or not you want to communicate with the device. - 检查
UsbDevice 的属性,如VID,PID,设备类,判断是不是你app想要通信的那个设备。
- When you are certain that you want to communicate with the device, find the appropriate
UsbInterface
that you want to use to communicate along with the appropriateUsbEndpoint
of that interface. Interfaces can have one or more endpoints, and commonly will have an input and output endpoint for two-way communication. - 如果你app确定了想要进行通信的设备,请找到适当的
UsbInterface
要用于与该UsbEndpoint接口
通信。接口可以具有一个或多个端点,并且通常将具有用于双向通信的in和out端点。 - When you find the correct endpoint, open a
UsbDeviceConnection
on that endpoint. - 当找到了正确的端点,使用
UsbDeviceConnection
打开端点。 - Supply the data that you want to transmit on the endpoint with the
bulkTransfer()
orcontrolTransfer()
method. You should carry out this step in another thread to prevent blocking the main UI thread. For more information about using threads in Android, see Processes and Threads. - 使用
bulkTransfer()
orcontrolTransfer()
方法 在端点上传输 数据。你应该在另一个线程中执行此步骤,以防止阻塞主UI线程。有关在Android中使用线程的更多信息,请参阅进程和线程。
The following code snippet is a trivial way to do a synchronous data transfer. Your code should have more logic to correctly find the correct interface and endpoints to communicate on and also should do any transferring of data in a different thread than the main UI thread:
以下代码片段是进行同步数据传输的简单方法。您的代码应该有更多的逻辑来找到正确的接口和端点进行通信,并且应该在与主UI线程不同的线程中进行数据传输:
private Byte[] bytes;
private static int TIMEOUT = 0;
private boolean forceClaim = true;
...
UsbInterface intf = device.getInterface(0);
UsbEndpoint endpoint = intf.getEndpoint(0);
UsbDeviceConnection connection = mUsbManager.openDevice(device);
connection.claimInterface(intf, forceClaim);
connection.bulkTransfer(endpoint, bytes, bytes.length, TIMEOUT); //do in another thread
To send data asynchronously, use the UsbRequest
class to initialize
and queue
an asynchronous request, then wait for the result with requestWait()
.
要异步发送数据,请使用UsbRequest
类 initialize
和queue
异步请求,然后等待结果 使用requestWait()
。
For more information, see the AdbTest sample, which shows how to do asynchronous bulk transfers, and the MissileLauncher sample, which shows how to listen on an interrupt endpoint asynchronously.
有关详细信息,请参阅AdbTest示例,其中显示了如何进行异步批量传输 ,MissileLauncher示例 该示例显示了如何异步侦听中断端点。
官网的示例已经转到Github上了。
https://github.com/aosp-mirror/platform_development/tree/master/samples/USB/MissileLauncher
https://github.com/aosp-mirror/platform_development/tree/master/samples/USB
Terminating communication with a device 结束与设备的通信
When you are done communicating with a device or if the device was detached, close the UsbInterface
and UsbDeviceConnection
by calling releaseInterface()
and close()
. To listen for detached events, create a broadcast receiver like below:
当您完成与设备的通信或设备分离时,关闭UsbInterface
和 UsbDeviceConnection
通过 调用 releaseInterface()
和 close()
。要监听分离的事件,请创建如下广播接收器:
BroadcastReceiver mUsbReceiver = 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
}
}
}
};
Creating the broadcast receiver within the application, and not the manifest, allows your application to only handle detached events while it is running. This way, detached events are only sent to the application that is currently running and not broadcast to all applications.
在应用程序中创建广播接收者,而不是在清单中,允许应用程序在运行时仅处理分离的事件。这样,分离的事件只会发送到当前运行的应用程序,而不是广播到所有的应用程序。