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 就会发生变化

因此在设置附件后需要回收设备句柄和接口。