MTP: Add support for retrieving thumbnails to MTP content provider.

Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/media/mtp/MtpCursor.cpp b/media/mtp/MtpCursor.cpp
index 42d9e38..d63a5bf 100644
--- a/media/mtp/MtpCursor.cpp
+++ b/media/mtp/MtpCursor.cpp
@@ -62,6 +62,7 @@
 #define OBJECT_DATE_CREATED         218
 #define OBJECT_DATE_MODIFIED        219
 #define OBJECT_KEYWORDS             220
+#define OBJECT_THUMB                221
 
 MtpCursor::MtpCursor(MtpClient* client, int queryType, int deviceID,
                 int storageID, int objectID, int columnCount, int* columns)
@@ -364,6 +365,10 @@
                 if (!putString(window, objectInfo->mKeywords, row, i))
                     goto fail;
                  break;
+            case OBJECT_THUMB:
+                if (!putThumbnail(window, objectID, row, i))
+                    goto fail;
+                break;
             default:
                 LOGE("fillStorage: unknown column %d\n", mColumns[i]);
                 goto fail;
@@ -421,4 +426,28 @@
     return true;
 }
 
+bool MtpCursor::putThumbnail(CursorWindow* window, int objectID, int row, int column) {
+    MtpDevice* device = mClient->getDevice(mDeviceID);
+    int size;
+    void* thumbnail = device->getThumbnail(objectID, size);
+
+    LOGD("putThumbnail: %p, size: %d\n", thumbnail, size);
+    int offset = window->alloc(size);
+    if (!offset) {
+        window->freeLastRow();
+        LOGE("Failed allocating %u bytes for thumbnail", size);
+        return false;
+    }
+    if (size > 0)
+        window->copyIn(offset, (const uint8_t*)thumbnail, size);
+
+    // This must be updated after the call to alloc(), since that
+    // may move the field around in the window
+    field_slot_t * fieldSlot = window->getFieldSlot(row, column);
+    fieldSlot->type = FIELD_TYPE_BLOB;
+    fieldSlot->data.buffer.offset = offset;
+    fieldSlot->data.buffer.size = size;
+    return true;
+}
+
 } // namespace android
diff --git a/media/mtp/MtpCursor.h b/media/mtp/MtpCursor.h
index 422f0c9..d51c052 100644
--- a/media/mtp/MtpCursor.h
+++ b/media/mtp/MtpCursor.h
@@ -68,6 +68,7 @@
     bool        prepareRow(CursorWindow* window);
     bool        putLong(CursorWindow* window, int value, int row, int column);
     bool        putString(CursorWindow* window, const char* text, int row, int column);
+    bool        putThumbnail(CursorWindow* window, int objectID, int row, int column);
 };
 
 }; // namespace android
diff --git a/media/mtp/MtpDataPacket.cpp b/media/mtp/MtpDataPacket.cpp
index f96284c..d12425a 100644
--- a/media/mtp/MtpDataPacket.cpp
+++ b/media/mtp/MtpDataPacket.cpp
@@ -361,11 +361,24 @@
 #ifdef MTP_HOST
 int MtpDataPacket::read(struct usb_endpoint *ep) {
     // first read the header
-    int ret = transfer(ep, mBuffer, mBufferSize);
-printf("MtpDataPacket::transfer returned %d\n", ret);
-    if (ret >= 0)
-        mPacketSize = ret;
-    return ret;
+    int length = transfer(ep, mBuffer, mBufferSize);
+    if (length > MTP_CONTAINER_HEADER_SIZE) {
+        // look at the length field to see if the data spans multiple packets
+        uint32_t totalLength = MtpPacket::getUInt32(MTP_CONTAINER_LENGTH_OFFSET);
+        while (totalLength > length) {
+            allocate(length + mAllocationIncrement);
+            int ret = transfer(ep, mBuffer + length, mAllocationIncrement);
+            if (ret >= 0)
+                length += ret;
+            else {
+                length = ret;
+                break;
+            }
+        }
+    }
+    if (length >= 0)
+        mPacketSize = length;
+    return length;
 }
 
 int MtpDataPacket::write(struct usb_endpoint *ep) {
@@ -382,4 +395,18 @@
 
 #endif // MTP_HOST
 
+void* MtpDataPacket::getData(int& outLength) const {
+    int length = mPacketSize - MTP_CONTAINER_HEADER_SIZE;
+    if (length > 0) {
+        void* result = malloc(length);
+        if (result) {
+            memcpy(result, mBuffer + MTP_CONTAINER_HEADER_SIZE, length);
+            outLength = length;
+            return result;
+        }
+    }
+    outLength = 0;
+    return NULL;
+}
+
 }  // namespace android
diff --git a/media/mtp/MtpDataPacket.h b/media/mtp/MtpDataPacket.h
index 4e743b2..146ef64 100644
--- a/media/mtp/MtpDataPacket.h
+++ b/media/mtp/MtpDataPacket.h
@@ -99,6 +99,7 @@
 #endif
 
     inline bool         hasData() const { return mPacketSize > MTP_CONTAINER_HEADER_SIZE; }
+    void*               getData(int& outLength) const;
 };
 
 }; // namespace android
diff --git a/media/mtp/MtpDevice.cpp b/media/mtp/MtpDevice.cpp
index 5c39628..ee35217 100644
--- a/media/mtp/MtpDevice.cpp
+++ b/media/mtp/MtpDevice.cpp
@@ -194,6 +194,20 @@
     return NULL;
 }
 
+void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
+    mRequest.reset();
+    mRequest.setParameter(1, handle);
+    if (sendRequest(MTP_OPERATION_GET_THUMB) && readData()) {
+        MtpResponseCode ret = readResponse();
+        if (ret == MTP_RESPONSE_OK) {
+            return mData.getData(outLength);
+        }
+    }
+    outLength = 0;
+    return NULL;
+
+}
+
 MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
     mRequest.reset();
     mRequest.setParameter(1, code);
diff --git a/media/mtp/MtpDevice.h b/media/mtp/MtpDevice.h
index 226b247..0ee930f 100644
--- a/media/mtp/MtpDevice.h
+++ b/media/mtp/MtpDevice.h
@@ -70,6 +70,7 @@
     MtpStorageInfo*         getStorageInfo(MtpStorageID storageID);
     MtpObjectHandleList*    getObjectHandles(MtpStorageID storageID, MtpObjectFormat format, MtpObjectHandle parent);
     MtpObjectInfo*          getObjectInfo(MtpObjectHandle handle);
+    void*                   getThumbnail(MtpObjectHandle handle, int& outLength);
 
     MtpProperty*            getDevicePropDesc(MtpDeviceProperty code);