UsbMidiDeviceJNI: fix race condition

Try multiple times to open the MIDI control device.
This fixes a race condition that caused Android to
sometimes not see a USB MIDI keyboard when it was plugged in.

Bug: 25328161
Change-Id: Ic72c5859364fc56bf7a40c1b1c9791c42827ea63
Signed-off-by: Phil Burk <philburk@google.com>
diff --git a/services/core/jni/com_android_server_UsbMidiDevice.cpp b/services/core/jni/com_android_server_UsbMidiDevice.cpp
index 06b9bc3..e12a016 100644
--- a/services/core/jni/com_android_server_UsbMidiDevice.cpp
+++ b/services/core/jni/com_android_server_UsbMidiDevice.cpp
@@ -43,12 +43,26 @@
         jint card, jint device)
 {
     char    path[100];
+    int     fd;
+    const   int kMaxRetries = 10;
+    const   int kSleepMicroseconds = 2000;
 
     snprintf(path, sizeof(path), "/dev/snd/controlC%d", card);
-    int fd = open(path, O_RDWR);
-    if (fd < 0) {
-        ALOGE("could not open %s", path);
-        return 0;
+    // This control device may not have been created yet. So we should
+    // try to open it several times to prevent intermittent failure
+    // from a race condition.
+    int retryCounter = 0;
+    while ((fd = open(path, O_RDWR)) < 0) {
+        if (++retryCounter > kMaxRetries) {
+            ALOGE("timed out after %d tries, could not open %s", retryCounter, path);
+            return 0;
+        } else {
+            ALOGW("attempt #%d, could not open %s", retryCounter, path);
+            // Increase the sleep interval each time.
+            // 10 retries will total 2 * sum(1..10) = 110 milliseconds.
+            // Typically the device should be ready in 5-10 milliseconds.
+            usleep(kSleepMicroseconds * retryCounter);
+        }
     }
 
     struct snd_rawmidi_info info;