android framework之旅(六)Usb多串口同时打开

这篇文章起源于之前遗留的一个问题(https://www.jianshu.com/p/189816294b37),公司的产品需要使用开发板做host与多个串口通信,多个串口同时打开会发生crash,必须先连接成功一个,再插另一根线连接另一个,很不方便,当时想尽办法并没有解决,最近在研究framework的时候有了新的发现。

环境

操作系统:ubuntu 16.04
源码版本:5.1.1

目标

android系统做host与多个串口同时通信。

发现过程

当时在使用pl2303出现这个问题时,我曾一度怀疑是芯片厂商提供的jar包代码有bug,因为我使用官方的demo打开多个串口时也有相同的问题,但是没有源码,向台湾厂家反应也毫无回应,最后不了了之。
最近这个问题又被提起,心想干脆不使用厂家封装好的jar包,自己用系统api来试试能不能通信,没想到刚写了几行代码就发现了问题:

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val usbManager:UsbManager = getSystemService(Context.USB_SERVICE) as UsbManager
        btnDeviceList.setOnClickListener {
            val deviceList = usbManager.deviceList
            println("设备数目:${deviceList.size}")
            deviceList.forEach { println(it) }
        }
    }
}

非常简单的一段代码,只是检测一下当前连接多少设备,并将设备信息依次打印出来,我分别试了连接一个设备和连接两个设备,下面看下日志:

