当你的Android主机是处于USB主模式下,它充当USB主机,开启设备并列举出所有已连接的USB设备。这种模式在Android 3.1及以上的版本支持。
API预览
在开始开发USB Host之前,需要了解android.hardware.usb包下USB host 的API:
Class | Description |
---|
UsbManager | 可以列举出USB设备,并和设备交互 | UsbDevice | 表示已连接的USB设备,包含了访问设备标识信息的方法、接口和挂载点 | UsbInterface | 表示设备接口,定义了一些列的功能函数。一个设备可以包含一个或多个接口 | UsbEndPoint | 表示一个挂载点接口,定义了一个交互通道。一个接口可以包含一个或多个挂载点。通常都包含用于和USB设备交互输入输出的双向端点 | UsbDeviceConnection | 表示到USB设备的一个基于端点通信的接口,这个类支持同步和异步数据的来回通讯 | UsbRequest | 表示一个异步的,基于UsbDeviceConnection通讯的异步请求 | UsbConstants | 定义了一些USB常量,与linux内核中的 Linux/usb/ch9.h中定义的一致 |
在USB设备交互的情况下,你需要用到所有的这些类(除了交互的时候,需要用到UsbRequest)。首先需要获取UsbManager对象来查询目标设备UsbDevice。当获得设备UsbDevice之后,你需要找到适当的UsbInterface以及该接口对应的UsbEndPoint,从而进行交互。一但拿到正确的端点,打开连接UsbDeviceConnection,以此和Usb设备进行交互。
Manifest文件的必要节点
下面的列表,列出了在使用Usb host的API之前,你所需要添加到Manifest文件中的东西:
由于不能确保所有的Android设备都支持USB host 的API , 我们可以在Manifest文件中加入 节点,来声明我们的应用需要android.hardware.usb.host 支持.
设置应用程序的最低支持的SDK版本为12或者更高. USB 主机模式的 API 在12以前的版本上不适用.
如果希望你的应用能够接收到USB设备挂在的通知,则需要在你的主Activity的android.hardware.usb.action.USB_DEVICE_ATTACHED过滤器中指定 和 节点对. 节点对应着一个外部XML 资源文件,这个资源文件定义了你想检测的设备的一些唯一标识信息.
在XML资源文件中,通过声明节点 来过滤你想要过滤的设备. 以下列表列出了 的 属性. 总的来说, 使用 vendor-id 和 product-id 来过滤指定的USB设备,使用 class, subclass 和 protocol 来过滤一组设备, 比如说大容量存储设备和数码相机. 你可以指定所有的属性或者一个都不指定.如果你什么属性都不设置,就将匹配所有的USB设备,当程序需要的时候,你可以这么指定:
vendor-id
product-id
class
subclass
protocol (device or interface)
该资源文件保存在 res/xml/ 目录下. 它的文件名(不包含.xml后缀名) 必须和 节点指定的名字一样. 文件的格式样式如下example: 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>
在上面的manifest文件下,对应的资源文件必须保存为 res/xml/device_filter.xml,并且使用特定的属性来过滤任何USB设备:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<usb-device product-id="4100" vendor-id="1204" />
<usb-device product-id="4356" vendor-id="1204" />
<usb-device product-id="22288" vendor-id="1155" />
<usb-device product-id="0xAA97" vendor-id="0xAAAA" />
</resources>
代码实例
public class UsbHostTool {
private final int USB_CONNECTED = 0;
private final int USB_DISCONNECT = 1;
private Context mContext = null;
private static final String WK_ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
private UsbDevice mUsbDevice = null;
private UsbDeviceConnection mDeviceConnection;
private UsbEndpoint endPointForSendData;
private UsbEndpoint endPointForUsb;
private UsbEndpoint endPointForGetData;
private int usbState = USB_DISCONNECT;
private FiFoUsbStream fiFoUsbStream = null;
private ExecutorService executorService = null;
private ScheduledThreadPoolExecutor mScheduledThreadPoolExecutor = null;
private IUsbHostDataListner iUsbHostDataListner;
private int interfaceCount = 0;
private UsbInterface[] mInterfaceArray = new UsbInterface[5];
private byte[] revUsbData = new byte[8 * 1024];
private final int TIMEOUT = 200;
public UsbHostTool(Context context, IUsbHostDataListner iUsbHostDataListner) {
this.mContext = context;
this.iUsbHostDataListner = iUsbHostDataListner;
executorService = Executors.newScheduledThreadPool(5);
mScheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(5);
usbDevicesInit(context);
}
public void registerMyReceivers(Context mContext) {
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(WK_ACTION_USB_PERMISSION);
mContext.registerReceiver(mUsbReceiver, filter);
}
public void unregisterMyReceivers(Context mContext) {
mContext.unregisterReceiver(mUsbReceiver);
}
private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
WkLogTool.showLog("action=" + action);
switch (action) {
case WK_ACTION_USB_PERMISSION:
synchronized (this) {
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
WkLogTool.showLog("授权成功 ,再次请求链接usb");
findEndPoints(mUsbDevice);
} else {
WkLogTool.showLog("用户拒绝授权");
}
}
break;
case UsbManager.ACTION_USB_DEVICE_DETACHED:
Observable.just(action)
.compose(RxjavaTool::toSimpleObservable)
.subscribe(value -> {
showMsg(mContext, "USB断开连接");
((Activity) mContext).finish();
});
break;
case UsbManager.ACTION_USB_DEVICE_ATTACHED:
Observable.just(action)
.compose(RxjavaTool::toSimpleObservable)
.subscribe(value -> {
showMsg(mContext, "USB已经连接");
});
break;
default:
break;
}
}
};
private void usbDevicesInit(Context mContext) {
WkLogTool.showLog("开始初始化 usb 设备----------------");
if (usbState == USB_CONNECTED) {
WkLogTool.showLog("usb 已经初始化了");
return;
}
HashMap<String, UsbDevice> deviceList = getUsbManager(mContext).getDeviceList();
Iterator<UsbDevice> deviceIterator = deviceList.values().iterator();
while (deviceIterator.hasNext()) {
UsbDevice device = deviceIterator.next();
int vid = device.getVendorId();
if (vid == 0x0000
|| vid == 0x04b4
|| vid == 0x0951
|| vid == 0x483
|| vid == 0xAAAA) {
mUsbDevice = device;
WkLogTool.showLog("发现usb设备, vid=" + vid);
}
}
boolean hasPermission = checkUsbPermission(mContext, mUsbDevice);
if (mUsbDevice == null) {
WkLogTool.showLog("mUsbDevice==null lllllllll");
return;
}
if (hasPermission) {
WkLogTool.showLog("有权限,无需授权 ,直接去打开usb 连接");
findEndPoints(mUsbDevice);
} else {
WkLogTool.showLog("usb没有权限,开始申请权限");
PendingIntent permissionIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(WK_ACTION_USB_PERMISSION), 0);
getUsbManager(mContext).requestPermission(mUsbDevice, permissionIntent);
}
}
private UsbManager getUsbManager(Context mContext) {
UsbManager manager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
if (manager == null) {
WkLogTool.showLogError("UsbManager 获取失败!!!");
}
return manager;
}
private boolean checkUsbPermission(Context mContext, UsbDevice mUsbDevice) {
if (mUsbDevice == null) {
WkLogTool.showLog("mUsbDevicew为null------------------------------");
return false;
}
return getUsbManager(mContext).hasPermission(mUsbDevice);
}
private void findEndPoints(UsbDevice mUsbDevice) {
WkLogTool.showLog("开始 寻找usb节点----------------");
int faceCount = mUsbDevice.getInterfaceCount();
interfaceCount = faceCount;
int pid = mUsbDevice.getProductId();
WkLogTool.showLog("pid=" + pid + " vid=" + mUsbDevice.getVendorId() + " 设备发现接口数量=" + faceCount);
if (faceCount < 1) {
WkLogTool.showLog("接口数量异常!!!!--------------------- " + faceCount);
return;
}
for (int i = 0; i < faceCount; i++) {
mInterfaceArray[i] = mUsbDevice.getInterface(i);
}
if (mInterfaceArray[0] == null) {
WkLogTool.showLogError("usb 设备接口获取失败");
} else {
UsbDeviceConnection connection = getUsbManager(mContext).openDevice(mUsbDevice);
mDeviceConnection = connection;
if (connection == null) {
WkLogTool.showLogError("usb connection 获取失败");
return;
}
for (int i = 0; i < faceCount; i++) {
boolean claimInterface = connection.claimInterface(mInterfaceArray[i], true);
if (claimInterface) {
int endpointCount = mInterfaceArray[i].getEndpointCount();
for (int index = 0; index < endpointCount; index++) {
UsbEndpoint usbEndpoint = mInterfaceArray[i].getEndpoint(index);
int direction = usbEndpoint.getDirection();
int endPointNumber = usbEndpoint.getEndpointNumber();
if (direction == UsbConstants.USB_DIR_IN) {
switch (endPointNumber) {
case 0x05:
break;
case 0x08:
case 0x04:
endPointForGetData = usbEndpoint;
break;
case 0x06:
endPointForUsb = usbEndpoint;
break;
case 0x03:
break;
default:
break;
}
} else if (direction == UsbConstants.USB_DIR_OUT) {
if (endPointNumber == 0x01) {
endPointForSendData = usbEndpoint;
}
}
}
} else {
WkLogTool.showLogError("usb 连接打开接口失败 。 节点=" + i);
}
}
onConnectUsbSuccess();
}
}
private void onConnectUsbSuccess() {
usbState = USB_CONNECTED;
fiFoUsbStream = new FiFoUsbStream();
new Thread(this::captureUsbStream).start();
mScheduledThreadPoolExecutor.scheduleWithFixedDelay(() -> {
if (isSerialPortDataTimeOut()) {
WkLogTool.showLog("灯变红 ,暂停主动接收 飞控信息 --------");
} else {
initCmdToGetFcsData();
}
}, 500, 300, TimeUnit.MILLISECONDS);
mScheduledThreadPoolExecutor.scheduleWithFixedDelay(() -> {
if (isSerialPortDataTimeOut()) {
WkLogTool.showLog("灯变红 ,暂停主动接收 图传质量等数据--------");
} else {
toGetDeviceInfo();
}
}, 500, 500, TimeUnit.MILLISECONDS);
mScheduledThreadPoolExecutor.scheduleWithFixedDelay(() -> {
if (isSerialPortDataTimeOut()) {
WkLogTool.showLog("灯变红 ,暂停主动接收 图传质量等数据--------");
} else {
toGetSignalQuality();
}
}, 0, 600, TimeUnit.MILLISECONDS);
mScheduledThreadPoolExecutor.scheduleWithFixedDelay(() -> {
if (isSerialPortDataTimeOut()) {
WkLogTool.showLog("灯变红 ,暂停主动接收 图传质量等数据--------");
} else {
queryAR8020Status();
}
}, 500, 200, TimeUnit.MILLISECONDS);
startRevDataThread();
executorService.execute(() -> {
try {
WkLogTool.showLog("开始接收 寻找 usb 视频帧 " + isRunning);
while (isRunning) {
if (fiFoUsbStream.buffer.length > 0) {
iUsbHostDataListner.toSearchUsbVideoFrame(fiFoUsbStream);
}
Thread.sleep(1);
}
} catch (Exception e) {
WkLogTool.showLog("主码流发生异常=" + e.getMessage());
e.printStackTrace();
}
});
}
private boolean isRunning = true;
public void releaseUsbDeviceConnections() {
isRunning = false;
usbState = USB_DISCONNECT;
WkLogTool.showLog("开始释放所有usb,status= " + usbState);
for (int i = 0; i < interfaceCount; i++) {
WkLogTool.showLog("释放usb接口" + i);
if (mDeviceConnection != null) {
mDeviceConnection.releaseInterface(mInterfaceArray[i]);
}
}
for (int i = 0; i < interfaceCount; i++) {
mInterfaceArray[i] = null;
}
if (mDeviceConnection != null) {
mDeviceConnection.close();
mDeviceConnection = null;
}
if (mScheduledThreadPoolExecutor != null) {
mScheduledThreadPoolExecutor.shutdown();
}
mUsbDevice = null;
executorService.shutdown();
stopStatusQuery();
WkLogTool.showLog("释放完成");
}
private void queryAR8020Status() {
int len = -1;
byte[] data = new byte[8];
Arrays.fill(data, (byte) 0x00);
data[0] = 0x01;
try {
if (USB_CONNECTED == usbState) {
byte[] protocolPacket = AirLinkProtocol.getUsbHostCmdPackToSend(0x11, (byte) 1, (byte) 0, data, 1);
len = mDeviceConnection.bulkTransfer(endPointForSendData, protocolPacket, protocolPacket.length, 1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
private int initCmdToGetFcsData() {
int len = -1;
try {
if (USB_CONNECTED == usbState) {
if (mDeviceConnection != null) {
byte[] protocolPacket = AirLinkProtocol.getUsbHostCmdPackToSend(UsbHostConfig.KEY_0X85, (byte) 1, (byte) 0, null, 0);
len = mDeviceConnection.bulkTransfer(endPointForSendData, protocolPacket, protocolPacket.length, 1000);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
public void switch24and58(boolean is24Way) {
try {
if (USB_CONNECTED == usbState) {
if (mDeviceConnection != null) {
int msgID = UsbHostConfig.KEY_0X22;
byte sumNum = 1;
byte curNum = 0;
byte[] payload = new byte[1];
payload[0] = is24Way ? (byte) 0x00 : (byte) 0x01;
int valueLen = payload.length;
byte[] protocolPacket = AirLinkProtocol.getUsbHostCmdPackToSend(msgID, sumNum, curNum, payload, valuelen);
WkLogTool.showLog("频道切换=" + MyByteBitTools.byte2HexStr(protocolPacket));
mDeviceConnection.bulkTransfer(endPointForSendData, protocolPacket, protocolPacket.length, 500);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void toGetDeviceInfo() {
int len = -1;
try {
if (USB_CONNECTED == usbState) {
if (mDeviceConnection != null) {
int msgID = UsbHostConfig.KEY_0X19;
byte sumNum = 1;
byte curNum = 0;
byte[] payload = null;
int valuelen = payload == null ? 0 : payload.length;
byte[] protocolPacket = AirLinkProtocol.getUsbHostCmdPackToSend(msgID, sumNum, curNum, payload, valuelen);
WkLogTool.showLog("为了读取0x19信息----------->" + MyByteBitTools.byte2HexStr(protocolPacket));
len = mDeviceConnection.bulkTransfer(endPointForSendData, protocolPacket, protocolPacket.length, 1000);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void toGetSignalQuality() {
int len = -1;
try {
if (USB_CONNECTED == usbState) {
if (mDeviceConnection != null) {
int msgID = UsbHostConfig.KEY_0X82;
byte sumNum = 1;
byte curNum = 0;
byte[] payload = null;
int valuelen = payload == null ? 0 : payload.length;
byte[] protocolPacket = AirLinkProtocol.getUsbHostCmdPackToSend(msgID, sumNum, curNum, payload, valuelen);
len = mDeviceConnection.bulkTransfer(endPointForSendData, protocolPacket, protocolPacket.length, 1000);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private boolean isSerialPortDataTimeOut() {
return false;
}
private void captureUsbStream() {
WkLogTool.showLog("开始接收 usb 数据--------" + usbState);
int dataLen = 0;
while (USB_CONNECTED == usbState) {
try {
if (isSerialPortDataTimeOut()) {
Thread.sleep(100);
WkLogTool.showLog("灯变红 ,暂停接收 usb 数据--------");
} else {
dataLen = mDeviceConnection.bulkTransfer(endPointForUsb, revUsbData, revUsbData.length, TIMEOUT);
boolean isError = dataLen < 200;
if (isError) {
byte[] testData = dataLen == -1 ? null : WkByteBitTools.subBytes(revUsbData, 0, 30);
WkLogTool.showLog("testData= " + MyByteBitTools.byte2HexStr(testData));
}
if (dataLen > 0) {
WkLogTool.showLog("usb数据接收 " + " " + revUsbData.length);
if (fiFoUsbStream != null) {
fiFoUsbStream.FiFoWrite(revUsbData, dataLen);
}
} else {
WkLogTool.showLog("usb数据接收为(异常) " + dataLen + " " + revUsbData.length);
}
}
} catch (Exception e) {
WkLogTool.showLogError("接收USB数据异常," + e.getMessage());
}
}
}
private byte[] mReceiveBytes = new byte[512];
public void startRevDataThread() {
executorService.execute(() -> {
try {
int len = -1;
WkLogTool.showLog("开始接收 飞控数据 " + isRunning);
while (isRunning) {
if (USB_CONNECTED == usbState && endPointForGetData != null) {
if (isSerialPortDataTimeOut()) {
WkLogTool.showLog("灯变红 ,暂停被动接收 飞控 数据--------");
Thread.sleep(100);
} else {
len = mDeviceConnection.bulkTransfer(endPointForGetData, mReceiveBytes, mReceiveBytes.length, TIMEOUT);
if (len >= 10) {
int msgId = ((mReceiveBytes[3] & 0x0FF) << 8) + (mReceiveBytes[2] & 0x0FF);
switch (msgId) {
case UsbHostConfig.KEY_0X85:
if (len > 10) {
if (mReceiveBytes[0] == (byte) 0xFF && mReceiveBytes[1] == (byte) 0x5A) {
int userDataLen = ((mReceiveBytes[7] & 0x0FF) << 8) + (mReceiveBytes[6] & 0x0FF);
if (userDataLen > 0 && userDataLen < len) {
byte[] userDataArray = new byte[userDataLen];
System.arraycopy(mReceiveBytes, 10, userDataArray, 0, userDataLen);
if (isRunning) {
iUsbHostDataListner.onDataRev(userDataArray);
}
}
}
}
break;
case UsbHostConfig.KEY_0X19:
if (isRunning) {
processOx19(mReceiveBytes, len);
}
break;
case UsbHostConfig.KEY_0X82:
if (isRunning) {
process0x82(mReceiveBytes, len);
}
break;
default:
break;
}
} else {
WkLogTool.showLog("飞控 数据接收为(异常) len= " + len);
}
}
} else {
WkLogTool.showLog("飞控 节点异常 ,或者数据通道还未未连接 ");
Thread.sleep(100);
}
}
} catch (Exception e) {
WkLogTool.showLogError("接收 飞控 数据异常," + e.getMessage());
e.printStackTrace();
}
});
}
private void processOx19(byte[] mReceiveBytes, int len) {
if (mReceiveBytes[0] == (byte) 0xFF && mReceiveBytes[1] == (byte) 0x5A) {
int userDataLen = ((mReceiveBytes[7] & 0x0FF) << 8) + (mReceiveBytes[6] & 0x0FF);
if (userDataLen > 0 && userDataLen < len) {
byte[] userDataArray = new byte[userDataLen];
System.arraycopy(mReceiveBytes, 10, userDataArray, 0, userDataLen);
int band = userDataArray[1];
if (iUsbHostDataListner != null) {
if (band == 1) {
iUsbHostDataListner.onReceiveSignType("2.4G");
} else if (band == 2) {
iUsbHostDataListner.onReceiveSignType("5.8G");
}
}
}
}
}
private void process0x82(byte[] mReceiveBytes, int len) {
if (mReceiveBytes[0] == (byte) 0xFF && mReceiveBytes[1] == (byte) 0x5A) {
int userDataLen = ((mReceiveBytes[7] & 0x0FF) << 8) + (mReceiveBytes[6] & 0x0FF);
if (userDataLen > 0 && userDataLen < len) {
byte[] userDataArray = new byte[userDataLen];
System.arraycopy(mReceiveBytes, 10, userDataArray, 0, userDataLen);
int groundSign= userDataArray[3];
int airSign = userDataArray[4];
if (iUsbHostDataListner != null) {
iUsbHostDataListner.onReceiveHdSingQuality(airSign);
iUsbHostDataListner.onReceiveRcSignQuality(groundSign);
}
}
}
}
private void showMsg(Context context, String str) {
Toast.makeText(context, str, Toast.LENGTH_SHORT).show();
}
public void setVisible(boolean isVisible) {
if (fiFoUsbStream != null) {
fiFoUsbStream.setVisible(isVisible);
}
}
private void stopStatusQuery() {
int len = -1;
byte[] data = new byte[8];
Arrays.fill(data, (byte) 0x00);
data[0] = 0x11;
data[1] = 0x01;
data[2] = 0x00;
try {
if (USB_CONNECTED == usbState) {
len = mDeviceConnection.bulkTransfer(endPointForSendData, data, 3, 1000);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
|