Android 接收 usb 批量传输
Android receive usb bulk transfer
我创建了一个 java 应用程序,它将自己设置为 USB 配件并通过批量传输发送一些数据。
Android 设备应该看到这个并将数据打印到 TextView。目前我拥有它,以便 Android 设备可以看到附件的连接并启动应用程序批量传输但是失败并显示以下内容。
org.usb4java.LibUsbException: USB error 5: Bulk write error!: Entity not found
at com.xxx.xxx.AccessoryTest.writeAndRead(AccessoryTest.java:60)
at com.xxx.xxx.AccessoryTest.main(AccessoryTest.java:37)
欢迎提出任何建议!
我有另一个实用程序,它将 Android 设备的端点信息报告为
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 512
bInterval 0
extralen 0
extra:
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 512
bInterval 0
extralen 0
extra:
我正在尝试写入 0x02 端点
Android代码
public class MainActivity extends AppCompatActivity implements Runnable {
private UsbManager manager;
private UsbAccessory accessory;
private ParcelFileDescriptor accessoryFileDescriptor;
private FileInputStream accessoryInput;
private FileOutputStream accessoryOutput;
private TextView questionTV;
private final BroadcastReceiver usbBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(action)) {
synchronized (this) {
accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
}
} else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
UsbAccessory accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (accessory != null) {
// call your method that cleans up and closes communication with the accessory
try {
accessoryFileDescriptor.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
manager = (UsbManager) getSystemService(Context.USB_SERVICE);
IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
registerReceiver(usbBroadcastReceiver, filter);
if (getLastNonConfigurationInstance() != null)
{
accessory = (UsbAccessory) getLastNonConfigurationInstance();
openAccessory();
}
setContentView(R.layout.activity_main);
}
private void openAccessory() {
accessoryFileDescriptor = manager.openAccessory(accessory);
if(accessoryFileDescriptor != null) {
FileDescriptor fd = accessoryFileDescriptor.getFileDescriptor();
accessoryInput = new FileInputStream(fd);
accessoryOutput = new FileOutputStream(fd);
Thread thread = new Thread(null, this, "AccessoryThread");
thread.start();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void run() {
int ret = 0;
byte[] buffer = new byte[51];
int bufferUsed = 0;
while(ret >= 0) {
try {
ret = accessoryInput.read(buffer);
} catch (IOException e) {
Log.e("MainActivity", "Exception in USB accessory input reading", e);
break;
}
}
String question = new String(buffer);
LinearLayout layout= (LinearLayout) findViewById(R.id.layout);
questionTV = new TextView(this);
questionTV.setText(question);
layout.addView(questionTV);
}
}
Java 应用代码
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import org.usb4java.BufferUtils;
import org.usb4java.Context;
import org.usb4java.Device;
import org.usb4java.DeviceDescriptor;
import org.usb4java.DeviceHandle;
import org.usb4java.DeviceList;
import org.usb4java.LibUsb;
import org.usb4java.LibUsbException;
public class AccessoryTest {
private final static byte REQUEST_TYPE_READ = (byte) 0xC0;
private final static byte END_POINT_IN = (byte) 0x81;
private final static byte END_POINT_OUT = (byte) 0x02;
private final static short NEXUS7_VENDORID = (short) 0x18D1;
private final static short NEXUS7_PRODUCTID = (short) 0x4EE1;
private static Context context;
private static Device device;
private static DeviceHandle handle;
public static void main(String[] args) {
try {
init();
int result = setupAccessory("PCHost", "PCHost1", "Description", "1.0", "http://www.mycompany.com",
"SerialNumber");
if (result != LibUsb.SUCCESS)
throw new LibUsbException("Unable to setup Accessory", result);
writeAndRead();
} catch (Exception e) {
e.printStackTrace();
} finally {
LibUsb.releaseInterface(handle, 0);
LibUsb.resetDevice(handle);
LibUsb.close(handle);
LibUsb.exit(context);
}
}
private static void writeAndRead() {
String question = "Hello Android I'll be your host today, how are you?";
byte[] questionBuffer = question.getBytes();
ByteBuffer questionData = BufferUtils.allocateByteBuffer(questionBuffer.length);
IntBuffer transferred = IntBuffer.allocate(1);
int result = 0;
//THIS IS THE PART WHERE IT FAILS!
result = LibUsb.bulkTransfer(handle, END_POINT_OUT, questionData, transferred, 5000);
if(result < 0) {
throw new LibUsbException("Bulk write error!", result);
}
}
private static void init() {
context = new Context();
int result = LibUsb.init(context);
if (result != LibUsb.SUCCESS)
throw new LibUsbException("Unable to initialize libusb.", result);
device = findDevice(NEXUS7_VENDORID);
handle = new DeviceHandle();
result = LibUsb.open(device, handle);
if (result < 0)
throw new LibUsbException("Unable to open USB device", result);
result = LibUsb.claimInterface(handle, 0);
if (result != LibUsb.SUCCESS)
throw new LibUsbException("Unable to claim interface", result);
}
private static Device findDevice(short vendorId) {
// Read the USB device list
DeviceList list = new DeviceList();
int result = LibUsb.getDeviceList(null, list);
if (result < 0)
throw new LibUsbException("Unable to get device list", result);
try {
// Iterate over all devices and scan for the right one
for (Device device : list) {
DeviceDescriptor descriptor = new DeviceDescriptor();
result = LibUsb.getDeviceDescriptor(device, descriptor);
if (result != LibUsb.SUCCESS)
throw new LibUsbException("Unable to read device descriptor", result);
if (descriptor.idVendor() == vendorId)
return device;
}
} finally {
// Ensure the allocated device list is freed
LibUsb.freeDeviceList(list, true);
}
// Device not found
return null;
}
private static int setupAccessory(String vendor, String model, String description, String version, String url,
String serial) throws LibUsbException {
int response = 0;
// Setup setup token
response = transferSetupPacket((short) 2, REQUEST_TYPE_READ, (byte) 51);
// Setup data packet
response = transferAccessoryDataPacket(vendor, (short) 0);
response = transferAccessoryDataPacket(model, (short) 1);
response = transferAccessoryDataPacket(description, (short) 2);
response = transferAccessoryDataPacket(version, (short) 3);
response = transferAccessoryDataPacket(url, (short) 4);
response = transferAccessoryDataPacket(serial, (short) 5);
// Setup handshake packet
response = transferSetupPacket((short) 0, (byte) (LibUsb.REQUEST_TYPE_VENDOR | LibUsb.ENDPOINT_OUT), (byte) 53);
LibUsb.releaseInterface(handle, 0);
return response;
}
private static int transferSetupPacket(short bufferLength, byte requestType, byte request) throws LibUsbException {
int response = 0;
byte[] bytebuff = new byte[bufferLength];
ByteBuffer data = BufferUtils.allocateByteBuffer(bytebuff.length);
data.put(bytebuff);
final short wValue = 0;
final short wIndex = 0;
final long timeout = 1000;
data.rewind();
response = LibUsb.controlTransfer(handle, requestType, request, wValue, wIndex,
data, timeout);
if(response < 0)
throw new LibUsbException("Unable to transfer setup packet ", response);
return response;
}
private static int transferAccessoryDataPacket(String param, short index) {
int response;
byte[] byteArray = param.getBytes();
ByteBuffer data = BufferUtils.allocateByteBuffer(byteArray.length);
data.put(byteArray);
final byte bRequest = (byte) 52;
final short wValue = 0;
final long timeout = 0;
response = LibUsb.controlTransfer(handle, LibUsb.REQUEST_TYPE_VENDOR, bRequest, wValue, index,
data, timeout);
if(response < 0)
throw new LibUsbException("Unable to control transfer.", response);
return response;
}
}
AndroidManifest.xml
<uses-feature android:name="android.hardware.usb.accessory" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<uses-library android:name="com.android.future.usb.accessory" />
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<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>
</application>
根据 AOP 标准,一旦 Android 设备进入附件模式,它的 VID 和 PID 就会发生变化
因此在设置附件后需要回收设备句柄和接口。
我创建了一个 java 应用程序,它将自己设置为 USB 配件并通过批量传输发送一些数据。
Android 设备应该看到这个并将数据打印到 TextView。目前我拥有它,以便 Android 设备可以看到附件的连接并启动应用程序批量传输但是失败并显示以下内容。
org.usb4java.LibUsbException: USB error 5: Bulk write error!: Entity not found
at com.xxx.xxx.AccessoryTest.writeAndRead(AccessoryTest.java:60)
at com.xxx.xxx.AccessoryTest.main(AccessoryTest.java:37)
欢迎提出任何建议!
我有另一个实用程序,它将 Android 设备的端点信息报告为
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x81 EP 1 IN
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 512
bInterval 0
extralen 0
extra:
Endpoint Descriptor:
bLength 7
bDescriptorType 5
bEndpointAddress 0x02 EP 2 OUT
bmAttributes 2
Transfer Type Bulk
Synch Type None
Usage Type Data
wMaxPacketSize 512
bInterval 0
extralen 0
extra:
我正在尝试写入 0x02 端点
Android代码
public class MainActivity extends AppCompatActivity implements Runnable {
private UsbManager manager;
private UsbAccessory accessory;
private ParcelFileDescriptor accessoryFileDescriptor;
private FileInputStream accessoryInput;
private FileOutputStream accessoryOutput;
private TextView questionTV;
private final BroadcastReceiver usbBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(action)) {
synchronized (this) {
accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
}
} else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
UsbAccessory accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (accessory != null) {
// call your method that cleans up and closes communication with the accessory
try {
accessoryFileDescriptor.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
manager = (UsbManager) getSystemService(Context.USB_SERVICE);
IntentFilter filter = new IntentFilter(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
registerReceiver(usbBroadcastReceiver, filter);
if (getLastNonConfigurationInstance() != null)
{
accessory = (UsbAccessory) getLastNonConfigurationInstance();
openAccessory();
}
setContentView(R.layout.activity_main);
}
private void openAccessory() {
accessoryFileDescriptor = manager.openAccessory(accessory);
if(accessoryFileDescriptor != null) {
FileDescriptor fd = accessoryFileDescriptor.getFileDescriptor();
accessoryInput = new FileInputStream(fd);
accessoryOutput = new FileOutputStream(fd);
Thread thread = new Thread(null, this, "AccessoryThread");
thread.start();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void run() {
int ret = 0;
byte[] buffer = new byte[51];
int bufferUsed = 0;
while(ret >= 0) {
try {
ret = accessoryInput.read(buffer);
} catch (IOException e) {
Log.e("MainActivity", "Exception in USB accessory input reading", e);
break;
}
}
String question = new String(buffer);
LinearLayout layout= (LinearLayout) findViewById(R.id.layout);
questionTV = new TextView(this);
questionTV.setText(question);
layout.addView(questionTV);
}
}
Java 应用代码
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import org.usb4java.BufferUtils;
import org.usb4java.Context;
import org.usb4java.Device;
import org.usb4java.DeviceDescriptor;
import org.usb4java.DeviceHandle;
import org.usb4java.DeviceList;
import org.usb4java.LibUsb;
import org.usb4java.LibUsbException;
public class AccessoryTest {
private final static byte REQUEST_TYPE_READ = (byte) 0xC0;
private final static byte END_POINT_IN = (byte) 0x81;
private final static byte END_POINT_OUT = (byte) 0x02;
private final static short NEXUS7_VENDORID = (short) 0x18D1;
private final static short NEXUS7_PRODUCTID = (short) 0x4EE1;
private static Context context;
private static Device device;
private static DeviceHandle handle;
public static void main(String[] args) {
try {
init();
int result = setupAccessory("PCHost", "PCHost1", "Description", "1.0", "http://www.mycompany.com",
"SerialNumber");
if (result != LibUsb.SUCCESS)
throw new LibUsbException("Unable to setup Accessory", result);
writeAndRead();
} catch (Exception e) {
e.printStackTrace();
} finally {
LibUsb.releaseInterface(handle, 0);
LibUsb.resetDevice(handle);
LibUsb.close(handle);
LibUsb.exit(context);
}
}
private static void writeAndRead() {
String question = "Hello Android I'll be your host today, how are you?";
byte[] questionBuffer = question.getBytes();
ByteBuffer questionData = BufferUtils.allocateByteBuffer(questionBuffer.length);
IntBuffer transferred = IntBuffer.allocate(1);
int result = 0;
//THIS IS THE PART WHERE IT FAILS!
result = LibUsb.bulkTransfer(handle, END_POINT_OUT, questionData, transferred, 5000);
if(result < 0) {
throw new LibUsbException("Bulk write error!", result);
}
}
private static void init() {
context = new Context();
int result = LibUsb.init(context);
if (result != LibUsb.SUCCESS)
throw new LibUsbException("Unable to initialize libusb.", result);
device = findDevice(NEXUS7_VENDORID);
handle = new DeviceHandle();
result = LibUsb.open(device, handle);
if (result < 0)
throw new LibUsbException("Unable to open USB device", result);
result = LibUsb.claimInterface(handle, 0);
if (result != LibUsb.SUCCESS)
throw new LibUsbException("Unable to claim interface", result);
}
private static Device findDevice(short vendorId) {
// Read the USB device list
DeviceList list = new DeviceList();
int result = LibUsb.getDeviceList(null, list);
if (result < 0)
throw new LibUsbException("Unable to get device list", result);
try {
// Iterate over all devices and scan for the right one
for (Device device : list) {
DeviceDescriptor descriptor = new DeviceDescriptor();
result = LibUsb.getDeviceDescriptor(device, descriptor);
if (result != LibUsb.SUCCESS)
throw new LibUsbException("Unable to read device descriptor", result);
if (descriptor.idVendor() == vendorId)
return device;
}
} finally {
// Ensure the allocated device list is freed
LibUsb.freeDeviceList(list, true);
}
// Device not found
return null;
}
private static int setupAccessory(String vendor, String model, String description, String version, String url,
String serial) throws LibUsbException {
int response = 0;
// Setup setup token
response = transferSetupPacket((short) 2, REQUEST_TYPE_READ, (byte) 51);
// Setup data packet
response = transferAccessoryDataPacket(vendor, (short) 0);
response = transferAccessoryDataPacket(model, (short) 1);
response = transferAccessoryDataPacket(description, (short) 2);
response = transferAccessoryDataPacket(version, (short) 3);
response = transferAccessoryDataPacket(url, (short) 4);
response = transferAccessoryDataPacket(serial, (short) 5);
// Setup handshake packet
response = transferSetupPacket((short) 0, (byte) (LibUsb.REQUEST_TYPE_VENDOR | LibUsb.ENDPOINT_OUT), (byte) 53);
LibUsb.releaseInterface(handle, 0);
return response;
}
private static int transferSetupPacket(short bufferLength, byte requestType, byte request) throws LibUsbException {
int response = 0;
byte[] bytebuff = new byte[bufferLength];
ByteBuffer data = BufferUtils.allocateByteBuffer(bytebuff.length);
data.put(bytebuff);
final short wValue = 0;
final short wIndex = 0;
final long timeout = 1000;
data.rewind();
response = LibUsb.controlTransfer(handle, requestType, request, wValue, wIndex,
data, timeout);
if(response < 0)
throw new LibUsbException("Unable to transfer setup packet ", response);
return response;
}
private static int transferAccessoryDataPacket(String param, short index) {
int response;
byte[] byteArray = param.getBytes();
ByteBuffer data = BufferUtils.allocateByteBuffer(byteArray.length);
data.put(byteArray);
final byte bRequest = (byte) 52;
final short wValue = 0;
final long timeout = 0;
response = LibUsb.controlTransfer(handle, LibUsb.REQUEST_TYPE_VENDOR, bRequest, wValue, index,
data, timeout);
if(response < 0)
throw new LibUsbException("Unable to control transfer.", response);
return response;
}
}
AndroidManifest.xml
<uses-feature android:name="android.hardware.usb.accessory" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<uses-library android:name="com.android.future.usb.accessory" />
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<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>
</application>
根据 AOP 标准,一旦 Android 设备进入附件模式,它的 VID 和 PID 就会发生变化
因此在设置附件后需要回收设备句柄和接口。