Android通信之USB
通信方式
- BLE蓝牙/传统蓝牙
- USB
- 局域网Socket
- 热点
- WebSocket
- Netty
等等…
USB通信介绍
即插即用,可热插拔。
USB2.0 :理论速度是每秒480Mbps(约每秒60MB) USB3.0 :理论速度能够达到每秒5Gbps(约为每秒625MB)
USB2.0总线提供最大达5v电压、500mA电流,USB3.0 可达1A。大部分USB外设无需单独的供电系统。
通信包括USB主机(USB HOST)、 USB设备(USB DEVICE)。
android支持USB accessory模式和USB host模式。通过这两种模式,android支持各种各样的USB 外围设备和USB 配件(硬件需要实现android配件协议)。
OTG协议
OTG是On-The-Go的缩写。随着USB技术的发展,使得PC和周边设备能够通过简单的方式、适度的制造成本,将各种数据传输速度的设备连接在一起。但都是通过USB连接到PC,并在PC的控制下进行数据交换。这种方便的交换方式,一旦离开了PC,各设备间无法利用USB口进行操作,因为没有一个从设备能够充当PC一样的主机。OTG技术就是在没有Host的情况下,实现设备间的数据传送。
USB HOST
Android工作在USB Host模式下,则连接到Android上的USB设备把Android类似的看作是一台主机,例如将鼠标、键盘插入则可以使用键盘、鼠标来操作Android系统。
做以下设置可将手机作为HOST端
//manifest
<uses-permission android:name="android.hardware.usb.host" />
USB DEVICE
将android手机设置为USB accessory模式
//manifest
<uses-feature android:name="android.hardware.usb.accessory" />
<!--过滤USB设备 product-id vendor-id-->
<activity android:name=".UsbAccessoryActivity">
<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>
xml/accessory_filter.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-accessory
manufacturer="Google, Inc."
model="loren"
type="demo"
version="1.0"/>
</resources>
基本流程
HOST => 订阅插拔广播、UsbManager枚举连接的设备、根据设备vendorId和productId找出指定设备、请求usb权限、打开Accessory模式、寻找设备接口、分配IN | OUT端点、打开连接通道、循环接收消息 |
ACCESSORY => 订阅插拔广播、申请权限、打开设备、循环接收
使用
订阅插拔广播
val filter = IntentFilter(ACTION_USB_PERMISSION)
registerReceiver(usbReceiver, filter)
val filter1 = IntentFilter(UsbManager.ACTION_USB_ACCESSORY_DETACHED)
registerReceiver(usbReceiver, filter1)
枚举连接设备
private fun enumerateDevice(mUsbManager: UsbManager?) {
log("开始进行枚举设备!")
if (mUsbManager == null) {
log("创建UsbManager失败,请重新启动应用!")
return
} else {
val deviceList = mUsbManager.deviceList
if (!deviceList.isEmpty()) {
val deviceIterator = deviceList.values.iterator()
while (deviceIterator.hasNext()) {
val device = deviceIterator.next()
log("deviceInfo: ${device.vendorId} , ${device.productId}")
usbDevice = device
if (mUsbManager.hasPermission(usbDevice)) {
initAccessory(usbDevice)
} else {
val mPermissionIntent = PendingIntent.getBroadcast(this, 0, Intent(ACTION_USB_PERMISSION), 0)
mUsbManager.requestPermission(usbDevice, mPermissionIntent)
}
}
} else {
log("device list 为空")
}
}
}
请求权限并打开
val mPermissionIntent = PendingIntent.getBroadcast(this, 0,
Intent(ACTION_USB_PERMISSION), 0)
mUsbManager.requestPermission(usbDevice, mPermissionIntent)
//打开
private fun initAccessory(usbDevice: UsbDevice) {
val usbDeviceConnection = mUsbManager.openDevice(usbDevice)
if (usbDeviceConnection == null) {
log("请连接USB")
return
}
//根据AOA协议打开Accessory模式
initStringControlTransfer(usbDeviceConnection, 0, "Google, Inc.") // MANUFACTURER
initStringControlTransfer(usbDeviceConnection, 1, "loren") // MODEL
initStringControlTransfer(usbDeviceConnection, 2, "loren desc") // DESCRIPTION
initStringControlTransfer(usbDeviceConnection, 3, "1.0") // VERSION
initStringControlTransfer(usbDeviceConnection, 4, "http://www.android.com") // URI
initStringControlTransfer(usbDeviceConnection, 5, "0123456789") // SERIAL
usbDeviceConnection.controlTransfer(0x40, 53, 0, 0, byteArrayOf(), 0, 100)
usbDeviceConnection.close()
getDeviceInterface()
}
寻找设备接口
//寻找设备接口
private fun getDeviceInterface() {
log("interfaceCounts : ${usbDevice.interfaceCount}")
for (i in 0 until usbDevice.interfaceCount) {
val intf = usbDevice.getInterface(i)
if (i == 0) {
interface1 = intf
assignEndpoint(intf)
openDevice(intf)
}
}
}
分配端点IN | OUT |
private fun assignEndpoint(mInterface: UsbInterface) {
for (i in 0 until mInterface.endpointCount) {
val ep = mInterface.getEndpoint(i)
// look for bulk endpoint
if (ep.type == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (ep.direction == UsbConstants.USB_DIR_OUT) {
epBulkOut = ep
println("""Find the BulkEndpointOut,index:$i,使用端点号:${epBulkOut!!.endpointNumber}""")
} else {
epBulkIn = ep
println("""Find the BulkEndpointIn:index:$i,使用端点号:${epBulkIn!!.endpointNumber}""")
}
}
}
}
建立连接
private fun openDevice(mInterface: UsbInterface?) {
var conn: UsbDeviceConnection? = null
if (mUsbManager.hasPermission(usbDevice)) {
conn = mUsbManager.openDevice(usbDevice)
} else {
log("无权限")
val mPermissionIntent = PendingIntent.getBroadcast(this, 0, Intent(""), 0)
mUsbManager.requestPermission(usbDevice, mPermissionIntent)
}
if (conn == null) {
return
}
if (conn.claimInterface(mInterface, true)) {
usbDeviceConnection = conn
// 到此你的android设备已经连上设备
if (usbDeviceConnection != null)
log("open设备成功!")
val mySerial = usbDeviceConnection!!.serial
log("设备serial number:$mySerial")
} else {
log("无法打开连接通道")
conn.close()
}
loopReceiverMessage()
}
循环接收
private fun loopReceiverMessage() {
mThreadPool.execute {
while (isReceiverMessage) {
if (usbDeviceConnection != null && epBulkIn != null) {
val i = usbDeviceConnection!!.bulkTransfer(epBulkIn, mBytes, mBytes.size, 3000)
if (i > 0) {
//mBytes
}
}
}
}
}
HOST向ACCESSORY发送消息
private fun sendMessageToPoint(buffer: ByteArray) {
if (null == usbDeviceConnection) return
mThreadPool.execute {
val i = usbDeviceConnection!!.bulkTransfer(epBulkOut, buffer, buffer.size, 3000)
if (i > 0) {
log("发送成功")
} else {
log("发送失败")
}
}
}
ACCESSORY打开设备
private fun openAccessory(usbAccessory: UsbAccessory) {
mParcelFileDescriptor = mUsbManager.openAccessory(usbAccessory)
if (mParcelFileDescriptor != null) {
log("打开成功")
val fileDescriptor = mParcelFileDescriptor!!.fileDescriptor
mFileInputStream = FileInputStream(fileDescriptor)
mFileOutputStream = FileOutputStream(fileDescriptor)
mThreadPool.execute {
var i = 0
while (i >= 0) {
try {
i = mFileInputStream!!.read(mBytes)
} catch (e: Exception) {
e.printStackTrace()
break
}
if (i > 0) {
log("接收到消息:${String(mBytes, 0, i)}")
}
}
}
} else {
log("mParcelFileDescriptor == null")
}
}
ACCESSORY向HOST发送消息
mThreadPool.execute {
try {
mFileOutputStream?.write("服务端发送消息".toByteArray())
} catch (e: IOException) {
e.printStackTrace()
}
}
注意
- 设备选择和端口的选择
项目中遇到过用第二个端口发送导致设备逻辑插拔,用第一个正常发送/接收
- 单次发送长度不能超过16384字节