Blog icon indicating copy to clipboard operation
Blog copied to clipboard

记录车机先插u盘再插iPhone,u盘会掉的问题

Open jason--liu opened this issue 5 years ago • 0 comments

这个问题很坑,走了不少弯路,在此记录一下。 背景: 在车机上先插入u盘(车机Host)播放音乐,再插入iPhone(车机OTG)然后U盘会掉。

开始调试

跟踪内核打印发现每次掉的时候内核都有"disconnect by usbfs的打印,搜索内核源码,发现是通过ioctl主动调用的。

/* disconnect kernel driver from interface */
case USBDEVFS_DISCONNECT:
	if (intf->dev.driver) {
		driver = to_usb_driver(intf->dev.driver);
		dev_dbg(&intf->dev, "disconnect by usbfs\n");
		usb_driver_release_interface(driver, intf);
	} else
		retval = -ENODATA;
	break;

添加打印

通过添加打印发现调用进程是system_server。

printk(KERN_INFO "The process is \"%s\" (pid %i)\n", current->comm, current->pid);

本以为可以直接发现是某个我们自己写的进程调用,结果是system_server就有点麻烦了。不过还有其他办法,既然是通过ioctl调用,那么系统里面肯定哪里会有USBDEVFS_DISCONNECT调用代码。

调试系统

在Android系统代码里面搜索USBDEVFS_DISCONNECT 在system/core/libusbhost/usbhost.c/里面搜到如下代码

 int usb_device_connect_kernel_driver(struct usb_device *device,
         unsigned int interface, int connect)
 {
     struct usbdevfs_ioctl ctl;
 
     ctl.ifno = interface;
     ctl.ioctl_code = (connect ? USBDEVFS_CONNECT : USBDEVFS_DISCONNECT);
     ctl.data = NULL;
     return ioctl(device->fd, USBDEVFS_IOCTL, &ctl);
 }

好,找都调用函数了,再继续找调用函数 base/core/jni/android_hardware_UsbDeviceConnection.cpp中

static jboolean
android_hardware_UsbDeviceConnection_claim_interface(JNIEnv *env, jobject thiz,
        int interfaceID, jboolean force)
{
    struct usb_device* device = get_device_from_object(env, thiz);
    if (!device) {
        ALOGE("device is closed in native_claim_interface");
        return -1;
    }
    int ret = usb_device_claim_interface(device, interfaceID);
    if (ret && force && errno == EBUSY) {
        // disconnect kernel driver and try again
        usb_device_connect_kernel_driver(device, interfaceID, false);
        ret = usb_device_claim_interface(device, interfaceID);
    }
    return ret == 0;
}

对应的native方法是native_claim_interface,再看下哪里被调用

public boolean claimInterface(UsbInterface intf, boolean force) {
        return native_claim_interface(intf.getId(), force);
    }

这个是系统对外的借口,搜索中发现是我们系统中间件调用了这个接口。 由于中间件是想获取iPhone的信息,当调用 Iterator<UsbDevice> deviceIterator = deviceList.values().iterator(); if (deviceIterator.hasNext()) { HashMap<String, UsbDevice> deviceList = mUsbManager.getDeviceList(); UsbDevice device = (UsbDevice) deviceIterator.next(); } if语句下面获得的第一个设备是优盘,当调用connection.claimInterface(intf, true)时,从上面的jni函数可以知道此时优盘因为在播放音乐会返回EBUSY,然后再调用usb_device_connect_kernel_driver(device, interfaceID, false)就不把优盘整掉线了。

参考资料

https://blog.csdn.net/encourage2011/article/details/76407232 https://blog.csdn.net/u014742281/article/details/89328518 https://www.veryarm.com/57647.html base/core/jni/android_hardware_UsbDeviceConnection.cpp android_hardware_UsbDeviceConnection_claim_interface

jason--liu avatar Jun 21 '19 11:06 jason--liu