PTP: Improve performance and reliability of file importing

Now the file copy is done completely within the media process
rather than pushing data to the client via ContProvider.openFile().

File system writes are now interleaved with USB reads, which allows us
to copy the data faster and prevents the camera from timing out during transfer.

File is automatically inserted in the media provider after a successful import
and a Uri is returned to the client.

BUG: 2994234

Change-Id: Ie75c63da76f623343d3d966c6a707aa1ae871972
Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index 20dd94d..e1d1a92 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -413,20 +413,32 @@
 }
 
 int MtpDataPacket::readData(struct usb_endpoint *ep, void* buffer, int length) {
-    int packetSize = usb_endpoint_max_packet(ep);
     int read = 0;
     while (read < length) {
-        int ret = transfer(ep, (char *)buffer + read, packetSize);
+        int ret = transfer(ep, (char *)buffer + read, length - read);
         if (ret < 0) {
-printf("MtpDataPacket::readData returning %d\n", ret);
             return ret;
         }
         read += ret;
     }
-printf("MtpDataPacket::readData returning %d\n", read);
     return read;
 }
 
+// Queue a read request.  Call readDataWait to wait for result
+int MtpDataPacket::readDataAsync(struct usb_endpoint *ep, void* buffer, int length) {
+    if (usb_endpoint_queue(ep, buffer, length)) {
+        LOGE("usb_endpoint_queue failed, errno: %d", errno);
+        return -1;
+    }
+    return 0;
+}
+
+// Wait for result of readDataAsync
+int MtpDataPacket::readDataWait(struct usb_endpoint *ep) {
+    int ep_num;
+    return usb_endpoint_wait(usb_endpoint_get_device(ep), &ep_num);
+}
+
 int MtpDataPacket::readDataHeader(struct usb_endpoint *ep) {
     int length = transfer(ep, mBuffer, usb_endpoint_max_packet(ep));
     if (length >= 0)
@@ -454,15 +466,7 @@
 }
 
 int MtpDataPacket::write(struct usb_endpoint *ep, void* buffer, uint32_t length) {
-    int ret = 0;
-    int packetSize = usb_endpoint_max_packet(ep);
-    while (length > 0) {
-        int write = (length > packetSize ? packetSize : length);
-        int ret = transfer(ep, buffer, write);
-        if (ret < 0)
-            break;
-        length -= ret;
-    }
+    int ret = transfer(ep, buffer, length);
     return (ret < 0 ? ret : 0);
 }
 
diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h
index fab6a07..3ae6226 100644
--- a/media/mtp/MtpDataPacket.h
+++ b/media/mtp/MtpDataPacket.h
@@ -102,6 +102,8 @@
 #ifdef MTP_HOST
     int                 read(struct usb_endpoint *ep);
     int                 readData(struct usb_endpoint *ep, void* buffer, int length);
+    int                 readDataAsync(struct usb_endpoint *ep, void* buffer, int length);
+    int                 readDataWait(struct usb_endpoint *ep);
     int                 readDataHeader(struct usb_endpoint *ep);
 
     int                 writeDataHeader(struct usb_endpoint *ep, uint32_t length);
@@ -110,6 +112,7 @@
 #endif
 
     inline bool         hasData() const { return mPacketSize > MTP_CONTAINER_HEADER_SIZE; }
+    inline uint32_t     getContainerLength() const { return MtpPacket::getUInt32(MTP_CONTAINER_LENGTH_OFFSET); }
     void*               getData(int& outLength) const;
 };
 
diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp
index fca0142..1b23a8e 100644
--- a/media/mtp/MtpDevice.cpp
+++ b/media/mtp/MtpDevice.cpp
@@ -348,97 +348,90 @@
     return NULL;
 }
 
