MTP: Implement GetPartialObject command

Allows host to read partial contents of files on the device

Change-Id: I74927f7394224d674e1d150a4b72a51d9358459b
Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/media/mtp/MtpPacket.cpp b/media/mtp/MtpPacket.cpp
index 42bf8ba..bd6196f 100644
--- a/media/mtp/MtpPacket.cpp
+++ b/media/mtp/MtpPacket.cpp
@@ -123,7 +123,7 @@
 
 uint32_t MtpPacket::getParameter(int index) const {
     if (index < 1 || index > 5) {
-        LOGE("index %d out of range in MtpRequestPacket::getParameter", index);
+        LOGE("index %d out of range in MtpPacket::getParameter", index);
         return 0;
     }
     return getUInt32(MTP_CONTAINER_PARAMETER_OFFSET + (index - 1) * sizeof(uint32_t));
@@ -131,7 +131,7 @@
 
 void MtpPacket::setParameter(int index, uint32_t value) {
     if (index < 1 || index > 5) {
-        LOGE("index %d out of range in MtpResponsePacket::setParameter", index);
+        LOGE("index %d out of range in MtpPacket::setParameter", index);
         return;
     }
     int offset = MTP_CONTAINER_PARAMETER_OFFSET + (index - 1) * sizeof(uint32_t);
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index ca13636..b841bf3 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -66,7 +66,7 @@
 //    MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
 //    MTP_OPERATION_MOVE_OBJECT,
 //    MTP_OPERATION_COPY_OBJECT,
-//    MTP_OPERATION_GET_PARTIAL_OBJECT,
+    MTP_OPERATION_GET_PARTIAL_OBJECT,
 //    MTP_OPERATION_INITIATE_OPEN_CAPTURE,
     MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
     MTP_OPERATION_GET_OBJECT_PROP_DESC,
@@ -289,6 +289,9 @@
         case MTP_OPERATION_GET_OBJECT:
             response = doGetObject();
             break;
+        case MTP_OPERATION_GET_PARTIAL_OBJECT:
+            response = doGetPartialObject();
+            break;
         case MTP_OPERATION_SEND_OBJECT_INFO:
             response = doSendObjectInfo();
             break;
@@ -583,6 +586,45 @@
     return MTP_RESPONSE_OK;
 }
 
+MtpResponseCode MtpServer::doGetPartialObject() {
+    MtpObjectHandle handle = mRequest.getParameter(1);
+    uint32_t offset = mRequest.getParameter(2);
+    uint32_t length = mRequest.getParameter(3);
+    MtpString pathBuf;
+    int64_t fileLength;
+    int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength);
+    if (result != MTP_RESPONSE_OK)
+        return result;
+    if (offset + length > fileLength)
+        length = fileLength - offset;
+
+    const char* filePath = (const char *)pathBuf;
+    mtp_file_range  mfr;
+    mfr.fd = open(filePath, O_RDONLY);
+    if (mfr.fd < 0) {
+        return MTP_RESPONSE_GENERAL_ERROR;
+    }
+    mfr.offset = offset;
+    mfr.length = length;
+    mResponse.setParameter(1, length);
+
+    // send data header
+    mData.setOperationCode(mRequest.getOperationCode());
+    mData.setTransactionID(mRequest.getTransactionID());
+    mData.writeDataHeader(mFD, length + MTP_CONTAINER_HEADER_SIZE);
+
+    // then transfer the file
+    int ret = ioctl(mFD, MTP_SEND_FILE, (unsigned long)&mfr);
+    close(mfr.fd);
+    if (ret < 0) {
+        if (errno == ECANCELED)
+            return MTP_RESPONSE_TRANSACTION_CANCELLED;
+        else
+            return MTP_RESPONSE_GENERAL_ERROR;
+    }
+    return MTP_RESPONSE_OK;
+}
+
 MtpResponseCode MtpServer::doSendObjectInfo() {
     MtpString path;
     MtpStorageID storageID = mRequest.getParameter(1);
diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h
index e65ddb0..5aee4ea 100644
--- a/media/mtp/MtpServer.h
+++ b/media/mtp/MtpServer.h
@@ -96,6 +96,7 @@
     MtpResponseCode     doGetObjectPropList();
     MtpResponseCode     doGetObjectInfo();
     MtpResponseCode     doGetObject();
+    MtpResponseCode     doGetPartialObject();
     MtpResponseCode     doSendObjectInfo();
     MtpResponseCode     doSendObject();
     MtpResponseCode     doDeleteObject();