Search code examples
androidhidusb-otg

USB OTG device does not work after unplugging and replugging


I am trying to interface with a USB OTG device on Android. Since code is a bit lengthy, I will just outline the basics:

  • There’s an activity with an Intent filter for USB_DEVICE_ATTACHED (filtered for the device IDs I am interested in) declared in the manifest.
  • When the activity is launched with that particular intent, it starts a service and passes the USB device as an argument.
  • When the servie starts, it registers a broadcast receiver for USB_DEVICE_DETACHED, then connects to the device and starts interacting with it (which currently generates some log output).
  • When the USB disconnect broadcast is received and the device is the one currently in use, close() is called on the USB device connection and the service stops.

The device is a Si4701 USB FM tuner, which presents itself as a USB HID device.

When I first plug in the device, I see my service connects to it, can change channels and receives RDS data.

However, when I unplug it and plug it back in, I see the service connecting to it again (and again apparently successfully reading the device registers), but it fails to change frequencies and I never see any RDS data.

If I force the app to stop between unplugging and replugging the device, the device works normally after geing plugged back in.

This suggests some cleanup fails to take place, or some resource isn’t being released properly. However:

  • I don’t keep any data structures related to the device between connects. Everything related to the USB connection is in class instances which get created when a device is detected, and all static members of these classes are final.
  • Some things work after replugging the device, such as reading registers for device types and firmware version (which works by getting a USB HID feature report). Initialization also sets a few registers (through sending a USB feature report), which does not return an error—but changing frequency (which also involves setting registers) does not work.
  • A ~15-minute interval between unplugging and replugging has no effect (device doesn’t work). On the other hand, if I unplug the device, kill the service and plug it back in, it works instantly—so the issue is definitely on the Android side, not on the device side.

What do I need to do to make the USB device work after an unplug/replug cycle?


Solution

  • I still don’t fully understand what is happening here, but it seems to work now.

    I should mention that the app is multi-threaded and the Si470x wrapper class is getting called from multiple threads.

    Since this has resulted in race conditions (one thread accessing the device when another thread had just closed it), I properly declared all methods as synchronized so that no two threads can access methods of the tuner class at the same time. Plugged the device in, unplugged it, plugged it back in—and lo and behold, it changes frequencies and spits out RDS data the way it is supposed to.

    Suspicion:

    UsbDeviceConnection#close() might not be thread-safe.

    Conclusions:

    • Always call UsbDeviceConnection#close() when you’re done with the device (including when the device is detached).
    • If your app is multithreaded, make sure that everything which accesses your USB connection (directly or indirectly) is properly synchronized.