📜 ⬆️ ⬇️

Working with USB devices in Android

In a recent article on Geektimes in the comments there was a question about support in the Android OS of peripherals connected to the USB bus. Indeed, most vendor software, for example, to work with printers and multifunction devices, only supports network connectivity. However, this does not mean that in the Android OS itself there is no such possibility - it only means that most devices do not have a full-fledged USB host, and not all have OTG support. On the same network can work absolutely all without exception.

Most Android devices, with an OTG port, support the following device classes at the system level (Linux kernels or standard Android components):


Somewhat less:


Hubs are supported with full host ports, but not supported on OTG ports.
')
A more detailed list of devices supported at the Linux kernel level can be obtained from sysfs:

$ ls /sys/bus/usb/drivers

If the module is in principle available in the source code of the Linux kernel, but is not included in Android - you should not expect that it will be possible to assemble and place it on all target systems.

However, starting with Android 3.1 (API 12), the system contains enough tools to support any USB peripherals at the application level. These tools are described in the USB Host section of the Android API manual. Here I want to give examples of real work with some types of devices.

Access rights


As with other actions, Android requires the application to receive permission to access USB peripherals. There are 2 ways to get this permission:


Since for my tasks extra questions to the user were undesirable, I used the first method.

So, we need to add the following to the manifest:

 <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> <uses-feature android:name="android.hardware.usb.host" /> <uses-sdk android:minSdkVersion="12" /> 

And in res / xml / device_filter.xml enter the following:

 <?xml version="1.0" encoding="utf-8"?> <resources> <!-- Serial converters --> <!-- 0x0403 / 0x6001: FTDI FT232R UART --> <usb-device vendor-id="1027" product-id="24577" /> <!-- … more devices … --> </resources> 

I note that although it is generally accepted to specify the VID: PID in hexadecimal notation, here they must be specified in decimal. The documentation states that it is possible to specify only a class, without a VID and PID, but this did not work for me.

Printers


Using the printer as an example, I will show how to directly use the android.hardware.usb API. At the data transfer level, all printers support the standard class of USB devices:

 int UsbConstants.USB_CLASS_PRINTER = 7; 

The class is extremely simple. Within this class, the device must support:


 int GET_DEVICE_ID = 0; int GET_PORT_STATUS = 1; int SOFT_RESET = 2; 

The code below provides functionality similar to / dev / usb / lp on Linux. Next, we need a filter that converts the original document into a data packet, understandable to a specific printer model. But this is the topic of another article. As one of the options, you can build ghostscript using the NDK.

To work with the device, we first need:

1. Find a device. In the example for the sake of simplicity, I am looking for the first available

 UsbDevice findDevice() { for (UsbDevice usbDevice: mUsbManager.getDeviceList().values()) { if (usbDevice.getDeviceClass() == UsbConstants.USB_CLASS_PRINTER) { return usbDevice; } else { UsbInterface usbInterface = findInterface(usbDevice); if (usbInterface != null) return usbDevice; } } return null; } UsbInterface findInterface(UsbDevice usbDevice) { for (int nIf = 0; nIf < usbDevice.getInterfaceCount(); nIf++) { UsbInterface usbInterface = usbDevice.getInterface(nIf); if (usbInterface.getInterfaceClass() == UsbConstants.USB_CLASS_PRINTER) { return usbInterface; } } return null; } UsbDevice mUsbDevice = findDevice(); UsbInterface mUsbInterface = findInterface(mUsbDevice); 

2. Get endpoints:

 for (int nEp = 0; nEp < mUsbInterface.getEndpointCount(); nEp++) { UsbEndpoint tmpEndpoint = mUsbInterface.getEndpoint(nEp); if (tmpEndpoint.getType() != UsbConstants.USB_ENDPOINT_XFER_BULK) continue; if ((mOutEndpoint == null) && (tmpEndpoint.getDirection() == UsbConstants.USB_DIR_OUT)) { mOutEndpoint = tmpEndpoint; } else if ((mInEndpoint == null) && (tmpEndpoint.getDirection() == UsbConstants.USB_DIR_IN)) { mInEndpoint = tmpEndpoint; } } if (mOutEndpoint == null) throw new IOException("No write endpoint: " + deviceName); 

3. Directly open the device:

 mConnection = mUsbManager.openDevice(mUsbDevice); if (mConnection == null) throw new IOException("Can't open USB connection:" + deviceName); mConnection.claimInterface (mUsbInterface, true); 

4. After this we can read and write to the device:

 public int read(final byte[] data) throws IOException { int size = Math.min(data.length, mInEndpoint.getMaxPacketSize()); return mConnection.bulkTransfer(mInEndpoint, data, size, getReadTimeout()); } public int write(final byte[] data, final int length) throws IOException { int offset = 0; while (offset < length) { int size = Math.min(length - offset, mInEndpoint.getMaxPacketSize()); int bytesWritten = mConnection.bulkTransfer(mOutEndpoint, Arrays.copyOfRange(data, offset, offset + size), size, getWriteTimeout()); if (bytesWritten <= 0) throw new IOException("None written"); offset += bytesWritten; } return offset; } 

5. Upon completion of the work - close the device:

 mConnection.close(); 

USB Serial Converters


Unlike printers, USB-Serial converters are much less standardized. There are several common chips for which the setting of the parameters of the serial port — bit rate, parity, and so on — differs significantly. Fortunately, there is a github.com/mik3y/usb-serial-for-android library that supports almost all existing chips. The library completely hides the USB API, minimizing all the necessary actions to a minimum of calls with a minimum of parameters.

1. Find and open the device:

 UsbSerialPort mUsbSerialPort; UsbManager mUsbManager = (UsbManager) DEVICE.getSystemService(Context.USB_SERVICE); String type = “FTDI”; for (UsbDevice usbDevice: mUsbManager.getDeviceList().values()) { UsbSerialDriver usbSerialDriver = UsbSerialProber.probeSingleDevice(usbDevice); if (usbSerialDriver == null) continue; if (!type.equals(usbSerialDriver.getShortDeviceName())) continue; mUsbSerialPort = usbSerialDriver.getPort(0); mUsbSerialPort.open(mUsbManager); break; } 

2. Set the parameters of the serial port:

 mUsbSerialPort.setParameters(baudRate, dataBits, stopBits, parity); 

3. Read and write to port:

 public int read(final byte[] data) throws IOException { return mUsbSerialPort.read(data, getReadTimeout()); } public int write(final byte[] data, final int length) throws IOException { return mUsbSerialPort.write(data, length, getWriteTimeout()); } 

4. Upon completion of work - close the port:

 mUsbSerialPort.close(); 

Summary


I hope that I managed to show that working with USB peripherals is quite simple and logical. Of course, the implementation of the protocols of some specific devices does not shine with simplicity - but it will manifest itself in any system to the same extent.

I took all the above examples from the real project, only eliminated the obvious checks, leaving only the key lines.

Source: https://habr.com/ru/post/277093/


All Articles