05-08 06:32:19.059 2101-2101/com.lxf.usbdemo I/System.out: 设备数目:1
05-08 06:32:19.060 2101-2101/com.lxf.usbdemo I/System.out: /dev/bus/usb/001/003=UsbDevice[mName=/dev/bus/usb/001/003,mVendorId=1659,mProductId=8963,mClass=0,mSubclass=0,mProtocol=0,mManufacturerName=Prolific Technology Inc. ,mProductName=USB-Serial Controller D,mSerialNumber=null,mConfigurations=[
05-08 06:32:19.060 2101-2101/com.lxf.usbdemo I/System.out: UsbConfiguration[mId=1,mName=null,mAttributes=160,mMaxPower=50,mInterfaces=[
05-08 06:32:19.060 2101-2101/com.lxf.usbdemo I/System.out: UsbInterface[mId=0,mAlternateSetting=0,mName=null,mClass=255,mSubclass=0,mProtocol=0,mEndpoints=[
05-08 06:32:19.060 2101-2101/com.lxf.usbdemo I/System.out: UsbEndpoint[mAddress=129,mAttributes=3,mMaxPacketSize=10,mInterval=1]
05-08 06:32:19.060 2101-2101/com.lxf.usbdemo I/System.out: UsbEndpoint[mAddress=2,mAttributes=2,mMaxPacketSize=64,mInterval=0]
05-08 06:32:19.060 2101-2101/com.lxf.usbdemo I/System.out: UsbEndpoint[mAddress=131,mAttributes=2,mMaxPacketSize=64,mInterval=0]]]]

这是一台设备的信息,当时我看到时心里是毫无波动的,因为除了看出来连接成功了其他啥也看不懂/(ㄒoㄒ)/~~,然后看两台设备的日志:

05-08 06:34:47.284 2101-2101/com.lxf.usbdemo I/System.out: 设备数目:2
05-08 06:34:47.284 2101-2101/com.lxf.usbdemo I/System.out: /dev/bus/usb/001/004=UsbDevice[mName=/dev/bus/usb/001/004,mVendorId=1659,mProductId=8963,mClass=0,mSubclass=0,mProtocol=0,mManufacturerName=Prolific Technology Inc. ,mProductName=USB-Serial Controller D,mSerialNumber=null,mConfigurations=[
05-08 06:34:47.285 2101-2101/com.lxf.usbdemo I/System.out: UsbConfiguration[mId=1,mName=null,mAttributes=128,mMaxPower=50,mInterfaces=[]]
05-08 06:34:47.285 2101-2101/com.lxf.usbdemo I/System.out: /dev/bus/usb/001/003=UsbDevice[mName=/dev/bus/usb/001/003,mVendorId=1659,mProductId=8963,mClass=0,mSubclass=0,mProtocol=0,mManufacturerName=Prolific Technology Inc. ,mProductName=USB-Serial Controller D,mSerialNumber=null,mConfigurations=[
05-08 06:34:47.285 2101-2101/com.lxf.usbdemo I/System.out: UsbConfiguration[mId=1,mName=null,mAttributes=160,mMaxPower=50,mInterfaces=[
05-08 06:34:47.285 2101-2101/com.lxf.usbdemo I/System.out: UsbInterface[mId=0,mAlternateSetting=0,mName=null,mClass=255,mSubclass=0,mProtocol=0,mEndpoints=[
05-08 06:34:47.285 2101-2101/com.lxf.usbdemo I/System.out: UsbEndpoint[mAddress=129,mAttributes=3,mMaxPacketSize=10,mInterval=1]
05-08 06:34:47.285 2101-2101/com.lxf.usbdemo I/System.out: UsbEndpoint[mAddress=2,mAttributes=2,mMaxPacketSize=64,mInterval=0]
05-08 06:34:47.285 2101-2101/com.lxf.usbdemo I/System.out: UsbEndpoint[mAddress=131,mAttributes=2,mMaxPacketSize=64,mInterval=0]]]]

2台设备的日志信息居然和1台设备的信息差不多,仔细看发现第一台设备的mInterfaces属性为空,回想之前发生crash的错误信息,于是大胆怀疑是系统源码的问题,毕竟这次是完全使用系统api的。

解决过程

android启动后会加载SystemServer来启动很多系统服务,我们看下SystemServer的结构:


SystemServer.png

方法并不多,一看就懂,在启动服务的方法中搜索一下我们关心的usb服务:

if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_USB_HOST)
                        || mPackageManager.hasSystemFeature(
                                PackageManager.FEATURE_USB_ACCESSORY)) {
                    // Manage USB host and device support
                    mSystemServiceManager.startService(USB_SERVICE_CLASS);
                }

...
private static final String USB_SERVICE_CLASS =
            "com.android.server.usb.UsbService$Lifecycle";

然后我们打开UsbService:

/**
 * UsbService manages all USB related state, including both host and device support.
 * Host related events and calls are delegated to UsbHostManager, and device related
 * support is delegated to UsbDeviceManager.
 */
public class UsbService extends IUsbManager.Stub {

翻译一下,UsbService管理所有的USB相关状态,支持做主机(host)和设备(device)。做主机时相关事件和回调由UsbHostManager管理,做设备时相关事件和回调由UsbDeviceManager管理。
于是打开USBHostManager,发现了一些我们熟悉的东西:

    private UsbDevice mNewDevice;
    private UsbConfiguration mNewConfiguration;
    private UsbInterface mNewInterface;
    private ArrayList mNewConfigurations;
    private ArrayList mNewInterfaces;
    private ArrayList mNewEndpoints;

很明显,这就是我们开始在日志中看到的相关信息。再看下结构图:


UsbHostManager.png

说下比较重要的几个方法:

  • systemReady:该类的起点,运行monitorUsbHostBus,一个native方法。
  • beginUsbDeviceAdded:有新的设备时由monitorUsbHostBus()调用,接下来会继续由jni调用addUsbConfiguration、addUsbInterface、addUsbEndpoint方法添加相应参数信息,并最后调用endUsbDeviceAdded方法。
  • usbDeviceRemoved:移除设备时由monitorUsbHostBus()调用。

联想我们开始发现的mInterfaces为空问题,看下addUsbConfiguration、addUsbInterface、addUsbEndpoint三个方法:

/* Called from JNI in monitorUsbHostBus() to report new USB configuration for the device
       currently being added.  Returns true if successful, false in case of error.
     */
    private void addUsbConfiguration(int id, String name, int attributes, int maxPower) {
        if (mNewConfiguration != null) {
            mNewConfiguration.setInterfaces(
                    mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
            mNewInterfaces.clear();
        }

        mNewConfiguration = new UsbConfiguration(id, name, attributes, maxPower);
        mNewConfigurations.add(mNewConfiguration);
    }

    /* Called from JNI in monitorUsbHostBus() to report new USB interface for the device
       currently being added.  Returns true if successful, false in case of error.
     */
    private void addUsbInterface(int id, String name, int altSetting,
            int Class, int subClass, int protocol) {
        if (mNewInterface != null) {
            mNewInterface.setEndpoints(
                    mNewEndpoints.toArray(new UsbEndpoint[mNewEndpoints.size()]));
            mNewEndpoints.clear();
        }

        mNewInterface = new UsbInterface(id, altSetting, name, Class, subClass, protocol);
        mNewInterfaces.add(mNewInterface);
    }

    /* Called from JNI in monitorUsbHostBus() to report new USB endpoint for the device
       currently being added.  Returns true if successful, false in case of error.
     */
    private void addUsbEndpoint(int address, int attributes, int maxPacketSize, int interval) {
        mNewEndpoints.add(new UsbEndpoint(address, attributes, maxPacketSize, interval));
    }

发现在添加相应usb信息时会判断mNewConfiguration和mNewInterface是否为null,如果不为null则会进行mNewInterfaces.clear()操作,而在endUsbDeviceAdded方法中并没有将mNewConfiguration和mNewInterface重置为null的操作,意味着除了第一次检测到usb接入,后续的都会执行clear操作。
因此尝试在add相关信息之前将mNewConfiguration和mNewInterface重置为null,所以在beginUsbDeviceAdded方法的最后添加代码:

mNewConfiguration = null;
mNewInterface = null;
System.out.println("UsbHostManager  by lxf");

重新编译services模块后刷入开发板,重新运行测试代码,日志是这样的:

05-08 07:48:16.489 2101-2101/com.lxf.usbdemo I/System.out: 设备数目:2
05-08 07:48:16.490 2101-2101/com.lxf.usbdemo I/System.out: /dev/bus/usb/001/005=UsbDevice[mName=/dev/bus/usb/001/005,mVendorId=1659,mProductId=8963,mClass=0,mSubclass=0,mProtocol=0,mManufacturerName=Prolific Technology Inc. ,mProductName=USB-Serial Controller D,mSerialNumber=null,mConfigurations=[
05-08 07:48:16.490 2101-2101/com.lxf.usbdemo I/System.out: UsbConfiguration[mId=1,mName=null,mAttributes=128,mMaxPower=50,mInterfaces=[
05-08 07:48:16.490 2101-2101/com.lxf.usbdemo I/System.out: UsbInterface[mId=0,mAlternateSetting=0,mName=null,mClass=255,mSubclass=0,mProtocol=0,mEndpoints=[
05-08 07:48:16.490 2101-2101/com.lxf.usbdemo I/System.out: UsbEndpoint[mAddress=129,mAttributes=3,mMaxPacketSize=10,mInterval=1]
05-08 07:48:16.490 2101-2101/com.lxf.usbdemo I/System.out: UsbEndpoint[mAddress=2,mAttributes=2,mMaxPacketSize=64,mInterval=0]
05-08 07:48:16.490 2101-2101/com.lxf.usbdemo I/System.out: UsbEndpoint[mAddress=131,mAttributes=2,mMaxPacketSize=64,mInterval=0]]]]
05-08 07:48:16.491 2101-2101/com.lxf.usbdemo I/System.out: /dev/bus/usb/001/003=UsbDevice[mName=/dev/bus/usb/001/003,mVendorId=1659,mProductId=8963,mClass=0,mSubclass=0,mProtocol=0,mManufacturerName=Prolific Technology Inc. ,mProductName=USB-Serial Controller D,mSerialNumber=null,mConfigurations=[
05-08 07:48:16.491 2101-2101/com.lxf.usbdemo I/System.out: UsbConfiguration[mId=1,mName=null,mAttributes=160,mMaxPower=50,mInterfaces=[
05-08 07:48:16.491 2101-2101/com.lxf.usbdemo I/System.out: UsbInterface[mId=0,mAlternateSetting=0,mName=null,mClass=255,mSubclass=0,mProtocol=0,mEndpoints=[
05-08 07:48:16.491 2101-2101/com.lxf.usbdemo I/System.out: UsbEndpoint[mAddress=129,mAttributes=3,mMaxPacketSize=10,mInterval=1]
05-08 07:48:16.491 2101-2101/com.lxf.usbdemo I/System.out: UsbEndpoint[mAddress=2,mAttributes=2,mMaxPacketSize=64,mInterval=0]
05-08 07:48:16.491 2101-2101/com.lxf.usbdemo I/System.out: UsbEndpoint[mAddress=131,mAttributes=2,mMaxPacketSize=64,mInterval=0]]]]

发现确实两个设备的信息都打印出来了,于是激动的去测试多个串口同时打开的问题,并没有发生crash,并且可以连接上,附一张图,绿色提示表示连接成功了。


多串口.jpg

你可能感兴趣的:(android framework之旅(六)Usb多串口同时打开)