-class ReadObjectThread : public Thread {
-private:
-    MtpDevice*          mDevice;
-    MtpObjectHandle     mHandle;
-    int                 mObjectSize;
-    void*               mInitialData;
-    int                 mInitialDataLength;
-    int                 mFD;
-
-public:
-    ReadObjectThread(MtpDevice* device, MtpObjectHandle handle, int objectSize)
-        : mDevice(device),
-          mHandle(handle),
-          mObjectSize(objectSize),
-          mInitialData(NULL),
-          mInitialDataLength(0)
-    {
-    }
-
-    virtual ~ReadObjectThread() {
-        if (mFD >= 0)
-            close(mFD);
-        free(mInitialData);
-    }
-
-    // returns file descriptor
-    int init() {
-        mDevice->mRequest.reset();
-        mDevice->mRequest.setParameter(1, mHandle);
-        if (mDevice->sendRequest(MTP_OPERATION_GET_OBJECT)
-                && mDevice->mData.readDataHeader(mDevice->mEndpointIn)) {
-
-            // mData will contain header and possibly the beginning of the object data
-            mInitialData = mDevice->mData.getData(mInitialDataLength);
-
-            // create a pipe for the client to read from
-            int pipefd[2];
-            if (pipe(pipefd) < 0) {
-                LOGE("pipe failed (%s)", strerror(errno));
-                return -1;
-            }
-
-            mFD = pipefd[1];
-            return pipefd[0];
-        } else {
-           return -1;
-        }
-    }
-
-    virtual bool threadLoop() {
-        int remaining = mObjectSize;
-        if (mInitialData) {
-            write(mFD, mInitialData, mInitialDataLength);
-            remaining -= mInitialDataLength;
-            free(mInitialData);
-            mInitialData = NULL;
-        }
-
-        char buffer[16384];
-        while (remaining > 0) {
-            int readSize = (remaining > sizeof(buffer) ? sizeof(buffer) : remaining);
-            int count = mDevice->mData.readData(mDevice->mEndpointIn, buffer, readSize);
-            int written;
-            if (count >= 0) {
-                int written = write(mFD, buffer, count);
-                // FIXME check error
-                remaining -= count;
-            } else {
-                break;
-            }
-        }
-
-        MtpResponseCode ret = mDevice->readResponse();
-        mDevice->mMutex.unlock();
+// reads the object's data and writes it to the specified file path
+bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath) {
+    LOGD("readObject: %s", destPath);
+    int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC);
+    if (fd < 0) {
+        LOGE("open failed for %s", destPath);
         return false;
     }
-};
 
-    // returns the file descriptor for a pipe to read the object's data
-int MtpDevice::readObject(MtpObjectHandle handle, int objectSize) {
-    mMutex.lock();
+    Mutex::Autolock autoLock(mMutex);
+    bool result = false;
 
-    ReadObjectThread* thread = new ReadObjectThread(this, handle, objectSize);
-    int fd = thread->init();
-    if (fd < 0) {
-        delete thread;
-        mMutex.unlock();
-    } else {
-        thread->run("ReadObjectThread");
+    mRequest.reset();
+    mRequest.setParameter(1, handle);
+    if (sendRequest(MTP_OPERATION_GET_OBJECT)
+            && mData.readDataHeader(mEndpointIn)) {
+        uint32_t length = mData.getContainerLength();
+        if (length < MTP_CONTAINER_HEADER_SIZE)
+            goto fail;
+        length -= MTP_CONTAINER_HEADER_SIZE;
+        uint32_t remaining = length;
+
+        int initialDataLength = 0;
+        void* initialData = mData.getData(initialDataLength);
+        if (initialData) {
+            if (initialDataLength > 0) {
+                if (write(fd, initialData, initialDataLength) != initialDataLength)
+                    goto fail;
+                remaining -= initialDataLength;
+            }
+            free(initialData);
+        }
+
+        // USB reads greater than 16K don't work
+        char buffer1[16384], buffer2[16384];
+        char* readBuffer = buffer1;
+        char* writeBuffer = NULL;
+        int writeLength = 0;
+
+        while (remaining > 0 || writeBuffer) {
+            if (remaining > 0) {
+                // queue up a read request
+                int readSize = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
+                if (mData.readDataAsync(mEndpointIn, readBuffer, readSize)) {
+                    LOGE("readDataAsync failed");
+                    goto fail;
+                }
+            } else {
+                readBuffer = NULL;
+            }
+
+            if (writeBuffer) {
+                // write previous buffer
+                if (write(fd, writeBuffer, writeLength) != writeLength) {
+                    LOGE("write failed");
+                    // wait for pending read before failing
+                    if (readBuffer)
+                        mData.readDataWait(mEndpointIn);
+                    goto fail;
+                }
+                writeBuffer = NULL;
+            }
+
+            // wait for read to complete
+            if (readBuffer) {
+                int read = mData.readDataWait(mEndpointIn);
+                if (read < 0)
+                    goto fail;
+
+                writeBuffer = readBuffer;
+                writeLength = read;
+                remaining -= read;
+                readBuffer = (readBuffer == buffer1 ? buffer2 : buffer1);
+            }
+        }
+
+        MtpResponseCode response = readResponse();
+        if (response == MTP_RESPONSE_OK)
+            result = true;
     }
-    return fd;
+
+fail:
+    ::close(fd);
+    return result;
 }
 
 bool MtpDevice::sendRequest(MtpOperationCode operation) {
diff --git a/media/mtp/MtpDevice.h b/media/mtp/MtpDevice.h
index 57f492f..720502c 100644
--- a/media/mtp/MtpDevice.h
+++ b/media/mtp/MtpDevice.h
@@ -86,12 +86,9 @@
 
     MtpProperty*            getDevicePropDesc(MtpDeviceProperty code);
 
-    // returns the file descriptor for a pipe to read the object's data
-    int                     readObject(MtpObjectHandle handle, int objectSize);
+    bool                   readObject(MtpObjectHandle handle, const char* destPath);
 
 private:
-    friend class ReadObjectThread;
-
     bool                    sendRequest(MtpOperationCode operation);
     bool                    sendData();
     bool                    readData();