Retry allocations in case of ENODEV

Unlike the read() and write() calls, the ioctl() call
does not wait for endpoints to be enabled, which is
problematic if ioctls() are the first calls on the
usb connection, since they may be called before the
endpoints are set up. We'll have to retry if this
causes them to fail.

Bug: 34822471
Test: Change configs to mtp and ptp
Change-Id: I89610b9614c33f0b22535cc68cd0c9b502cb55c9
diff --git a/media/mtp/MtpFfsHandle.cpp b/media/mtp/MtpFfsHandle.cpp
index 5d747c2..1583218 100644
--- a/media/mtp/MtpFfsHandle.cpp
+++ b/media/mtp/MtpFfsHandle.cpp
@@ -62,8 +62,13 @@
 constexpr int USB_FFS_MAX_WRITE = MTP_BUFFER_SIZE;
 constexpr int USB_FFS_MAX_READ = MTP_BUFFER_SIZE;
 
+static_assert(USB_FFS_MAX_WRITE > 0, "Max r/w values must be > 0!");
+static_assert(USB_FFS_MAX_READ > 0, "Max r/w values must be > 0!");
+
 constexpr unsigned int MAX_MTP_FILE_SIZE = 0xFFFFFFFF;
 
+constexpr size_t ENDPOINT_ALLOC_RETRIES = 10;
+
 struct func_desc {
     struct usb_interface_descriptor intf;
     struct usb_endpoint_descriptor_no_audio sink;
@@ -459,19 +464,28 @@
     mMaxWrite = android::base::GetIntProperty("sys.usb.ffs.max_write", USB_FFS_MAX_WRITE);
     mMaxRead = android::base::GetIntProperty("sys.usb.ffs.max_read", USB_FFS_MAX_READ);
 
-    while (mMaxWrite > USB_FFS_MAX_WRITE && mMaxRead > USB_FFS_MAX_READ) {
+    size_t attempts = 0;
+    while (mMaxWrite >= USB_FFS_MAX_WRITE && mMaxRead >= USB_FFS_MAX_READ &&
+            attempts < ENDPOINT_ALLOC_RETRIES) {
         // If larger contiguous chunks of memory aren't available, attempt to try
         // smaller allocations.
         if (ioctl(mBulkIn, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(mMaxWrite)) ||
             ioctl(mBulkOut, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(mMaxRead))) {
+            if (errno == ENODEV) {
+                // Driver hasn't enabled endpoints yet.
+                std::this_thread::sleep_for(std::chrono::milliseconds(100));
+                attempts += 1;
+                continue;
+            }
             mMaxWrite /= 2;
             mMaxRead /=2;
         } else {
             return 0;
         }
     }
+    // Try to start MtpServer anyway, with the smallest max r/w values
     PLOG(ERROR) << "Functionfs could not allocate any memory!";
-    return -1;
+    return 0;
 }
 
 int MtpFfsHandle::configure(bool usePtp) {
diff --git a/media/mtp/MtpFfsHandle.h b/media/mtp/MtpFfsHandle.h
index 44ff0f3..b4d5a97 100644
--- a/media/mtp/MtpFfsHandle.h
+++ b/media/mtp/MtpFfsHandle.h
@@ -59,6 +59,10 @@
     int sendFile(mtp_file_range mfr);
     int sendEvent(mtp_event me);
 
+    /**
+     * Open ffs endpoints and allocate necessary kernel and user memory.
+     * Will sleep until endpoints are enabled, for up to 1 second.
+     */
     int start();
     void close();