summaryrefslogtreecommitdiffstats
path: root/mtp
diff options
context:
space:
mode:
Diffstat (limited to 'mtp')
-rw-r--r--mtp/Android.mk79
-rw-r--r--mtp/MtpDataPacket.cpp492
-rw-r--r--mtp/MtpDataPacket.h123
-rw-r--r--mtp/MtpDatabase.h116
-rw-r--r--mtp/MtpDebug.cpp424
-rw-r--r--mtp/MtpDebug.h50
-rw-r--r--mtp/MtpDevice.cpp851
-rw-r--r--mtp/MtpDevice.h116
-rw-r--r--mtp/MtpDeviceInfo.cpp94
-rw-r--r--mtp/MtpDeviceInfo.h54
-rw-r--r--mtp/MtpEventPacket.cpp68
-rw-r--r--mtp/MtpEventPacket.h46
-rw-r--r--mtp/MtpMessage.hpp33
-rw-r--r--mtp/MtpObjectInfo.cpp105
-rw-r--r--mtp/MtpObjectInfo.h60
-rw-r--r--mtp/MtpPacket.cpp164
-rw-r--r--mtp/MtpPacket.h72
-rw-r--r--mtp/MtpProperty.cpp531
-rw-r--r--mtp/MtpProperty.h114
-rw-r--r--mtp/MtpRequestPacket.cpp61
-rw-r--r--mtp/MtpRequestPacket.h49
-rw-r--r--mtp/MtpResponsePacket.cpp58
-rw-r--r--mtp/MtpResponsePacket.h48
-rw-r--r--mtp/MtpServer.cpp1379
-rw-r--r--mtp/MtpServer.h155
-rw-r--r--mtp/MtpStorage.cpp856
-rw-r--r--mtp/MtpStorage.h120
-rw-r--r--mtp/MtpStorageInfo.cpp69
-rw-r--r--mtp/MtpStorageInfo.h49
-rw-r--r--mtp/MtpStringBuffer.cpp167
-rw-r--r--mtp/MtpStringBuffer.h55
-rw-r--r--mtp/MtpTypes.h78
-rw-r--r--mtp/MtpUtils.cpp83
-rw-r--r--mtp/MtpUtils.h27
-rw-r--r--mtp/btree.cpp79
-rw-r--r--mtp/btree.hpp82
-rw-r--r--mtp/mtp.h607
-rw-r--r--mtp/mtp_MtpDatabase.cpp871
-rw-r--r--mtp/mtp_MtpDatabase.hpp146
-rw-r--r--mtp/mtp_MtpServer.cpp199
-rw-r--r--mtp/mtp_MtpServer.hpp65
-rw-r--r--mtp/node.cpp143
-rw-r--r--mtp/twrpMtp.cpp123
-rw-r--r--mtp/twrpMtp.hpp48
44 files changed, 9209 insertions, 0 deletions
diff --git a/mtp/Android.mk b/mtp/Android.mk
new file mode 100644
index 000000000..32518d3fa
--- /dev/null
+++ b/mtp/Android.mk
@@ -0,0 +1,79 @@
+LOCAL_PATH := $(call my-dir)
+
+# Build libtwrpmtp library
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libtwrpmtp
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64 -DMTP_DEVICE -DMTP_HOST -fno-strict-aliasing
+LOCAL_C_INCLUDES += $(LOCAL_PATH) bionic frameworks/base/include system/core/include bionic/libc/private/
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+ LOCAL_C_INCLUDES += external/stlport/stlport
+ LOCAL_SHARED_LIBRARIES += libstlport
+else
+ LOCAL_SHARED_LIBRARIES += libc++
+endif
+
+LOCAL_SRC_FILES = \
+ btree.cpp \
+ MtpDataPacket.cpp \
+ MtpDebug.cpp \
+ MtpDevice.cpp \
+ MtpDeviceInfo.cpp \
+ MtpEventPacket.cpp \
+ MtpObjectInfo.cpp \
+ MtpPacket.cpp \
+ MtpProperty.cpp \
+ MtpRequestPacket.cpp \
+ MtpResponsePacket.cpp \
+ MtpServer.cpp \
+ MtpStorage.cpp \
+ MtpStorageInfo.cpp \
+ MtpStringBuffer.cpp \
+ MtpUtils.cpp \
+ mtp_MtpServer.cpp \
+ twrpMtp.cpp \
+ mtp_MtpDatabase.cpp \
+ node.cpp
+LOCAL_SHARED_LIBRARIES += libz libc libusbhost libstdc++ libdl libcutils libutils libaosprecovery libselinux
+
+ifneq ($(TW_MTP_DEVICE),)
+ LOCAL_CFLAGS += -DUSB_MTP_DEVICE=$(TW_MTP_DEVICE)
+endif
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 25; echo $$?),0)
+ LOCAL_CFLAGS += -DHAS_USBHOST_TIMEOUT
+endif
+
+include $(BUILD_SHARED_LIBRARY)
+
+# Build twrpmtp binary / executable
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := twrpmtp
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64 -DMTP_DEVICE -DMTP_HOST -DTWRPMTP
+LOCAL_C_INCLUDES += $(LOCAL_PATH) bionic frameworks/base/include system/core/include bionic/libc/private/
+LOCAL_SRC_FILES = \
+ btree.cpp \
+ MtpDataPacket.cpp \
+ MtpDebug.cpp \
+ MtpDevice.cpp \
+ MtpDeviceInfo.cpp \
+ MtpEventPacket.cpp \
+ MtpObjectInfo.cpp \
+ MtpPacket.cpp \
+ MtpProperty.cpp \
+ MtpRequestPacket.cpp \
+ MtpResponsePacket.cpp \
+ MtpServer.cpp \
+ MtpStorage.cpp \
+ MtpStorageInfo.cpp \
+ MtpStringBuffer.cpp \
+ MtpUtils.cpp \
+ mtp_MtpServer.cpp \
+ twrpMtp.cpp \
+ mtp_MtpDatabase.cpp \
+ node.cpp
+LOCAL_SHARED_LIBRARIES += libz libc libusbhost libstdc++ libdl libcutils libutils libaosprecovery
+include $(BUILD_EXECUTABLE)
diff --git a/mtp/MtpDataPacket.cpp b/mtp/MtpDataPacket.cpp
new file mode 100644
index 000000000..845db968e
--- /dev/null
+++ b/mtp/MtpDataPacket.cpp
@@ -0,0 +1,492 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to c++
+ *
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include <usbhost/usbhost.h>
+
+#include "MtpDataPacket.h"
+#include "MtpStringBuffer.h"
+#include "MtpDebug.h"
+
+#define MTP_BUFFER_SIZE 16384
+
+
+MtpDataPacket::MtpDataPacket()
+ : MtpPacket(MTP_BUFFER_SIZE), // MAX_USBFS_BUFFER_SIZE
+ mOffset(MTP_CONTAINER_HEADER_SIZE)
+{
+}
+
+MtpDataPacket::~MtpDataPacket() {
+}
+
+void MtpDataPacket::reset() {
+ MtpPacket::reset();
+ mOffset = MTP_CONTAINER_HEADER_SIZE;
+}
+
+void MtpDataPacket::setOperationCode(MtpOperationCode code) {
+ MtpPacket::putUInt16(MTP_CONTAINER_CODE_OFFSET, code);
+}
+
+void MtpDataPacket::setTransactionID(MtpTransactionID id) {
+ MtpPacket::putUInt32(MTP_CONTAINER_TRANSACTION_ID_OFFSET, id);
+}
+
+uint16_t MtpDataPacket::getUInt16() {
+ int offset = mOffset;
+ uint16_t result = (uint16_t)mBuffer[offset] | ((uint16_t)mBuffer[offset + 1] << 8);
+ mOffset += 2;
+ return result;
+}
+
+uint32_t MtpDataPacket::getUInt32() {
+ int offset = mOffset;
+ uint32_t result = (uint32_t)mBuffer[offset] | ((uint32_t)mBuffer[offset + 1] << 8) |
+ ((uint32_t)mBuffer[offset + 2] << 16) | ((uint32_t)mBuffer[offset + 3] << 24);
+ mOffset += 4;
+ return result;
+}
+
+uint64_t MtpDataPacket::getUInt64() {
+ int offset = mOffset;
+ uint64_t result = (uint64_t)mBuffer[offset] | ((uint64_t)mBuffer[offset + 1] << 8) |
+ ((uint64_t)mBuffer[offset + 2] << 16) | ((uint64_t)mBuffer[offset + 3] << 24) |
+ ((uint64_t)mBuffer[offset + 4] << 32) | ((uint64_t)mBuffer[offset + 5] << 40) |
+ ((uint64_t)mBuffer[offset + 6] << 48) | ((uint64_t)mBuffer[offset + 7] << 56);
+ mOffset += 8;
+ return result;
+}
+
+void MtpDataPacket::getUInt128(uint128_t& value) {
+ value[0] = getUInt32();
+ value[1] = getUInt32();
+ value[2] = getUInt32();
+ value[3] = getUInt32();
+}
+
+void MtpDataPacket::getString(MtpStringBuffer& string)
+{
+ string.readFromPacket(this);
+}
+
+Int8List* MtpDataPacket::getAInt8() {
+ Int8List* result = new Int8List;
+ int count = getUInt32();
+ for (int i = 0; i < count; i++)
+ result->push(getInt8());
+ return result;
+}
+
+UInt8List* MtpDataPacket::getAUInt8() {
+ UInt8List* result = new UInt8List;
+ int count = getUInt32();
+ for (int i = 0; i < count; i++)
+ result->push(getUInt8());
+ return result;
+}
+
+Int16List* MtpDataPacket::getAInt16() {
+ Int16List* result = new Int16List;
+ int count = getUInt32();
+ for (int i = 0; i < count; i++)
+ result->push(getInt16());
+ return result;
+}
+
+UInt16List* MtpDataPacket::getAUInt16() {
+ UInt16List* result = new UInt16List;
+ int count = getUInt32();
+ for (int i = 0; i < count; i++)
+ result->push(getUInt16());
+ return result;
+}
+
+Int32List* MtpDataPacket::getAInt32() {
+ Int32List* result = new Int32List;
+ int count = getUInt32();
+ for (int i = 0; i < count; i++)
+ result->push(getInt32());
+ return result;
+}
+
+UInt32List* MtpDataPacket::getAUInt32() {
+ UInt32List* result = new UInt32List;
+ int count = getUInt32();
+ for (int i = 0; i < count; i++)
+ result->push(getUInt32());
+ return result;
+}
+
+Int64List* MtpDataPacket::getAInt64() {
+ Int64List* result = new Int64List;
+ int count = getUInt32();
+ for (int i = 0; i < count; i++)
+ result->push(getInt64());
+ return result;
+}
+
+UInt64List* MtpDataPacket::getAUInt64() {
+ UInt64List* result = new UInt64List;
+ int count = getUInt32();
+ for (int i = 0; i < count; i++)
+ result->push(getUInt64());
+ return result;
+}
+
+void MtpDataPacket::putInt8(int8_t value) {
+ allocate(mOffset + 1);
+ mBuffer[mOffset++] = (uint8_t)value;
+ if (mPacketSize < mOffset)
+ mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putUInt8(uint8_t value) {
+ allocate(mOffset + 1);
+ mBuffer[mOffset++] = (uint8_t)value;
+ if (mPacketSize < mOffset)
+ mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putInt16(int16_t value) {
+ allocate(mOffset + 2);
+ mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
+ if (mPacketSize < mOffset)
+ mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putUInt16(uint16_t value) {
+ allocate(mOffset + 2);
+ mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
+ if (mPacketSize < mOffset)
+ mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putInt32(int32_t value) {
+ allocate(mOffset + 4);
+ mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF);
+ if (mPacketSize < mOffset)
+ mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putUInt32(uint32_t value) {
+ allocate(mOffset + 4);
+ mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF);
+ if (mPacketSize < mOffset)
+ mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putInt64(int64_t value) {
+ allocate(mOffset + 8);
+ mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 32) & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 40) & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 48) & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 56) & 0xFF);
+ if (mPacketSize < mOffset)
+ mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putUInt64(uint64_t value) {
+ allocate(mOffset + 8);
+ mBuffer[mOffset++] = (uint8_t)(value & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 8) & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 16) & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 24) & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 32) & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 40) & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 48) & 0xFF);
+ mBuffer[mOffset++] = (uint8_t)((value >> 56) & 0xFF);
+ if (mPacketSize < mOffset)
+ mPacketSize = mOffset;
+}
+
+void MtpDataPacket::putInt128(const int128_t& value) {
+ putInt32(value[0]);
+ putInt32(value[1]);
+ putInt32(value[2]);
+ putInt32(value[3]);
+}
+
+void MtpDataPacket::putUInt128(const uint128_t& value) {
+ putUInt32(value[0]);
+ putUInt32(value[1]);
+ putUInt32(value[2]);
+ putUInt32(value[3]);
+}
+
+void MtpDataPacket::putInt128(int64_t value) {
+ putInt64(value);
+ putInt64(value < 0 ? -1 : 0);
+}
+
+void MtpDataPacket::putUInt128(uint64_t value) {
+ putUInt64(value);
+ putUInt64(0);
+}
+
+void MtpDataPacket::putAInt8(const int8_t* values, int count) {
+ putUInt32(count);
+ for (int i = 0; i < count; i++)
+ putInt8(*values++);
+}
+
+void MtpDataPacket::putAUInt8(const uint8_t* values, int count) {
+ putUInt32(count);
+ for (int i = 0; i < count; i++)
+ putUInt8(*values++);
+}
+
+void MtpDataPacket::putAInt16(const int16_t* values, int count) {
+ putUInt32(count);
+ for (int i = 0; i < count; i++)
+ putInt16(*values++);
+}
+
+void MtpDataPacket::putAUInt16(const uint16_t* values, int count) {
+ putUInt32(count);
+ for (int i = 0; i < count; i++)
+ putUInt16(*values++);
+}
+
+void MtpDataPacket::putAUInt16(const UInt16List* values) {
+ size_t count = (values ? values->size() : 0);
+ putUInt32(count);
+ for (size_t i = 0; i < count; i++)
+ putUInt16((*values)[i]);
+}
+
+void MtpDataPacket::putAInt32(const int32_t* values, int count) {
+ putUInt32(count);
+ for (int i = 0; i < count; i++)
+ putInt32(*values++);
+}
+
+void MtpDataPacket::putAUInt32(const uint32_t* values, int count) {
+ putUInt32(count);
+ for (int i = 0; i < count; i++)
+ putUInt32(*values++);
+}
+
+void MtpDataPacket::putAUInt32(const UInt32List* list) {
+ if (!list) {
+ putEmptyArray();
+ } else {
+ size_t size = list->size();
+ putUInt32(size);
+ for (size_t i = 0; i < size; i++)
+ putUInt32((*list)[i]);
+ }
+}
+
+void MtpDataPacket::putAInt64(const int64_t* values, int count) {
+ putUInt32(count);
+ for (int i = 0; i < count; i++)
+ putInt64(*values++);
+}
+
+void MtpDataPacket::putAUInt64(const uint64_t* values, int count) {
+ putUInt32(count);
+ for (int i = 0; i < count; i++)
+ putUInt64(*values++);
+}
+
+void MtpDataPacket::putString(const MtpStringBuffer& string) {
+ string.writeToPacket(this);
+}
+
+void MtpDataPacket::putString(const char* s) {
+ MtpStringBuffer string(s);
+ string.writeToPacket(this);
+}
+
+void MtpDataPacket::putString(const uint16_t* string) {
+ int count = 0;
+ for (int i = 0; i < 256; i++) {
+ if (string[i])
+ count++;
+ else
+ break;
+ }
+ putUInt8(count > 0 ? count + 1 : 0);
+ for (int i = 0; i < count; i++)
+ putUInt16(string[i]);
+ // only terminate with zero if string is not empty
+ if (count > 0)
+ putUInt16(0);
+}
+
+#ifdef MTP_DEVICE
+int MtpDataPacket::read(int fd) {
+ int ret = ::read(fd, mBuffer, MTP_BUFFER_SIZE);
+ if (ret < MTP_CONTAINER_HEADER_SIZE)
+ return -1;
+ mPacketSize = ret;
+ mOffset = MTP_CONTAINER_HEADER_SIZE;
+ return ret;
+}
+
+int MtpDataPacket::write(int fd) {
+ MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+ MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
+ int ret = ::write(fd, mBuffer, mPacketSize);
+ return (ret < 0 ? ret : 0);
+}
+
+int MtpDataPacket::writeData(int fd, void* data, uint32_t length) {
+ allocate(length);
+ memcpy(mBuffer + MTP_CONTAINER_HEADER_SIZE, data, length);
+ length += MTP_CONTAINER_HEADER_SIZE;
+ MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length);
+ MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
+ int ret = ::write(fd, mBuffer, length);
+ return (ret < 0 ? ret : 0);
+}
+
+#endif // MTP_DEVICE
+
+#ifdef MTP_HOST
+int MtpDataPacket::read(struct usb_request *request) {
+ // first read the header
+ request->buffer = mBuffer;
+ request->buffer_length = mBufferSize;
+ int32_t length = transfer(request);
+ 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);
+ allocate(totalLength);
+ while (totalLength > (uint32_t)length) {
+ request->buffer = mBuffer + length;
+ request->buffer_length = totalLength - length;
+ int ret = transfer(request);
+ if (ret >= 0)
+ length += ret;
+ else {
+ length = ret;
+ break;
+ }
+ }
+ }
+ if (length >= 0)
+ mPacketSize = length;
+ return length;
+}
+
+int MtpDataPacket::readData(struct usb_request *request, void* buffer, int length) {
+ int read = 0;
+ while (read < length) {
+ request->buffer = (char *)buffer + read;
+ request->buffer_length = length - read;
+ int ret = transfer(request);
+ if (ret < 0) {
+ return ret;
+ }
+ read += ret;
+ }
+ return read;
+}
+
+// Queue a read request. Call readDataWait to wait for result
+int MtpDataPacket::readDataAsync(struct usb_request *req) {
+ if (usb_request_queue(req)) {
+ MTPE("usb_endpoint_queue failed, errno: %d", errno);
+ return -1;
+ }
+ return 0;
+}
+
+// Wait for result of readDataAsync
+int MtpDataPacket::readDataWait(struct usb_device *device) {
+#ifdef HAS_USBHOST_TIMEOUT
+ struct usb_request *req = usb_request_wait(device, 200);
+#else
+ struct usb_request *req = usb_request_wait(device);
+#endif
+ return (req ? req->actual_length : -1);
+}
+
+int MtpDataPacket::readDataHeader(struct usb_request *request) {
+ request->buffer = mBuffer;
+ request->buffer_length = request->max_packet_size;
+ int length = transfer(request);
+ if (length >= 0)
+ mPacketSize = length;
+ return length;
+}
+
+int MtpDataPacket::writeDataHeader(struct usb_request *request, uint32_t length) {
+ MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, length);
+ MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
+ request->buffer = mBuffer;
+ request->buffer_length = MTP_CONTAINER_HEADER_SIZE;
+ int ret = transfer(request);
+ return (ret < 0 ? ret : 0);
+}
+int MtpDataPacket::write(struct usb_request *request) {
+ MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+ MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
+
+ // send header separately from data
+ request->buffer = mBuffer;
+ request->buffer_length = MTP_CONTAINER_HEADER_SIZE;
+ int ret = transfer(request);
+ if (ret == MTP_CONTAINER_HEADER_SIZE) {
+ request->buffer = mBuffer + MTP_CONTAINER_HEADER_SIZE;
+ request->buffer_length = mPacketSize - MTP_CONTAINER_HEADER_SIZE;
+ ret = transfer(request);
+ }
+ return (ret < 0 ? ret : 0);
+}
+
+int MtpDataPacket::write(struct usb_request *request, void* buffer, uint32_t length) {
+ request->buffer = buffer;
+ request->buffer_length = length;
+ int ret = transfer(request);
+ return (ret < 0 ? ret : 0);
+}
+
+#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;
+}
+
diff --git a/mtp/MtpDataPacket.h b/mtp/MtpDataPacket.h
new file mode 100644
index 000000000..0e7a873ab
--- /dev/null
+++ b/mtp/MtpDataPacket.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to c++
+ *
+ */
+
+#ifndef _MTP_DATA_PACKET_H
+#define _MTP_DATA_PACKET_H
+
+#include "MtpPacket.h"
+#include "mtp.h"
+
+struct usb_device;
+struct usb_request;
+
+
+class MtpStringBuffer;
+
+class MtpDataPacket : public MtpPacket {
+private:
+ // current offset for get/put methods
+ uint64_t mOffset;
+
+public:
+ MtpDataPacket();
+ virtual ~MtpDataPacket();
+
+ virtual void reset();
+
+ void setOperationCode(MtpOperationCode code);
+ void setTransactionID(MtpTransactionID id);
+
+ inline const uint8_t* getData() const { return mBuffer + MTP_CONTAINER_HEADER_SIZE; }
+ inline uint8_t getUInt8() { return (uint8_t)mBuffer[mOffset++]; }
+ inline int8_t getInt8() { return (int8_t)mBuffer[mOffset++]; }
+ uint16_t getUInt16();
+ inline int16_t getInt16() { return (int16_t)getUInt16(); }
+ uint32_t getUInt32();
+ inline int32_t getInt32() { return (int32_t)getUInt32(); }
+ uint64_t getUInt64();
+ inline int64_t getInt64() { return (int64_t)getUInt64(); }
+ void getUInt128(uint128_t& value);
+ inline void getInt128(int128_t& value) { getUInt128((uint128_t&)value); }
+ void getString(MtpStringBuffer& string);
+
+ Int8List* getAInt8();
+ UInt8List* getAUInt8();
+ Int16List* getAInt16();
+ UInt16List* getAUInt16();
+ Int32List* getAInt32();
+ UInt32List* getAUInt32();
+ Int64List* getAInt64();
+ UInt64List* getAUInt64();
+
+ void putInt8(int8_t value);
+ void putUInt8(uint8_t value);
+ void putInt16(int16_t value);
+ void putUInt16(uint16_t value);
+ void putInt32(int32_t value);
+ void putUInt32(uint32_t value);
+ void putInt64(int64_t value);
+ void putUInt64(uint64_t value);
+ void putInt128(const int128_t& value);
+ void putUInt128(const uint128_t& value);
+ void putInt128(int64_t value);
+ void putUInt128(uint64_t value);
+
+ void putAInt8(const int8_t* values, int count);
+ void putAUInt8(const uint8_t* values, int count);
+ void putAInt16(const int16_t* values, int count);
+ void putAUInt16(const uint16_t* values, int count);
+ void putAUInt16(const UInt16List* values);
+ void putAInt32(const int32_t* values, int count);
+ void putAUInt32(const uint32_t* values, int count);
+ void putAUInt32(const UInt32List* list);
+ void putAInt64(const int64_t* values, int count);
+ void putAUInt64(const uint64_t* values, int count);
+ void putString(const MtpStringBuffer& string);
+ void putString(const char* string);
+ void putString(const uint16_t* string);
+ inline void putEmptyString() { putUInt8(0); }
+ inline void putEmptyArray() { putUInt32(0); }
+
+
+#ifdef MTP_DEVICE
+ // fill our buffer with data from the given file descriptor
+ int read(int fd);
+
+ // write our data to the given file descriptor
+ int write(int fd);
+ int writeData(int fd, void* data, uint32_t length);
+#endif
+#ifdef MTP_HOST
+ int read(struct usb_request *request);
+ int readData(struct usb_request *request, void* buffer, int length);
+ int readDataAsync(struct usb_request *req);
+ int readDataWait(struct usb_device *device);
+ int readDataHeader(struct usb_request *ep);
+
+ int writeDataHeader(struct usb_request *ep, uint32_t length);
+ int write(struct usb_request *ep);
+ int write(struct usb_request *ep, void* buffer, uint32_t length);
+#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;
+};
+
+
+#endif // _MTP_DATA_PACKET_H
diff --git a/mtp/MtpDatabase.h b/mtp/MtpDatabase.h
new file mode 100644
index 000000000..a0ff8dace
--- /dev/null
+++ b/mtp/MtpDatabase.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to c++
+ */
+
+#ifndef _MTP_DATABASE_H
+#define _MTP_DATABASE_H
+
+#include "MtpTypes.h"
+
+class MtpDataPacket;
+class MtpProperty;
+class MtpObjectInfo;
+
+class MtpDatabase {
+public:
+ virtual ~MtpDatabase() {}
+
+ // called from SendObjectInfo to reserve a database entry for the incoming file
+ virtual MtpObjectHandle beginSendObject(const char* path,
+ MtpObjectFormat format,
+ MtpObjectHandle parent,
+ MtpStorageID storage,
+ uint64_t size,
+ time_t modified) = 0;
+
+ // called to report success or failure of the SendObject file transfer
+ // success should signal a notification of the new object's creation,
+ // failure should remove the database entry created in beginSendObject
+ virtual void endSendObject(const char* path,
+ MtpObjectHandle handle,
+ MtpObjectFormat format,
+ bool succeeded) = 0;
+
+ virtual MtpObjectHandleList* getObjectList(MtpStorageID storageID,
+ MtpObjectFormat format,
+ MtpObjectHandle parent) = 0;
+
+ virtual int getNumObjects(MtpStorageID storageID,
+ MtpObjectFormat format,
+ MtpObjectHandle parent) = 0;
+
+ // callee should delete[] the results from these
+ // results can be NULL
+ virtual MtpObjectFormatList* getSupportedPlaybackFormats() = 0;
+ virtual MtpObjectFormatList* getSupportedCaptureFormats() = 0;
+ virtual MtpObjectPropertyList* getSupportedObjectProperties(MtpObjectFormat format) = 0;
+ virtual MtpDevicePropertyList* getSupportedDeviceProperties() = 0;
+
+ virtual void createDB(MtpStorage* storage, MtpStorageID storageID) = 0;
+ virtual void destroyDB(MtpStorageID storageID) = 0;
+
+ virtual MtpResponseCode getObjectPropertyValue(MtpObjectHandle handle,
+ MtpObjectProperty property,
+ MtpDataPacket& packet) = 0;
+
+ virtual MtpResponseCode setObjectPropertyValue(MtpObjectHandle handle,
+ MtpObjectProperty property,
+ MtpDataPacket& packet) = 0;
+
+ virtual MtpResponseCode getDevicePropertyValue(MtpDeviceProperty property,
+ MtpDataPacket& packet) = 0;
+
+ virtual MtpResponseCode setDevicePropertyValue(MtpDeviceProperty property,
+ MtpDataPacket& packet) = 0;
+
+ virtual MtpResponseCode resetDeviceProperty(MtpDeviceProperty property) = 0;
+
+ virtual MtpResponseCode getObjectPropertyList(MtpObjectHandle handle,
+ uint32_t format, uint32_t property,
+ int groupCode, int depth,
+ MtpDataPacket& packet) = 0;
+
+ virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle,
+ MtpObjectInfo& info) = 0;
+
+ virtual void* getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) = 0;
+
+ virtual MtpResponseCode getObjectFilePath(MtpObjectHandle handle,
+ MtpString& outFilePath,
+ int64_t& outFileLength,
+ MtpObjectFormat& outFormat) = 0;
+
+ virtual MtpResponseCode deleteFile(MtpObjectHandle handle) = 0;
+
+ virtual MtpObjectHandleList* getObjectReferences(MtpObjectHandle handle) = 0;
+
+ virtual MtpResponseCode setObjectReferences(MtpObjectHandle handle,
+ MtpObjectHandleList* references) = 0;
+
+ virtual MtpProperty* getObjectPropertyDesc(MtpObjectProperty property,
+ MtpObjectFormat format) = 0;
+
+ virtual MtpProperty* getDevicePropertyDesc(MtpDeviceProperty property) = 0;
+
+ virtual void sessionStarted() = 0;
+
+ virtual void sessionEnded() = 0;
+ virtual void lockMutex() = 0;
+ virtual void unlockMutex() = 0;
+};
+
+#endif // _MTP_DATABASE_H
diff --git a/mtp/MtpDebug.cpp b/mtp/MtpDebug.cpp
new file mode 100644
index 000000000..c986923db
--- /dev/null
+++ b/mtp/MtpDebug.cpp
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2017 TeamWin
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "MtpDebug.h"
+#include <stdarg.h>
+#include <stdio.h>
+
+#define MTP_DEBUG_BUFFER_SIZE 2048
+static int debug_enabled = 0;
+
+extern "C" void mtpdebug(const char *fmt, ...)
+{
+ if (debug_enabled) {
+ char buf[MTP_DEBUG_BUFFER_SIZE]; // We're going to limit a single request to 512 bytes
+
+ va_list ap;
+ va_start(ap, fmt);
+ vsnprintf(buf, MTP_DEBUG_BUFFER_SIZE, fmt, ap);
+ va_end(ap);
+
+ fputs(buf, stdout);
+ }
+}
+
+struct CodeEntry {
+ const char* name;
+ uint16_t code;
+};
+
+static const CodeEntry sOperationCodes[] = {
+ { "MTP_OPERATION_GET_DEVICE_INFO", 0x1001 },
+ { "MTP_OPERATION_OPEN_SESSION", 0x1002 },
+ { "MTP_OPERATION_CLOSE_SESSION", 0x1003 },
+ { "MTP_OPERATION_GET_STORAGE_IDS", 0x1004 },
+ { "MTP_OPERATION_GET_STORAGE_INFO", 0x1005 },
+ { "MTP_OPERATION_GET_NUM_OBJECTS", 0x1006 },
+ { "MTP_OPERATION_GET_OBJECT_HANDLES", 0x1007 },
+ { "MTP_OPERATION_GET_OBJECT_INFO", 0x1008 },
+ { "MTP_OPERATION_GET_OBJECT", 0x1009 },
+ { "MTP_OPERATION_GET_THUMB", 0x100A },
+ { "MTP_OPERATION_DELETE_OBJECT", 0x100B },
+ { "MTP_OPERATION_SEND_OBJECT_INFO", 0x100C },
+ { "MTP_OPERATION_SEND_OBJECT", 0x100D },
+ { "MTP_OPERATION_INITIATE_CAPTURE", 0x100E },
+ { "MTP_OPERATION_FORMAT_STORE", 0x100F },
+ { "MTP_OPERATION_RESET_DEVICE", 0x1010 },
+ { "MTP_OPERATION_SELF_TEST", 0x1011 },
+ { "MTP_OPERATION_SET_OBJECT_PROTECTION", 0x1012 },
+ { "MTP_OPERATION_POWER_DOWN", 0x1013 },
+ { "MTP_OPERATION_GET_DEVICE_PROP_DESC", 0x1014 },
+ { "MTP_OPERATION_GET_DEVICE_PROP_VALUE", 0x1015 },
+ { "MTP_OPERATION_SET_DEVICE_PROP_VALUE", 0x1016 },
+ { "MTP_OPERATION_RESET_DEVICE_PROP_VALUE", 0x1017 },
+ { "MTP_OPERATION_TERMINATE_OPEN_CAPTURE", 0x1018 },
+ { "MTP_OPERATION_MOVE_OBJECT", 0x1019 },
+ { "MTP_OPERATION_COPY_OBJECT", 0x101A },
+ { "MTP_OPERATION_GET_PARTIAL_OBJECT", 0x101B },
+ { "MTP_OPERATION_INITIATE_OPEN_CAPTURE", 0x101C },
+ { "MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED", 0x9801 },
+ { "MTP_OPERATION_GET_OBJECT_PROP_DESC", 0x9802 },
+ { "MTP_OPERATION_GET_OBJECT_PROP_VALUE", 0x9803 },
+ { "MTP_OPERATION_SET_OBJECT_PROP_VALUE", 0x9804 },
+ { "MTP_OPERATION_GET_OBJECT_PROP_LIST", 0x9805 },
+ { "MTP_OPERATION_SET_OBJECT_PROP_LIST", 0x9806 },
+ { "MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC", 0x9807 },
+ { "MTP_OPERATION_SEND_OBJECT_PROP_LIST", 0x9808 },
+ { "MTP_OPERATION_GET_OBJECT_REFERENCES", 0x9810 },
+ { "MTP_OPERATION_SET_OBJECT_REFERENCES", 0x9811 },
+ { "MTP_OPERATION_SKIP", 0x9820 },
+ // android extensions
+ { "MTP_OPERATION_GET_PARTIAL_OBJECT_64", 0x95C1 },
+ { "MTP_OPERATION_SEND_PARTIAL_OBJECT", 0x95C2 },
+ { "MTP_OPERATION_TRUNCATE_OBJECT", 0x95C3 },
+ { "MTP_OPERATION_BEGIN_EDIT_OBJECT", 0x95C4 },
+ { "MTP_OPERATION_END_EDIT_OBJECT", 0x95C5 },
+ { 0, 0 },
+};
+
+static const CodeEntry sFormatCodes[] = {
+ { "MTP_FORMAT_UNDEFINED", 0x3000 },
+ { "MTP_FORMAT_ASSOCIATION", 0x3001 },
+ { "MTP_FORMAT_SCRIPT", 0x3002 },
+ { "MTP_FORMAT_EXECUTABLE", 0x3003 },
+ { "MTP_FORMAT_TEXT", 0x3004 },
+ { "MTP_FORMAT_HTML", 0x3005 },
+ { "MTP_FORMAT_DPOF", 0x3006 },
+ { "MTP_FORMAT_AIFF", 0x3007 },
+ { "MTP_FORMAT_WAV", 0x3008 },
+ { "MTP_FORMAT_MP3", 0x3009 },
+ { "MTP_FORMAT_AVI", 0x300A },
+ { "MTP_FORMAT_MPEG", 0x300B },
+ { "MTP_FORMAT_ASF", 0x300C },
+ { "MTP_FORMAT_DEFINED", 0x3800 },
+ { "MTP_FORMAT_EXIF_JPEG", 0x3801 },
+ { "MTP_FORMAT_TIFF_EP", 0x3802 },
+ { "MTP_FORMAT_FLASHPIX", 0x3803 },
+ { "MTP_FORMAT_BMP", 0x3804 },
+ { "MTP_FORMAT_CIFF", 0x3805 },
+ { "MTP_FORMAT_GIF", 0x3807 },
+ { "MTP_FORMAT_JFIF", 0x3808 },
+ { "MTP_FORMAT_CD", 0x3809 },
+ { "MTP_FORMAT_PICT", 0x380A },
+ { "MTP_FORMAT_PNG", 0x380B },
+ { "MTP_FORMAT_TIFF", 0x380D },
+ { "MTP_FORMAT_TIFF_IT", 0x380E },
+ { "MTP_FORMAT_JP2", 0x380F },
+ { "MTP_FORMAT_JPX", 0x3810 },
+ { "MTP_FORMAT_UNDEFINED_FIRMWARE", 0xB802 },
+ { "MTP_FORMAT_WINDOWS_IMAGE_FORMAT", 0xB881 },
+ { "MTP_FORMAT_UNDEFINED_AUDIO", 0xB900 },
+ { "MTP_FORMAT_WMA", 0xB901 },
+ { "MTP_FORMAT_OGG", 0xB902 },
+ { "MTP_FORMAT_AAC", 0xB903 },
+ { "MTP_FORMAT_AUDIBLE", 0xB904 },
+ { "MTP_FORMAT_FLAC", 0xB906 },
+ { "MTP_FORMAT_UNDEFINED_VIDEO", 0xB980 },
+ { "MTP_FORMAT_WMV", 0xB981 },
+ { "MTP_FORMAT_MP4_CONTAINER", 0xB982 },
+ { "MTP_FORMAT_MP2", 0xB983 },
+ { "MTP_FORMAT_3GP_CONTAINER", 0xB984 },
+ { "MTP_FORMAT_UNDEFINED_COLLECTION", 0xBA00 },
+ { "MTP_FORMAT_ABSTRACT_MULTIMEDIA_ALBUM", 0xBA01 },
+ { "MTP_FORMAT_ABSTRACT_IMAGE_ALBUM", 0xBA02 },
+ { "MTP_FORMAT_ABSTRACT_AUDIO_ALBUM", 0xBA03 },
+ { "MTP_FORMAT_ABSTRACT_VIDEO_ALBUM", 0xBA04 },
+ { "MTP_FORMAT_ABSTRACT_AV_PLAYLIST", 0xBA05 },
+ { "MTP_FORMAT_ABSTRACT_CONTACT_GROUP", 0xBA06 },
+ { "MTP_FORMAT_ABSTRACT_MESSAGE_FOLDER", 0xBA07 },
+ { "MTP_FORMAT_ABSTRACT_CHAPTERED_PRODUCTION", 0xBA08 },
+ { "MTP_FORMAT_ABSTRACT_AUDIO_PLAYLIST", 0xBA09 },
+ { "MTP_FORMAT_ABSTRACT_VIDEO_PLAYLIST", 0xBA0A },
+ { "MTP_FORMAT_ABSTRACT_MEDIACAST", 0xBA0B },
+ { "MTP_FORMAT_WPL_PLAYLIST", 0xBA10 },
+ { "MTP_FORMAT_M3U_PLAYLIST", 0xBA11 },
+ { "MTP_FORMAT_MPL_PLAYLIST", 0xBA12 },
+ { "MTP_FORMAT_ASX_PLAYLIST", 0xBA13 },
+ { "MTP_FORMAT_PLS_PLAYLIST", 0xBA14 },
+ { "MTP_FORMAT_UNDEFINED_DOCUMENT", 0xBA80 },
+ { "MTP_FORMAT_ABSTRACT_DOCUMENT", 0xBA81 },
+ { "MTP_FORMAT_XML_DOCUMENT", 0xBA82 },
+ { "MTP_FORMAT_MS_WORD_DOCUMENT", 0xBA83 },
+ { "MTP_FORMAT_MHT_COMPILED_HTML_DOCUMENT", 0xBA84 },
+ { "MTP_FORMAT_MS_EXCEL_SPREADSHEET", 0xBA85 },
+ { "MTP_FORMAT_MS_POWERPOINT_PRESENTATION", 0xBA86 },
+ { "MTP_FORMAT_UNDEFINED_MESSAGE", 0xBB00 },
+ { "MTP_FORMAT_ABSTRACT_MESSSAGE", 0xBB01 },
+ { "MTP_FORMAT_UNDEFINED_CONTACT", 0xBB80 },
+ { "MTP_FORMAT_ABSTRACT_CONTACT", 0xBB81 },
+ { "MTP_FORMAT_VCARD_2", 0xBB82 },
+ { 0, 0 },
+};
+
+static const CodeEntry sObjectPropCodes[] = {
+ { "MTP_PROPERTY_STORAGE_ID", 0xDC01 },
+ { "MTP_PROPERTY_OBJECT_FORMAT", 0xDC02 },
+ { "MTP_PROPERTY_PROTECTION_STATUS", 0xDC03 },
+ { "MTP_PROPERTY_OBJECT_SIZE", 0xDC04 },
+ { "MTP_PROPERTY_ASSOCIATION_TYPE", 0xDC05 },
+ { "MTP_PROPERTY_ASSOCIATION_DESC", 0xDC06 },
+ { "MTP_PROPERTY_OBJECT_FILE_NAME", 0xDC07 },
+ { "MTP_PROPERTY_DATE_CREATED", 0xDC08 },
+ { "MTP_PROPERTY_DATE_MODIFIED", 0xDC09 },
+ { "MTP_PROPERTY_KEYWORDS", 0xDC0A },
+ { "MTP_PROPERTY_PARENT_OBJECT", 0xDC0B },
+ { "MTP_PROPERTY_ALLOWED_FOLDER_CONTENTS", 0xDC0C },
+ { "MTP_PROPERTY_HIDDEN", 0xDC0D },
+ { "MTP_PROPERTY_SYSTEM_OBJECT", 0xDC0E },
+ { "MTP_PROPERTY_PERSISTENT_UID", 0xDC41 },
+ { "MTP_PROPERTY_SYNC_ID", 0xDC42 },
+ { "MTP_PROPERTY_PROPERTY_BAG", 0xDC43 },
+ { "MTP_PROPERTY_NAME", 0xDC44 },
+ { "MTP_PROPERTY_CREATED_BY", 0xDC45 },
+ { "MTP_PROPERTY_ARTIST", 0xDC46 },
+ { "MTP_PROPERTY_DATE_AUTHORED", 0xDC47 },
+ { "MTP_PROPERTY_DESCRIPTION", 0xDC48 },
+ { "MTP_PROPERTY_URL_REFERENCE", 0xDC49 },
+ { "MTP_PROPERTY_LANGUAGE_LOCALE", 0xDC4A },
+ { "MTP_PROPERTY_COPYRIGHT_INFORMATION", 0xDC4B },
+ { "MTP_PROPERTY_SOURCE", 0xDC4C },
+ { "MTP_PROPERTY_ORIGIN_LOCATION", 0xDC4D },
+ { "MTP_PROPERTY_DATE_ADDED", 0xDC4E },
+ { "MTP_PROPERTY_NON_CONSUMABLE", 0xDC4F },
+ { "MTP_PROPERTY_CORRUPT_UNPLAYABLE", 0xDC50 },
+ { "MTP_PROPERTY_PRODUCER_SERIAL_NUMBER", 0xDC51 },
+ { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_FORMAT", 0xDC81 },
+ { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_SIZE", 0xDC82 },
+ { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_HEIGHT", 0xDC83 },
+ { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_WIDTH", 0xDC84 },
+ { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DURATION", 0xDC85 },
+ { "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DATA", 0xDC86 },
+ { "MTP_PROPERTY_WIDTH", 0xDC87 },
+ { "MTP_PROPERTY_HEIGHT", 0xDC88 },
+ { "MTP_PROPERTY_DURATION", 0xDC89 },
+ { "MTP_PROPERTY_RATING", 0xDC8A },
+ { "MTP_PROPERTY_TRACK", 0xDC8B },
+ { "MTP_PROPERTY_GENRE", 0xDC8C },
+ { "MTP_PROPERTY_CREDITS", 0xDC8D },
+ { "MTP_PROPERTY_LYRICS", 0xDC8E },
+ { "MTP_PROPERTY_SUBSCRIPTION_CONTENT_ID", 0xDC8F },
+ { "MTP_PROPERTY_PRODUCED_BY", 0xDC90 },
+ { "MTP_PROPERTY_USE_COUNT", 0xDC91 },
+ { "MTP_PROPERTY_SKIP_COUNT", 0xDC92 },
+ { "MTP_PROPERTY_LAST_ACCESSED", 0xDC93 },
+ { "MTP_PROPERTY_PARENTAL_RATING", 0xDC94 },
+ { "MTP_PROPERTY_META_GENRE", 0xDC95 },
+ { "MTP_PROPERTY_COMPOSER", 0xDC96 },
+ { "MTP_PROPERTY_EFFECTIVE_RATING", 0xDC97 },
+ { "MTP_PROPERTY_SUBTITLE", 0xDC98 },
+ { "MTP_PROPERTY_ORIGINAL_RELEASE_DATE", 0xDC99 },
+ { "MTP_PROPERTY_ALBUM_NAME", 0xDC9A },
+ { "MTP_PROPERTY_ALBUM_ARTIST", 0xDC9B },
+ { "MTP_PROPERTY_MOOD", 0xDC9C },
+ { "MTP_PROPERTY_DRM_STATUS", 0xDC9D },
+ { "MTP_PROPERTY_SUB_DESCRIPTION", 0xDC9E },
+ { "MTP_PROPERTY_IS_CROPPED", 0xDCD1 },
+ { "MTP_PROPERTY_IS_COLOUR_CORRECTED", 0xDCD2 },
+ { "MTP_PROPERTY_IMAGE_BIT_DEPTH", 0xDCD3 },
+ { "MTP_PROPERTY_F_NUMBER", 0xDCD4 },
+ { "MTP_PROPERTY_EXPOSURE_TIME", 0xDCD5 },
+ { "MTP_PROPERTY_EXPOSURE_INDEX", 0xDCD6 },
+ { "MTP_PROPERTY_TOTAL_BITRATE", 0xDE91 },
+ { "MTP_PROPERTY_BITRATE_TYPE", 0xDE92 },
+ { "MTP_PROPERTY_SAMPLE_RATE", 0xDE93 },
+ { "MTP_PROPERTY_NUMBER_OF_CHANNELS", 0xDE94 },
+ { "MTP_PROPERTY_AUDIO_BIT_DEPTH", 0xDE95 },
+ { "MTP_PROPERTY_SCAN_TYPE", 0xDE97 },
+ { "MTP_PROPERTY_AUDIO_WAVE_CODEC", 0xDE99 },
+ { "MTP_PROPERTY_AUDIO_BITRATE", 0xDE9A },
+ { "MTP_PROPERTY_VIDEO_FOURCC_CODEC", 0xDE9B },
+ { "MTP_PROPERTY_VIDEO_BITRATE", 0xDE9C },
+ { "MTP_PROPERTY_FRAMES_PER_THOUSAND_SECONDS", 0xDE9D },
+ { "MTP_PROPERTY_KEYFRAME_DISTANCE", 0xDE9E },
+ { "MTP_PROPERTY_BUFFER_SIZE", 0xDE9F },
+ { "MTP_PROPERTY_ENCODING_QUALITY", 0xDEA0 },
+ { "MTP_PROPERTY_ENCODING_PROFILE", 0xDEA1 },
+ { "MTP_PROPERTY_DISPLAY_NAME", 0xDCE0 },
+ { "MTP_PROPERTY_BODY_TEXT", 0xDCE1 },
+ { "MTP_PROPERTY_SUBJECT", 0xDCE2 },
+ { "MTP_PROPERTY_PRIORITY", 0xDCE3 },
+ { "MTP_PROPERTY_GIVEN_NAME", 0xDD00 },
+ { "MTP_PROPERTY_MIDDLE_NAMES", 0xDD01 },
+ { "MTP_PROPERTY_FAMILY_NAME", 0xDD02 },
+ { "MTP_PROPERTY_PREFIX", 0xDD03 },
+ { "MTP_PROPERTY_SUFFIX", 0xDD04 },
+ { "MTP_PROPERTY_PHONETIC_GIVEN_NAME", 0xDD05 },
+ { "MTP_PROPERTY_PHONETIC_FAMILY_NAME", 0xDD06 },
+ { "MTP_PROPERTY_EMAIL_PRIMARY", 0xDD07 },
+ { "MTP_PROPERTY_EMAIL_PERSONAL_1", 0xDD08 },
+ { "MTP_PROPERTY_EMAIL_PERSONAL_2", 0xDD09 },
+ { "MTP_PROPERTY_EMAIL_BUSINESS_1", 0xDD0A },
+ { "MTP_PROPERTY_EMAIL_BUSINESS_2", 0xDD0B },
+ { "MTP_PROPERTY_EMAIL_OTHERS", 0xDD0C },
+ { "MTP_PROPERTY_PHONE_NUMBER_PRIMARY", 0xDD0D },
+ { "MTP_PROPERTY_PHONE_NUMBER_PERSONAL", 0xDD0E },
+ { "MTP_PROPERTY_PHONE_NUMBER_PERSONAL_2", 0xDD0F },
+ { "MTP_PROPERTY_PHONE_NUMBER_BUSINESS", 0xDD10 },
+ { "MTP_PROPERTY_PHONE_NUMBER_BUSINESS_2", 0xDD11 },
+ { "MTP_PROPERTY_PHONE_NUMBER_MOBILE", 0xDD12 },
+ { "MTP_PROPERTY_PHONE_NUMBER_MOBILE_2", 0xDD13 },
+ { "MTP_PROPERTY_FAX_NUMBER_PRIMARY", 0xDD14 },
+ { "MTP_PROPERTY_FAX_NUMBER_PERSONAL", 0xDD15 },
+ { "MTP_PROPERTY_FAX_NUMBER_BUSINESS", 0xDD16 },
+ { "MTP_PROPERTY_PAGER_NUMBER", 0xDD17 },
+ { "MTP_PROPERTY_PHONE_NUMBER_OTHERS", 0xDD18 },
+ { "MTP_PROPERTY_PRIMARY_WEB_ADDRESS", 0xDD19 },
+ { "MTP_PROPERTY_PERSONAL_WEB_ADDRESS", 0xDD1A },
+ { "MTP_PROPERTY_BUSINESS_WEB_ADDRESS", 0xDD1B },
+ { "MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS", 0xDD1C },
+ { "MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_2", 0xDD1D },
+ { "MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_3", 0xDD1E },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_FULL", 0xDD1F },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_1", 0xDD20 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_2", 0xDD21 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_CITY", 0xDD22 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_REGION", 0xDD23 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_POSTAL_CODE", 0xDD24 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_COUNTRY", 0xDD25 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_FULL", 0xDD26 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_1", 0xDD27 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_2", 0xDD28 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_CITY", 0xDD29 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_REGION", 0xDD2A },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_POSTAL_CODE", 0xDD2B },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_COUNTRY", 0xDD2C },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_FULL", 0xDD2D },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_1", 0xDD2E },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_2", 0xDD2F },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_CITY", 0xDD30 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_REGION", 0xDD31 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_POSTAL_CODE", 0xDD32 },
+ { "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_COUNTRY", 0xDD33 },
+ { "MTP_PROPERTY_ORGANIZATION_NAME", 0xDD34 },
+ { "MTP_PROPERTY_PHONETIC_ORGANIZATION_NAME", 0xDD35 },
+ { "MTP_PROPERTY_ROLE", 0xDD36 },
+ { "MTP_PROPERTY_BIRTHDATE", 0xDD37 },
+ { "MTP_PROPERTY_MESSAGE_TO", 0xDD40 },
+ { "MTP_PROPERTY_MESSAGE_CC", 0xDD41 },
+ { "MTP_PROPERTY_MESSAGE_BCC", 0xDD42 },
+ { "MTP_PROPERTY_MESSAGE_READ", 0xDD43 },
+ { "MTP_PROPERTY_MESSAGE_RECEIVED_TIME", 0xDD44 },
+ { "MTP_PROPERTY_MESSAGE_SENDER", 0xDD45 },
+ { "MTP_PROPERTY_ACTIVITY_BEGIN_TIME", 0xDD50 },
+ { "MTP_PROPERTY_ACTIVITY_END_TIME", 0xDD51 },
+ { "MTP_PROPERTY_ACTIVITY_LOCATION", 0xDD52 },
+ { "MTP_PROPERTY_ACTIVITY_REQUIRED_ATTENDEES", 0xDD54 },
+ { "MTP_PROPERTY_ACTIVITY_OPTIONAL_ATTENDEES", 0xDD55 },
+ { "MTP_PROPERTY_ACTIVITY_RESOURCES", 0xDD56 },
+ { "MTP_PROPERTY_ACTIVITY_ACCEPTED", 0xDD57 },
+ { "MTP_PROPERTY_ACTIVITY_TENTATIVE", 0xDD58 },
+ { "MTP_PROPERTY_ACTIVITY_DECLINED", 0xDD59 },
+ { "MTP_PROPERTY_ACTIVITY_REMAINDER_TIME", 0xDD5A },
+ { "MTP_PROPERTY_ACTIVITY_OWNER", 0xDD5B },
+ { "MTP_PROPERTY_ACTIVITY_STATUS", 0xDD5C },
+ { "MTP_PROPERTY_OWNER", 0xDD5D },
+ { "MTP_PROPERTY_EDITOR", 0xDD5E },
+ { "MTP_PROPERTY_WEBMASTER", 0xDD5F },
+ { "MTP_PROPERTY_URL_SOURCE", 0xDD60 },
+ { "MTP_PROPERTY_URL_DESTINATION", 0xDD61 },
+ { "MTP_PROPERTY_TIME_BOOKMARK", 0xDD62 },
+ { "MTP_PROPERTY_OBJECT_BOOKMARK", 0xDD63 },
+ { "MTP_PROPERTY_BYTE_BOOKMARK", 0xDD64 },
+ { "MTP_PROPERTY_LAST_BUILD_DATE", 0xDD70 },
+ { "MTP_PROPERTY_TIME_TO_LIVE", 0xDD71 },
+ { "MTP_PROPERTY_MEDIA_GUID", 0xDD72 },
+ { 0, 0 },
+};
+
+static const CodeEntry sDevicePropCodes[] = {
+ { "MTP_DEVICE_PROPERTY_UNDEFINED", 0x5000 },
+ { "MTP_DEVICE_PROPERTY_BATTERY_LEVEL", 0x5001 },
+ { "MTP_DEVICE_PROPERTY_FUNCTIONAL_MODE", 0x5002 },
+ { "MTP_DEVICE_PROPERTY_IMAGE_SIZE", 0x5003 },
+ { "MTP_DEVICE_PROPERTY_COMPRESSION_SETTING", 0x5004 },
+ { "MTP_DEVICE_PROPERTY_WHITE_BALANCE", 0x5005 },
+ { "MTP_DEVICE_PROPERTY_RGB_GAIN", 0x5006 },
+ { "MTP_DEVICE_PROPERTY_F_NUMBER", 0x5007 },
+ { "MTP_DEVICE_PROPERTY_FOCAL_LENGTH", 0x5008 },
+ { "MTP_DEVICE_PROPERTY_FOCUS_DISTANCE", 0x5009 },
+ { "MTP_DEVICE_PROPERTY_FOCUS_MODE", 0x500A },
+ { "MTP_DEVICE_PROPERTY_EXPOSURE_METERING_MODE", 0x500B },
+ { "MTP_DEVICE_PROPERTY_FLASH_MODE", 0x500C },
+ { "MTP_DEVICE_PROPERTY_EXPOSURE_TIME", 0x500D },
+ { "MTP_DEVICE_PROPERTY_EXPOSURE_PROGRAM_MODE", 0x500E },
+ { "MTP_DEVICE_PROPERTY_EXPOSURE_INDEX", 0x500F },
+ { "MTP_DEVICE_PROPERTY_EXPOSURE_BIAS_COMPENSATION", 0x5010 },
+ { "MTP_DEVICE_PROPERTY_DATETIME", 0x5011 },
+ { "MTP_DEVICE_PROPERTY_CAPTURE_DELAY", 0x5012 },
+ { "MTP_DEVICE_PROPERTY_STILL_CAPTURE_MODE", 0x5013 },
+ { "MTP_DEVICE_PROPERTY_CONTRAST", 0x5014 },
+ { "MTP_DEVICE_PROPERTY_SHARPNESS", 0x5015 },
+ { "MTP_DEVICE_PROPERTY_DIGITAL_ZOOM", 0x5016 },
+ { "MTP_DEVICE_PROPERTY_EFFECT_MODE", 0x5017 },
+ { "MTP_DEVICE_PROPERTY_BURST_NUMBER", 0x5018 },
+ { "MTP_DEVICE_PROPERTY_BURST_INTERVAL", 0x5019 },
+ { "MTP_DEVICE_PROPERTY_TIMELAPSE_NUMBER", 0x501A },
+ { "MTP_DEVICE_PROPERTY_TIMELAPSE_INTERVAL", 0x501B },
+ { "MTP_DEVICE_PROPERTY_FOCUS_METERING_MODE", 0x501C },
+ { "MTP_DEVICE_PROPERTY_UPLOAD_URL", 0x501D },
+ { "MTP_DEVICE_PROPERTY_ARTIST", 0x501E },
+ { "MTP_DEVICE_PROPERTY_COPYRIGHT_INFO", 0x501F },
+ { "MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER", 0xD401 },
+ { "MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME", 0xD402 },
+ { "MTP_DEVICE_PROPERTY_VOLUME", 0xD403 },
+ { "MTP_DEVICE_PROPERTY_SUPPORTED_FORMATS_ORDERED", 0xD404 },
+ { "MTP_DEVICE_PROPERTY_DEVICE_ICON", 0xD405 },
+ { "MTP_DEVICE_PROPERTY_PLAYBACK_RATE", 0xD410 },
+ { "MTP_DEVICE_PROPERTY_PLAYBACK_OBJECT", 0xD411 },
+ { "MTP_DEVICE_PROPERTY_PLAYBACK_CONTAINER_INDEX", 0xD412 },
+ { "MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO", 0xD406 },
+ { "MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE", 0xD407 },
+ { 0, 0 },
+};
+
+static const char* getCodeName(uint16_t code, const CodeEntry* table) {
+ const CodeEntry* entry = table;
+ while (entry->name) {
+ if (entry->code == code)
+ return entry->name;
+ entry++;
+ }
+ return "UNKNOWN";
+}
+
+const char* MtpDebug::getOperationCodeName(MtpOperationCode code) {
+ return getCodeName(code, sOperationCodes);
+}
+
+const char* MtpDebug::getFormatCodeName(MtpObjectFormat code) {
+ if (code == 0)
+ return "NONE";
+ return getCodeName(code, sFormatCodes);
+}
+
+const char* MtpDebug::getObjectPropCodeName(MtpPropertyCode code) {
+ if (code == 0)
+ return "NONE";
+ return getCodeName(code, sObjectPropCodes);
+}
+
+const char* MtpDebug::getDevicePropCodeName(MtpPropertyCode code) {
+ if (code == 0)
+ return "NONE";
+ return getCodeName(code, sDevicePropCodes);
+}
+
+void MtpDebug::enableDebug(void) {
+ debug_enabled = 1;
+ MTPD("MTP debug logging enabled\n");
+}
diff --git a/mtp/MtpDebug.h b/mtp/MtpDebug.h
new file mode 100644
index 000000000..073ddcc83
--- /dev/null
+++ b/mtp/MtpDebug.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2017 TeamWin
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef _MTP_DEBUG_H
+#define _MTP_DEBUG_H
+
+// #define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "MtpTypes.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void mtpdebug(const char *fmt, ...);
+
+#define MTPI(...) fprintf(stdout, "I:[MTP] " __VA_ARGS__)
+#define MTPD(...) mtpdebug("D:[MTP] " __VA_ARGS__)
+#define MTPE(...) fprintf(stdout, "E:[MTP] " __VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif
+
+class MtpDebug {
+public:
+ static const char* getOperationCodeName(MtpOperationCode code);
+ static const char* getFormatCodeName(MtpObjectFormat code);
+ static const char* getObjectPropCodeName(MtpPropertyCode code);
+ static const char* getDevicePropCodeName(MtpPropertyCode code);
+ static void enableDebug();
+};
+
+
+#endif // _MTP_DEBUG_H
diff --git a/mtp/MtpDevice.cpp b/mtp/MtpDevice.cpp
new file mode 100644
index 000000000..089fc2272
--- /dev/null
+++ b/mtp/MtpDevice.cpp
@@ -0,0 +1,851 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include "MtpDevice.h"
+#include "MtpDebug.h"
+#include "MtpDeviceInfo.h"
+#include "MtpObjectInfo.h"
+#include "MtpProperty.h"
+#include "MtpStorageInfo.h"
+#include "MtpStringBuffer.h"
+#include "MtpUtils.h"
+#include "MtpDataPacket.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <endian.h>
+
+#include <usbhost/usbhost.h>
+
+
+#if 0
+static bool isMtpDevice(uint16_t vendor, uint16_t product) {
+ // Sandisk Sansa Fuze
+ if (vendor == 0x0781 && product == 0x74c2)
+ return true;
+ // Samsung YP-Z5
+ if (vendor == 0x04e8 && product == 0x503c)
+ return true;
+ return false;
+}
+#endif
+
+#ifdef HAS_USBHOST_TIMEOUT
+static const int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
+#endif
+
+MtpDevice* MtpDevice::open(const char* deviceName, int fd) {
+ struct usb_device *device = usb_device_new(deviceName, fd);
+ if (!device) {
+ MTPE("usb_device_new failed for %s", deviceName);
+ return NULL;
+ }
+
+ struct usb_descriptor_header* desc;
+ struct usb_descriptor_iter iter;
+
+ usb_descriptor_iter_init(device, &iter);
+
+ while ((desc = usb_descriptor_iter_next(&iter)) != NULL) {
+ if (desc->bDescriptorType == USB_DT_INTERFACE) {
+ struct usb_interface_descriptor *interface = (struct usb_interface_descriptor *)desc;
+
+ if (interface->bInterfaceClass == USB_CLASS_STILL_IMAGE &&
+ interface->bInterfaceSubClass == 1 && // Still Image Capture
+ interface->bInterfaceProtocol == 1) // Picture Transfer Protocol (PIMA 15470)
+ {
+#ifdef HAS_USBHOST_TIMEOUT
+ char* manufacturerName = usb_device_get_manufacturer_name(device, USB_CONTROL_TRANSFER_TIMEOUT_MS);
+ char* productName = usb_device_get_product_name(device, USB_CONTROL_TRANSFER_TIMEOUT_MS);
+#else
+ char* manufacturerName = usb_device_get_manufacturer_name(device);
+ char* productName = usb_device_get_product_name(device);
+#endif
+ MTPD("Found camera: \"%s\" \"%s\"\n", manufacturerName, productName);
+ free(manufacturerName);
+ free(productName);
+ } else if (interface->bInterfaceClass == 0xFF &&
+ interface->bInterfaceSubClass == 0xFF &&
+ interface->bInterfaceProtocol == 0) {
+#ifdef HAS_USBHOST_TIMEOUT
+ char* interfaceName = usb_device_get_string(device, interface->iInterface, USB_CONTROL_TRANSFER_TIMEOUT_MS);
+#else
+ char* interfaceName = usb_device_get_string(device, interface->iInterface);
+#endif
+ if (!interfaceName) {
+ continue;
+ } else if (strcmp(interfaceName, "MTP")) {
+ free(interfaceName);
+ continue;
+ }
+ free(interfaceName);
+
+ // Looks like an android style MTP device
+#ifdef HAS_USBHOST_TIMEOUT
+ char* manufacturerName = usb_device_get_manufacturer_name(device, USB_CONTROL_TRANSFER_TIMEOUT_MS);
+ char* productName = usb_device_get_product_name(device, USB_CONTROL_TRANSFER_TIMEOUT_MS);
+#else
+ char* manufacturerName = usb_device_get_manufacturer_name(device);
+ char* productName = usb_device_get_product_name(device);
+#endif
+ MTPI("Found MTP device: \"%s\" \"%s\"\n", manufacturerName, productName);
+ free(manufacturerName);
+ free(productName);
+ }
+#if 0
+ else {
+ // look for special cased devices based on vendor/product ID
+ // we are doing this mainly for testing purposes
+ uint16_t vendor = usb_device_get_vendor_id(device);
+ uint16_t product = usb_device_get_product_id(device);
+ if (!isMtpDevice(vendor, product)) {
+ // not an MTP or PTP device
+ continue;
+ }
+ // request MTP OS string and descriptor
+ // some music players need to see this before entering MTP mode.
+ char buffer[256];
+ memset(buffer, 0, sizeof(buffer));
+ int ret = usb_device_control_transfer(device,
+ USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_STANDARD,
+ USB_REQ_GET_DESCRIPTOR, (USB_DT_STRING << 8) | 0xEE,
+ 0, buffer, sizeof(buffer), 0);
+ MTPE("usb_device_control_transfer returned %d errno: %d\n", ret, errno);
+ if (ret > 0) {
+ MTPI("got MTP string %s\n", buffer);
+ ret = usb_device_control_transfer(device,
+ USB_DIR_IN|USB_RECIP_DEVICE|USB_TYPE_VENDOR, 1,
+ 0, 4, buffer, sizeof(buffer), 0);
+ MTPI("OS descriptor got %d\n", ret);
+ } else {
+ MTPI("no MTP string\n");
+ }
+ }
+#endif
+ // if we got here, then we have a likely MTP or PTP device
+
+ // interface should be followed by three endpoints
+ struct usb_endpoint_descriptor *ep;
+ struct usb_endpoint_descriptor *ep_in_desc = NULL;
+ struct usb_endpoint_descriptor *ep_out_desc = NULL;
+ struct usb_endpoint_descriptor *ep_intr_desc = NULL;
+ for (int i = 0; i < 3; i++) {
+ ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
+ if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
+ MTPE("endpoints not found\n");
+ usb_device_close(device);
+ return NULL;
+ }
+ if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
+ if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ ep_in_desc = ep;
+ else
+ ep_out_desc = ep;
+ } else if (ep->bmAttributes == USB_ENDPOINT_XFER_INT &&
+ ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) {
+ ep_intr_desc = ep;
+ }
+ }
+ if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
+ MTPE("endpoints not found\n");
+ usb_device_close(device);
+ return NULL;
+ }
+
+ if (usb_device_claim_interface(device, interface->bInterfaceNumber)) {
+ MTPE("usb_device_claim_interface failed errno: %d\n", errno);
+ usb_device_close(device);
+ return NULL;
+ }
+
+ MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber,
+ ep_in_desc, ep_out_desc, ep_intr_desc);
+ mtpDevice->initialize();
+ return mtpDevice;
+ }
+ }
+
+ usb_device_close(device);
+ MTPE("device not found");
+ return NULL;
+}
+
+MtpDevice::MtpDevice(struct usb_device* device, int interface,
+ const struct usb_endpoint_descriptor *ep_in,
+ const struct usb_endpoint_descriptor *ep_out,
+ const struct usb_endpoint_descriptor *ep_intr)
+ : mDevice(device),
+ mInterface(interface),
+ mRequestIn1(NULL),
+ mRequestIn2(NULL),
+ mRequestOut(NULL),
+ mRequestIntr(NULL),
+ mDeviceInfo(NULL),
+ mSessionID(0),
+ mTransactionID(0),
+ mReceivedResponse(false)
+{
+ mRequestIn1 = usb_request_new(device, ep_in);
+ mRequestIn2 = usb_request_new(device, ep_in);
+ mRequestOut = usb_request_new(device, ep_out);
+ mRequestIntr = usb_request_new(device, ep_intr);
+}
+
+MtpDevice::~MtpDevice() {
+ close();
+ for (size_t i = 0; i < mDeviceProperties.size(); i++)
+ delete mDeviceProperties[i];
+ usb_request_free(mRequestIn1);
+ usb_request_free(mRequestIn2);
+ usb_request_free(mRequestOut);
+ usb_request_free(mRequestIntr);
+}
+
+void MtpDevice::initialize() {
+ openSession();
+ mDeviceInfo = getDeviceInfo();
+ if (mDeviceInfo) {
+ if (mDeviceInfo->mDeviceProperties) {
+ int count = mDeviceInfo->mDeviceProperties->size();
+ for (int i = 0; i < count; i++) {
+ MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
+ MtpProperty* property = getDevicePropDesc(propCode);
+ if (property)
+ mDeviceProperties.push(property);
+ }
+ }
+ }
+}
+
+void MtpDevice::close() {
+ if (mDevice) {
+ usb_device_release_interface(mDevice, mInterface);
+ usb_device_close(mDevice);
+ mDevice = NULL;
+ }
+}
+
+void MtpDevice::print() {
+ if (mDeviceInfo) {
+ mDeviceInfo->print();
+
+ if (mDeviceInfo->mDeviceProperties) {
+ MTPI("***** DEVICE PROPERTIES *****\n");
+ int count = mDeviceInfo->mDeviceProperties->size();
+ for (int i = 0; i < count; i++) {
+ MtpDeviceProperty propCode = (*mDeviceInfo->mDeviceProperties)[i];
+ MtpProperty* property = getDevicePropDesc(propCode);
+ if (property) {
+ property->print();
+ delete property;
+ }
+ }
+ }
+ }
+
+ if (mDeviceInfo->mPlaybackFormats) {
+ MTPI("***** OBJECT PROPERTIES *****\n");
+ int count = mDeviceInfo->mPlaybackFormats->size();
+ for (int i = 0; i < count; i++) {
+ MtpObjectFormat format = (*mDeviceInfo->mPlaybackFormats)[i];
+ MTPI("*** FORMAT: %s\n", MtpDebug::getFormatCodeName(format));
+ MtpObjectPropertyList* props = getObjectPropsSupported(format);
+ if (props) {
+ for (size_t j = 0; j < props->size(); j++) {
+ MtpObjectProperty prop = (*props)[j];
+ MtpProperty* property = getObjectPropDesc(prop, format);
+ if (property) {
+ property->print();
+ delete property;
+ } else {
+ MTPI("could not fetch property: %s",
+ MtpDebug::getObjectPropCodeName(prop));
+ }
+ }
+ }
+ }
+ }
+}
+
+const char* MtpDevice::getDeviceName() {
+ if (mDevice)
+ return usb_device_get_name(mDevice);
+ else
+ return "???";
+}
+
+bool MtpDevice::openSession() {
+ android::Mutex::Autolock autoLock(mMutex);
+
+ mSessionID = 0;
+ mTransactionID = 0;
+ MtpSessionID newSession = 1;
+ mRequest.reset();
+ mRequest.setParameter(1, newSession);
+ if (!sendRequest(MTP_OPERATION_OPEN_SESSION))
+ return false;
+ MtpResponseCode ret = readResponse();
+ if (ret == MTP_RESPONSE_SESSION_ALREADY_OPEN)
+ newSession = mResponse.getParameter(1);
+ else if (ret != MTP_RESPONSE_OK)
+ return false;
+
+ mSessionID = newSession;
+ mTransactionID = 1;
+ return true;
+}
+
+bool MtpDevice::closeSession() {
+ // FIXME
+ return true;
+}
+
+MtpDeviceInfo* MtpDevice::getDeviceInfo() {
+ android::Mutex::Autolock autoLock(mMutex);
+
+ mRequest.reset();
+ if (!sendRequest(MTP_OPERATION_GET_DEVICE_INFO))
+ return NULL;
+ if (!readData())
+ return NULL;
+ MtpResponseCode ret = readResponse();
+ if (ret == MTP_RESPONSE_OK) {
+ MtpDeviceInfo* info = new MtpDeviceInfo;
+ info->read(mData);
+ return info;
+ }
+ return NULL;
+}
+
+MtpStorageIDList* MtpDevice::getStorageIDs() {
+ android::Mutex::Autolock autoLock(mMutex);
+
+ mRequest.reset();
+ if (!sendRequest(MTP_OPERATION_GET_STORAGE_IDS))
+ return NULL;
+ if (!readData())
+ return NULL;
+ MtpResponseCode ret = readResponse();
+ if (ret == MTP_RESPONSE_OK) {
+ return mData.getAUInt32();
+ }
+ return NULL;
+}
+
+MtpStorageInfo* MtpDevice::getStorageInfo(MtpStorageID storageID) {
+ android::Mutex::Autolock autoLock(mMutex);
+
+ mRequest.reset();
+ mRequest.setParameter(1, storageID);
+ if (!sendRequest(MTP_OPERATION_GET_STORAGE_INFO))
+ return NULL;
+ if (!readData())
+ return NULL;
+ MtpResponseCode ret = readResponse();
+ if (ret == MTP_RESPONSE_OK) {
+ MtpStorageInfo* info = new MtpStorageInfo(storageID);
+ info->read(mData);
+ return info;
+ }
+ return NULL;
+}
+
+MtpObjectHandleList* MtpDevice::getObjectHandles(MtpStorageID storageID,
+ MtpObjectFormat format, MtpObjectHandle parent) {
+ android::Mutex::Autolock autoLock(mMutex);
+
+ mRequest.reset();
+ mRequest.setParameter(1, storageID);
+ mRequest.setParameter(2, format);
+ mRequest.setParameter(3, parent);
+ if (!sendRequest(MTP_OPERATION_GET_OBJECT_HANDLES))
+ return NULL;
+ if (!readData())
+ return NULL;
+ MtpResponseCode ret = readResponse();
+ if (ret == MTP_RESPONSE_OK) {
+ return mData.getAUInt32();
+ }
+ return NULL;
+}
+
+MtpObjectInfo* MtpDevice::getObjectInfo(MtpObjectHandle handle) {
+ android::Mutex::Autolock autoLock(mMutex);
+
+ // FIXME - we might want to add some caching here
+
+ mRequest.reset();
+ mRequest.setParameter(1, handle);
+ if (!sendRequest(MTP_OPERATION_GET_OBJECT_INFO))
+ return NULL;
+ if (!readData())
+ return NULL;
+ MtpResponseCode ret = readResponse();
+ if (ret == MTP_RESPONSE_OK) {
+ MtpObjectInfo* info = new MtpObjectInfo(handle);
+ info->read(mData);
+ return info;
+ }
+ return NULL;
+}
+
+void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
+ android::Mutex::Autolock autoLock(mMutex);
+
+ 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;
+}
+
+MtpObjectHandle MtpDevice::sendObjectInfo(MtpObjectInfo* info) {
+ android::Mutex::Autolock autoLock(mMutex);
+
+ mRequest.reset();
+ MtpObjectHandle parent = info->mParent;
+ if (parent == 0)
+ parent = MTP_PARENT_ROOT;
+
+ mRequest.setParameter(1, info->mStorageID);
+ mRequest.setParameter(2, info->mParent);
+
+ mData.putUInt32(info->mStorageID);
+ mData.putUInt16(info->mFormat);
+ mData.putUInt16(info->mProtectionStatus);
+ mData.putUInt32(info->mCompressedSize);
+ mData.putUInt16(info->mThumbFormat);
+ mData.putUInt32(info->mThumbCompressedSize);
+ mData.putUInt32(info->mThumbPixWidth);
+ mData.putUInt32(info->mThumbPixHeight);
+ mData.putUInt32(info->mImagePixWidth);
+ mData.putUInt32(info->mImagePixHeight);
+ mData.putUInt32(info->mImagePixDepth);
+ mData.putUInt32(info->mParent);
+ mData.putUInt16(info->mAssociationType);
+ mData.putUInt32(info->mAssociationDesc);
+ mData.putUInt32(info->mSequenceNumber);
+ mData.putString(info->mName);
+
+ char created[100], modified[100];
+ formatDateTime(info->mDateCreated, created, sizeof(created));
+ formatDateTime(info->mDateModified, modified, sizeof(modified));
+
+ mData.putString(created);
+ mData.putString(modified);
+ if (info->mKeywords)
+ mData.putString(info->mKeywords);
+ else
+ mData.putEmptyString();
+
+ if (sendRequest(MTP_OPERATION_SEND_OBJECT_INFO) && sendData()) {
+ MtpResponseCode ret = readResponse();
+ if (ret == MTP_RESPONSE_OK) {
+ info->mStorageID = mResponse.getParameter(1);
+ info->mParent = mResponse.getParameter(2);
+ info->mHandle = mResponse.getParameter(3);
+ return info->mHandle;
+ }
+ }
+ return (MtpObjectHandle)-1;
+}
+
+bool MtpDevice::sendObject(MtpObjectInfo* info, int srcFD) {
+ android::Mutex::Autolock autoLock(mMutex);
+
+ int remaining = info->mCompressedSize;
+ mRequest.reset();
+ mRequest.setParameter(1, info->mHandle);
+ if (sendRequest(MTP_OPERATION_SEND_OBJECT)) {
+ // send data header
+ writeDataHeader(MTP_OPERATION_SEND_OBJECT, remaining);
+
+ char buffer[65536];
+ while (remaining > 0) {
+ int count = read(srcFD, buffer, sizeof(buffer));
+ if (count > 0) {
+ int written = mData.write(mRequestOut, buffer, count);
+ // FIXME check error
+ remaining -= count;
+ } else {
+ break;
+ }
+ }
+ }
+ MtpResponseCode ret = readResponse();
+ return (remaining == 0 && ret == MTP_RESPONSE_OK);
+}
+
+bool MtpDevice::deleteObject(MtpObjectHandle handle) {
+ android::Mutex::Autolock autoLock(mMutex);
+
+ mRequest.reset();
+ mRequest.setParameter(1, handle);
+ if (sendRequest(MTP_OPERATION_DELETE_OBJECT)) {
+ MtpResponseCode ret = readResponse();
+ if (ret == MTP_RESPONSE_OK)
+ return true;
+ }
+ return false;
+}
+
+MtpObjectHandle MtpDevice::getParent(MtpObjectHandle handle) {
+ MtpObjectInfo* info = getObjectInfo(handle);
+ if (info) {
+ MtpObjectHandle parent = info->mParent;
+ delete info;
+ return parent;
+ } else {
+ return -1;
+ }
+}
+
+MtpObjectHandle MtpDevice::getStorageID(MtpObjectHandle handle) {
+ MtpObjectInfo* info = getObjectInfo(handle);
+ if (info) {
+ MtpObjectHandle storageId = info->mStorageID;
+ delete info;
+ return storageId;
+ } else {
+ return -1;
+ }
+}
+
+MtpObjectPropertyList* MtpDevice::getObjectPropsSupported(MtpObjectFormat format) {
+ android::Mutex::Autolock autoLock(mMutex);
+
+ mRequest.reset();
+ mRequest.setParameter(1, format);
+ if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED))
+ return NULL;
+ if (!readData())
+ return NULL;
+ MtpResponseCode ret = readResponse();
+ if (ret == MTP_RESPONSE_OK) {
+ return mData.getAUInt16();
+ }
+ return NULL;
+
+}
+
+MtpProperty* MtpDevice::getDevicePropDesc(MtpDeviceProperty code) {
+ android::Mutex::Autolock autoLock(mMutex);
+
+ mRequest.reset();
+ mRequest.setParameter(1, code);
+ if (!sendRequest(MTP_OPERATION_GET_DEVICE_PROP_DESC))
+ return NULL;
+ if (!readData())
+ return NULL;
+ MtpResponseCode ret = readResponse();
+ if (ret == MTP_RESPONSE_OK) {
+ MtpProperty* property = new MtpProperty;
+ property->read(mData);
+ return property;
+ }
+ return NULL;
+}
+
+MtpProperty* MtpDevice::getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format) {
+ android::Mutex::Autolock autoLock(mMutex);
+
+ mRequest.reset();
+ mRequest.setParameter(1, code);
+ mRequest.setParameter(2, format);
+ if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROP_DESC))
+ return NULL;
+ if (!readData())
+ return NULL;
+ MtpResponseCode ret = readResponse();
+ if (ret == MTP_RESPONSE_OK) {
+ MtpProperty* property = new MtpProperty;
+ property->read(mData);
+ return property;
+ }
+ return NULL;
+}
+
+bool MtpDevice::readObject(MtpObjectHandle handle,
+ bool (* callback)(void* data, int offset, int length, void* clientData),
+ int objectSize, void* clientData) {
+ android::Mutex::Autolock autoLock(mMutex);
+ bool result = false;
+
+ mRequest.reset();
+ mRequest.setParameter(1, handle);
+ if (sendRequest(MTP_OPERATION_GET_OBJECT)
+ && mData.readDataHeader(mRequestIn1)) {
+ uint32_t length = mData.getContainerLength();
+ if ((int)length - MTP_CONTAINER_HEADER_SIZE != objectSize) {
+ MTPE("readObject error objectSize: %d, length: %d",
+ objectSize, length);
+ goto fail;
+ }
+ length -= MTP_CONTAINER_HEADER_SIZE;
+ uint32_t remaining = length;
+ int offset = 0;
+
+ int initialDataLength = 0;
+ void* initialData = mData.getData(initialDataLength);
+ if (initialData) {
+ if (initialDataLength > 0) {
+ if (!callback(initialData, 0, initialDataLength, clientData))
+ goto fail;
+ remaining -= initialDataLength;
+ offset += initialDataLength;
+ }
+ free(initialData);
+ }
+
+ // USB reads greater than 16K don't work
+ char buffer1[16384], buffer2[16384];
+ mRequestIn1->buffer = buffer1;
+ mRequestIn2->buffer = buffer2;
+ struct usb_request* req = mRequestIn1;
+ void* writeBuffer = NULL;
+ int writeLength = 0;
+
+ while (remaining > 0 || writeBuffer) {
+ if (remaining > 0) {
+ // queue up a read request
+ req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
+ if (mData.readDataAsync(req)) {
+ MTPE("readDataAsync failed");
+ goto fail;
+ }
+ } else {
+ req = NULL;
+ }
+
+ if (writeBuffer) {
+ // write previous buffer
+ if (!callback(writeBuffer, offset, writeLength, clientData)) {
+ MTPE("write failed");
+ // wait for pending read before failing
+ if (req)
+ mData.readDataWait(mDevice);
+ goto fail;
+ }
+ offset += writeLength;
+ writeBuffer = NULL;
+ }
+
+ // wait for read to complete
+ if (req) {
+ int read = mData.readDataWait(mDevice);
+ if (read < 0)
+ goto fail;
+
+ if (read > 0) {
+ writeBuffer = req->buffer;
+ writeLength = read;
+ remaining -= read;
+ req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
+ } else {
+ writeBuffer = NULL;
+ }
+ }
+ }
+
+ MtpResponseCode response = readResponse();
+ if (response == MTP_RESPONSE_OK)
+ result = true;
+ }
+
+fail:
+ return result;
+}
+
+
+// reads the object's data and writes it to the specified file path
+bool MtpDevice::readObject(MtpObjectHandle handle, const char* destPath, int group, int perm) {
+ MTPI("readObject: %s", destPath);
+ int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC, 0640);
+ if (fd < 0) {
+ MTPE("open failed for %s", destPath);
+ return false;
+ }
+
+ fchown(fd, getuid(), group);
+ // set permissions
+ int mask = umask(0);
+ fchmod(fd, perm);
+ umask(mask);
+
+ android::Mutex::Autolock autoLock(mMutex);
+ bool result = false;
+
+ mRequest.reset();
+ mRequest.setParameter(1, handle);
+ if (sendRequest(MTP_OPERATION_GET_OBJECT)
+ && mData.readDataHeader(mRequestIn1)) {
+ 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) {
+ free(initialData);
+ goto fail;
+ }
+ remaining -= initialDataLength;
+ }
+ free(initialData);
+ }
+
+ // USB reads greater than 16K don't work
+ char buffer1[16384], buffer2[16384];
+ mRequestIn1->buffer = buffer1;
+ mRequestIn2->buffer = buffer2;
+ struct usb_request* req = mRequestIn1;
+ void* writeBuffer = NULL;
+ int writeLength = 0;
+
+ while (remaining > 0 || writeBuffer) {
+ if (remaining > 0) {
+ // queue up a read request
+ req->buffer_length = (remaining > sizeof(buffer1) ? sizeof(buffer1) : remaining);
+ if (mData.readDataAsync(req)) {
+ MTPE("readDataAsync failed");
+ goto fail;
+ }
+ } else {
+ req = NULL;
+ }
+
+ if (writeBuffer) {
+ // write previous buffer
+ if (write(fd, writeBuffer, writeLength) != writeLength) {
+ MTPE("write failed");
+ // wait for pending read before failing
+ if (req)
+ mData.readDataWait(mDevice);
+ goto fail;
+ }
+ writeBuffer = NULL;
+ }
+
+ // wait for read to complete
+ if (req) {
+ int read = mData.readDataWait(mDevice);
+ if (read < 0)
+ goto fail;
+
+ if (read > 0) {
+ writeBuffer = req->buffer;
+ writeLength = read;
+ remaining -= read;
+ req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
+ } else {
+ writeBuffer = NULL;
+ }
+ }
+ }
+
+ MtpResponseCode response = readResponse();
+ if (response == MTP_RESPONSE_OK)
+ result = true;
+ }
+
+fail:
+ ::close(fd);
+ return result;
+}
+
+bool MtpDevice::sendRequest(MtpOperationCode operation) {
+ MTPD("sendRequest: %s\n", MtpDebug::getOperationCodeName(operation));
+ mReceivedResponse = false;
+ mRequest.setOperationCode(operation);
+ if (mTransactionID > 0)
+ mRequest.setTransactionID(mTransactionID++);
+ int ret = mRequest.write(mRequestOut);
+ mRequest.dump();
+ return (ret > 0);
+}
+
+bool MtpDevice::sendData() {
+ MTPD("sendData\n");
+ mData.setOperationCode(mRequest.getOperationCode());
+ mData.setTransactionID(mRequest.getTransactionID());
+ int ret = mData.write(mRequestOut);
+ mData.dump();
+ return (ret > 0);
+}
+
+bool MtpDevice::readData() {
+ mData.reset();
+ int ret = mData.read(mRequestIn1);
+ MTPD("readData returned %d\n", ret);
+ if (ret >= MTP_CONTAINER_HEADER_SIZE) {
+ if (mData.getContainerType() == MTP_CONTAINER_TYPE_RESPONSE) {
+ MTPD("got response packet instead of data packet");
+ // we got a response packet rather than data
+ // copy it to mResponse
+ mResponse.copyFrom(mData);
+ mReceivedResponse = true;
+ return false;
+ }
+ mData.dump();
+ return true;
+ }
+ else {
+ MTPE("readResponse failed\n");
+ return false;
+ }
+}
+
+bool MtpDevice::writeDataHeader(MtpOperationCode operation, int dataLength) {
+ mData.setOperationCode(operation);
+ mData.setTransactionID(mRequest.getTransactionID());
+ return (!mData.writeDataHeader(mRequestOut, dataLength));
+}
+
+MtpResponseCode MtpDevice::readResponse() {
+ MTPD("readResponse\n");
+ if (mReceivedResponse) {
+ mReceivedResponse = false;
+ return mResponse.getResponseCode();
+ }
+ int ret = mResponse.read(mRequestIn1);
+ // handle zero length packets, which might occur if the data transfer
+ // ends on a packet boundary
+ if (ret == 0)
+ ret = mResponse.read(mRequestIn1);
+ if (ret >= MTP_CONTAINER_HEADER_SIZE) {
+ mResponse.dump();
+ return mResponse.getResponseCode();
+ } else {
+ MTPE("readResponse failed\n");
+ return -1;
+ }
+}
diff --git a/mtp/MtpDevice.h b/mtp/MtpDevice.h
new file mode 100644
index 000000000..d90b0c071
--- /dev/null
+++ b/mtp/MtpDevice.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_DEVICE_H
+#define _MTP_DEVICE_H
+
+#include "MtpRequestPacket.h"
+#include "MtpDataPacket.h"
+#include "MtpResponsePacket.h"
+#include "MtpTypes.h"
+
+#include <utils/threads.h>
+
+struct usb_device;
+struct usb_request;
+struct usb_endpoint_descriptor;
+
+
+class MtpDeviceInfo;
+class MtpObjectInfo;
+class MtpStorageInfo;
+
+class MtpDevice {
+private:
+ struct usb_device* mDevice;
+ int mInterface;
+ struct usb_request* mRequestIn1;
+ struct usb_request* mRequestIn2;
+ struct usb_request* mRequestOut;
+ struct usb_request* mRequestIntr;
+ MtpDeviceInfo* mDeviceInfo;
+ MtpPropertyList mDeviceProperties;
+
+ // current session ID
+ MtpSessionID mSessionID;
+ // current transaction ID
+ MtpTransactionID mTransactionID;
+
+ MtpRequestPacket mRequest;
+ MtpDataPacket mData;
+ MtpResponsePacket mResponse;
+ // set to true if we received a response packet instead of a data packet
+ bool mReceivedResponse;
+
+ // to ensure only one MTP transaction at a time
+ android::Mutex mMutex;
+
+public:
+ MtpDevice(struct usb_device* device, int interface,
+ const struct usb_endpoint_descriptor *ep_in,
+ const struct usb_endpoint_descriptor *ep_out,
+ const struct usb_endpoint_descriptor *ep_intr);
+
+ static MtpDevice* open(const char* deviceName, int fd);
+
+ virtual ~MtpDevice();
+
+ void initialize();
+ void close();
+ void print();
+ const char* getDeviceName();
+
+ bool openSession();
+ bool closeSession();
+
+ MtpDeviceInfo* getDeviceInfo();
+ MtpStorageIDList* getStorageIDs();
+ MtpStorageInfo* getStorageInfo(MtpStorageID storageID);
+ MtpObjectHandleList* getObjectHandles(MtpStorageID storageID, MtpObjectFormat format,
+ MtpObjectHandle parent);
+ MtpObjectInfo* getObjectInfo(MtpObjectHandle handle);
+ void* getThumbnail(MtpObjectHandle handle, int& outLength);
+ MtpObjectHandle sendObjectInfo(MtpObjectInfo* info);
+ bool sendObject(MtpObjectInfo* info, int srcFD);
+ bool deleteObject(MtpObjectHandle handle);
+ MtpObjectHandle getParent(MtpObjectHandle handle);
+ MtpObjectHandle getStorageID(MtpObjectHandle handle);
+
+ MtpObjectPropertyList* getObjectPropsSupported(MtpObjectFormat format);
+
+ MtpProperty* getDevicePropDesc(MtpDeviceProperty code);
+ MtpProperty* getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format);
+
+ bool readObject(MtpObjectHandle handle,
+ bool (* callback)(void* data, int offset,
+ int length, void* clientData),
+ int objectSize, void* clientData);
+ bool readObject(MtpObjectHandle handle, const char* destPath, int group,
+ int perm);
+
+private:
+ bool sendRequest(MtpOperationCode operation);
+ bool sendData();
+ bool readData();
+ bool writeDataHeader(MtpOperationCode operation, int dataLength);
+ MtpResponseCode readResponse();
+
+};
+
+
+#endif // _MTP_DEVICE_H
diff --git a/mtp/MtpDeviceInfo.cpp b/mtp/MtpDeviceInfo.cpp
new file mode 100644
index 000000000..337cc130d
--- /dev/null
+++ b/mtp/MtpDeviceInfo.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include "MtpDebug.h"
+#include "MtpDataPacket.h"
+#include "MtpDeviceInfo.h"
+#include "MtpStringBuffer.h"
+
+MtpDeviceInfo::MtpDeviceInfo()
+ : mStandardVersion(0),
+ mVendorExtensionID(0),
+ mVendorExtensionVersion(0),
+ mVendorExtensionDesc(NULL),
+ mFunctionalCode(0),
+ mOperations(NULL),
+ mEvents(NULL),
+ mDeviceProperties(NULL),
+ mCaptureFormats(NULL),
+ mPlaybackFormats(NULL),
+ mManufacturer(NULL),
+ mModel(NULL),
+ mVersion(NULL),
+ mSerial(NULL)
+{
+}
+
+MtpDeviceInfo::~MtpDeviceInfo() {
+ if (mVendorExtensionDesc)
+ free(mVendorExtensionDesc);
+ delete mOperations;
+ delete mEvents;
+ delete mDeviceProperties;
+ delete mCaptureFormats;
+ delete mPlaybackFormats;
+ if (mManufacturer)
+ free(mManufacturer);
+ if (mModel)
+ free(mModel);
+ if (mVersion)
+ free(mVersion);
+ if (mSerial)
+ free(mSerial);
+}
+
+void MtpDeviceInfo::read(MtpDataPacket& packet) {
+ MtpStringBuffer string;
+
+ // read the device info
+ mStandardVersion = packet.getUInt16();
+ mVendorExtensionID = packet.getUInt32();
+ mVendorExtensionVersion = packet.getUInt16();
+
+ packet.getString(string);
+ mVendorExtensionDesc = strdup((const char *)string);
+
+ mFunctionalCode = packet.getUInt16();
+ mOperations = packet.getAUInt16();
+ mEvents = packet.getAUInt16();
+ mDeviceProperties = packet.getAUInt16();
+ mCaptureFormats = packet.getAUInt16();
+ mPlaybackFormats = packet.getAUInt16();
+
+ packet.getString(string);
+ mManufacturer = strdup((const char *)string);
+ packet.getString(string);
+ mModel = strdup((const char *)string);
+ packet.getString(string);
+ mVersion = strdup((const char *)string);
+ packet.getString(string);
+ mSerial = strdup((const char *)string);
+}
+
+void MtpDeviceInfo::print() {
+ MTPI("Device Info:\n\tmStandardVersion: %d\n\tmVendorExtensionID: %d\n\tmVendorExtensionVersiony: %d\n",
+ mStandardVersion, mVendorExtensionID, mVendorExtensionVersion);
+ MTPI("\tmVendorExtensionDesc: %s\n\tmFunctionalCode: %d\n\tmManufacturer: %s\n\tmModel: %s\n\tmVersion: %s\n\tmSerial: %s\n",
+ mVendorExtensionDesc, mFunctionalCode, mManufacturer, mModel, mVersion, mSerial);
+}
+
diff --git a/mtp/MtpDeviceInfo.h b/mtp/MtpDeviceInfo.h
new file mode 100644
index 000000000..b31637195
--- /dev/null
+++ b/mtp/MtpDeviceInfo.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_DEVICE_INFO_H
+#define _MTP_DEVICE_INFO_H
+
+struct stat;
+
+
+class MtpDataPacket;
+
+class MtpDeviceInfo {
+public:
+ uint16_t mStandardVersion;
+ uint32_t mVendorExtensionID;
+ uint16_t mVendorExtensionVersion;
+ char* mVendorExtensionDesc;
+ uint16_t mFunctionalCode;
+ UInt16List* mOperations;
+ UInt16List* mEvents;
+ MtpDevicePropertyList* mDeviceProperties;
+ MtpObjectFormatList* mCaptureFormats;
+ MtpObjectFormatList* mPlaybackFormats;
+ char* mManufacturer;
+ char* mModel;
+ char* mVersion;
+ char* mSerial;
+
+public:
+ MtpDeviceInfo();
+ virtual ~MtpDeviceInfo();
+
+ void read(MtpDataPacket& packet);
+
+ void print();
+};
+
+
+#endif // _MTP_DEVICE_INFO_H
diff --git a/mtp/MtpEventPacket.cpp b/mtp/MtpEventPacket.cpp
new file mode 100644
index 000000000..1119f7d53
--- /dev/null
+++ b/mtp/MtpEventPacket.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#ifdef MTP_DEVICE
+#include <linux/usb/f_mtp.h>
+#endif
+
+#include "MtpEventPacket.h"
+
+#include <usbhost/usbhost.h>
+
+
+MtpEventPacket::MtpEventPacket()
+ : MtpPacket(512)
+{
+}
+
+MtpEventPacket::~MtpEventPacket() {
+}
+
+#ifdef MTP_DEVICE
+int MtpEventPacket::write(int fd) {
+ struct mtp_event event;
+
+ putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+ putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_EVENT);
+
+ event.data = mBuffer;
+ event.length = mPacketSize;
+ int ret = ::ioctl(fd, MTP_SEND_EVENT, (unsigned long)&event);
+ return (ret < 0 ? ret : 0);
+}
+#endif
+
+#ifdef MTP_HOST
+int MtpEventPacket::read(struct usb_request *request) {
+ request->buffer = mBuffer;
+ request->buffer_length = mBufferSize;
+ int ret = transfer(request);
+ if (ret >= 0)
+ mPacketSize = ret;
+ else
+ mPacketSize = 0;
+ return ret;
+}
+#endif
+
+
diff --git a/mtp/MtpEventPacket.h b/mtp/MtpEventPacket.h
new file mode 100644
index 000000000..b42abceaf
--- /dev/null
+++ b/mtp/MtpEventPacket.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_EVENT_PACKET_H
+#define _MTP_EVENT_PACKET_H
+
+#include "MtpPacket.h"
+#include "mtp.h"
+
+class MtpEventPacket : public MtpPacket {
+
+public:
+ MtpEventPacket();
+ virtual ~MtpEventPacket();
+
+#ifdef MTP_DEVICE
+ // write our data to the given file descriptor
+ int write(int fd);
+#endif
+
+#ifdef MTP_HOST
+ // read our buffer with the given request
+ int read(struct usb_request *request);
+#endif
+
+ inline MtpEventCode getEventCode() const { return getContainerCode(); }
+ inline void setEventCode(MtpEventCode code)
+ { return setContainerCode(code); }
+};
+
+#endif // _MTP_EVENT_PACKET_H
diff --git a/mtp/MtpMessage.hpp b/mtp/MtpMessage.hpp
new file mode 100644
index 000000000..31465d8c6
--- /dev/null
+++ b/mtp/MtpMessage.hpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTPMESSAGE_HPP
+#define _MTPMESSAGE_HPP
+
+#define MTP_MESSAGE_ADD_STORAGE 1
+#define MTP_MESSAGE_REMOVE_STORAGE 2
+
+struct mtpmsg {
+ int message_type; // 1 is add, 2 is remove, see above
+ unsigned int storage_id;
+ char display[1024];
+ char path[1024];
+ uint64_t maxFileSize;
+};
+
+#endif //_MTPMESSAGE_HPP
diff --git a/mtp/MtpObjectInfo.cpp b/mtp/MtpObjectInfo.cpp
new file mode 100644
index 000000000..50192d730
--- /dev/null
+++ b/mtp/MtpObjectInfo.cpp
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include "MtpDebug.h"
+#include "MtpDataPacket.h"
+#include "MtpObjectInfo.h"
+#include "MtpStringBuffer.h"
+#include "MtpUtils.h"
+
+MtpObjectInfo::MtpObjectInfo(MtpObjectHandle handle)
+ : mHandle(handle),
+ mStorageID(0),
+ mFormat(0),
+ mProtectionStatus(0),
+ mCompressedSize(0),
+ mThumbFormat(0),
+ mThumbCompressedSize(0),
+ mThumbPixWidth(0),
+ mThumbPixHeight(0),
+ mImagePixWidth(0),
+ mImagePixHeight(0),
+ mImagePixDepth(0),
+ mParent(0),
+ mAssociationType(0),
+ mAssociationDesc(0),
+ mSequenceNumber(0),
+ mName(NULL),
+ mDateCreated(0),
+ mDateModified(0),
+ mKeywords(NULL)
+{
+}
+
+MtpObjectInfo::~MtpObjectInfo() {
+ if (mName)
+ free(mName);
+ if (mKeywords)
+ free(mKeywords);
+}
+
+void MtpObjectInfo::read(MtpDataPacket& packet) {
+ MtpStringBuffer string;
+ time_t time;
+
+ mStorageID = packet.getUInt32();
+ mFormat = packet.getUInt16();
+ mProtectionStatus = packet.getUInt16();
+ mCompressedSize = packet.getUInt32();
+ mThumbFormat = packet.getUInt16();
+ mThumbCompressedSize = packet.getUInt32();
+ mThumbPixWidth = packet.getUInt32();
+ mThumbPixHeight = packet.getUInt32();
+ mImagePixWidth = packet.getUInt32();
+ mImagePixHeight = packet.getUInt32();
+ mImagePixDepth = packet.getUInt32();
+ mParent = packet.getUInt32();
+ mAssociationType = packet.getUInt16();
+ mAssociationDesc = packet.getUInt32();
+ mSequenceNumber = packet.getUInt32();
+
+ packet.getString(string);
+ mName = strdup((const char *)string);
+
+ packet.getString(string);
+ if (parseDateTime((const char*)string, time))
+ mDateCreated = time;
+
+ packet.getString(string);
+ if (parseDateTime((const char*)string, time))
+ mDateModified = time;
+
+ packet.getString(string);
+ mKeywords = strdup((const char *)string);
+}
+
+void MtpObjectInfo::print() {
+ MTPI("MtpObject Info %08X: %s\n", mHandle, mName);
+ MTPI(" mStorageID: %08X mFormat: %04X mProtectionStatus: %d\n",
+ mStorageID, mFormat, mProtectionStatus);
+ MTPI(" mCompressedSize: %d mThumbFormat: %04X mThumbCompressedSize: %d\n",
+ mCompressedSize, mFormat, mThumbCompressedSize);
+ MTPI(" mThumbPixWidth: %d mThumbPixHeight: %d\n", mThumbPixWidth, mThumbPixHeight);
+ MTPI(" mImagePixWidth: %d mImagePixHeight: %d mImagePixDepth: %d\n",
+ mImagePixWidth, mImagePixHeight, mImagePixDepth);
+ MTPI(" mParent: %08X mAssociationType: %04X mAssociationDesc: %04X\n",
+ mParent, mAssociationType, mAssociationDesc);
+ MTPI(" mSequenceNumber: %d mDateCreated: %ld mDateModified: %ld mKeywords: %s\n",
+ mSequenceNumber, mDateCreated, mDateModified, mKeywords);
+}
+
diff --git a/mtp/MtpObjectInfo.h b/mtp/MtpObjectInfo.h
new file mode 100644
index 000000000..406c3f4ba
--- /dev/null
+++ b/mtp/MtpObjectInfo.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_OBJECT_INFO_H
+#define _MTP_OBJECT_INFO_H
+
+#include "MtpTypes.h"
+
+
+class MtpDataPacket;
+
+class MtpObjectInfo {
+public:
+ MtpObjectHandle mHandle;
+ MtpStorageID mStorageID;
+ MtpObjectFormat mFormat;
+ uint16_t mProtectionStatus;
+ uint32_t mCompressedSize;
+ MtpObjectFormat mThumbFormat;
+ uint32_t mThumbCompressedSize;
+ uint32_t mThumbPixWidth;
+ uint32_t mThumbPixHeight;
+ uint32_t mImagePixWidth;
+ uint32_t mImagePixHeight;
+ uint32_t mImagePixDepth;
+ MtpObjectHandle mParent;
+ uint16_t mAssociationType;
+ uint32_t mAssociationDesc;
+ uint32_t mSequenceNumber;
+ char* mName;
+ time_t mDateCreated;
+ time_t mDateModified;
+ char* mKeywords;
+
+public:
+ MtpObjectInfo(MtpObjectHandle handle);
+ virtual ~MtpObjectInfo();
+
+ void read(MtpDataPacket& packet);
+
+ void print();
+};
+
+
+#endif // _MTP_OBJECT_INFO_H
diff --git a/mtp/MtpPacket.cpp b/mtp/MtpPacket.cpp
new file mode 100644
index 000000000..2f9e438ba
--- /dev/null
+++ b/mtp/MtpPacket.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include "MtpDebug.h"
+#include "MtpPacket.h"
+#include "mtp.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <usbhost/usbhost.h>
+
+
+MtpPacket::MtpPacket(int bufferSize)
+ : mBuffer(NULL),
+ mBufferSize(bufferSize),
+ mAllocationIncrement(bufferSize),
+ mPacketSize(0)
+{
+ mBuffer = (uint8_t *)malloc(bufferSize);
+ if (!mBuffer) {
+ MTPE("out of memory!");
+ abort();
+ }
+}
+
+MtpPacket::~MtpPacket() {
+ if (mBuffer)
+ free(mBuffer);
+}
+
+void MtpPacket::reset() {
+ allocate(MTP_CONTAINER_HEADER_SIZE);
+ mPacketSize = MTP_CONTAINER_HEADER_SIZE;
+ memset(mBuffer, 0, mBufferSize);
+}
+
+void MtpPacket::allocate(int length) {
+ if (length > mBufferSize) {
+ int newLength = length + mAllocationIncrement;
+ mBuffer = (uint8_t *)realloc(mBuffer, newLength);
+ if (!mBuffer) {
+ MTPE("out of memory!");
+ abort();
+ }
+ mBufferSize = newLength;
+ }
+}
+
+void MtpPacket::dump() {
+#define DUMP_BYTES_PER_ROW 16
+ char buffer[500];
+ char* bufptr = buffer;
+
+ for (size_t i = 0; i < mPacketSize; i++) {
+ sprintf(bufptr, "%02X ", mBuffer[i]);
+ bufptr += strlen(bufptr);
+ if (i % DUMP_BYTES_PER_ROW == (DUMP_BYTES_PER_ROW - 1)) {
+ MTPD("%s", buffer);
+ bufptr = buffer;
+ }
+ }
+ if (bufptr != buffer) {
+ // print last line
+ MTPD("%s", buffer);
+ }
+ MTPD("\n");
+}
+
+void MtpPacket::copyFrom(const MtpPacket& src) {
+ int length = src.mPacketSize;
+ allocate(length);
+ mPacketSize = length;
+ memcpy(mBuffer, src.mBuffer, length);
+}
+
+uint16_t MtpPacket::getUInt16(int offset) const {
+ return ((uint16_t)mBuffer[offset + 1] << 8) | (uint16_t)mBuffer[offset];
+}
+
+uint32_t MtpPacket::getUInt32(int offset) const {
+ return ((uint32_t)mBuffer[offset + 3] << 24) | ((uint32_t)mBuffer[offset + 2] << 16) |
+ ((uint32_t)mBuffer[offset + 1] << 8) | (uint32_t)mBuffer[offset];
+}
+
+void MtpPacket::putUInt16(int offset, uint16_t value) {
+ mBuffer[offset++] = (uint8_t)(value & 0xFF);
+ mBuffer[offset++] = (uint8_t)((value >> 8) & 0xFF);
+}
+
+void MtpPacket::putUInt32(int offset, uint32_t value) {
+ mBuffer[offset++] = (uint8_t)(value & 0xFF);
+ mBuffer[offset++] = (uint8_t)((value >> 8) & 0xFF);
+ mBuffer[offset++] = (uint8_t)((value >> 16) & 0xFF);
+ mBuffer[offset++] = (uint8_t)((value >> 24) & 0xFF);
+}
+
+uint16_t MtpPacket::getContainerCode() const {
+ return getUInt16(MTP_CONTAINER_CODE_OFFSET);
+}
+
+void MtpPacket::setContainerCode(uint16_t code) {
+ putUInt16(MTP_CONTAINER_CODE_OFFSET, code);
+}
+
+uint16_t MtpPacket::getContainerType() const {
+ return getUInt16(MTP_CONTAINER_TYPE_OFFSET);
+}
+
+MtpTransactionID MtpPacket::getTransactionID() const {
+ return getUInt32(MTP_CONTAINER_TRANSACTION_ID_OFFSET);
+}
+
+void MtpPacket::setTransactionID(MtpTransactionID id) {
+ putUInt32(MTP_CONTAINER_TRANSACTION_ID_OFFSET, id);
+}
+
+uint32_t MtpPacket::getParameter(int index) const {
+ if (index < 1 || index > 5) {
+ MTPE("index %d out of range in MtpPacket::getParameter", index);
+ return 0;
+ }
+ return getUInt32(MTP_CONTAINER_PARAMETER_OFFSET + (index - 1) * sizeof(uint32_t));
+}
+
+void MtpPacket::setParameter(int index, uint32_t value) {
+ if (index < 1 || index > 5) {
+ MTPE("index %d out of range in MtpPacket::setParameter", index);
+ return;
+ }
+ int offset = MTP_CONTAINER_PARAMETER_OFFSET + (index - 1) * sizeof(uint32_t);
+ if (mPacketSize < offset + sizeof(uint32_t))
+ mPacketSize = offset + sizeof(uint32_t);
+ putUInt32(offset, value);
+}
+
+#ifdef MTP_HOST
+int MtpPacket::transfer(struct usb_request* request) {
+ int result = usb_device_bulk_transfer(request->dev,
+ request->endpoint,
+ request->buffer,
+ request->buffer_length,
+ 0);
+ request->actual_length = result;
+ return result;
+}
+#endif
+
diff --git a/mtp/MtpPacket.h b/mtp/MtpPacket.h
new file mode 100644
index 000000000..ec763c825
--- /dev/null
+++ b/mtp/MtpPacket.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_PACKET_H
+#define _MTP_PACKET_H
+
+#include "MtpTypes.h"
+
+struct usb_request;
+
+
+class MtpPacket {
+
+protected:
+ uint8_t* mBuffer;
+ // current size of the buffer
+ int mBufferSize;
+ // number of bytes to add when resizing the buffer
+ int mAllocationIncrement;
+ // size of the data in the packet
+ unsigned mPacketSize;
+
+public:
+ MtpPacket(int bufferSize);
+ virtual ~MtpPacket();
+
+ // sets packet size to the default container size and sets buffer to zero
+ virtual void reset();
+
+ void allocate(int length);
+ void dump();
+ void copyFrom(const MtpPacket& src);
+
+ uint16_t getContainerCode() const;
+ void setContainerCode(uint16_t code);
+
+ uint16_t getContainerType() const;
+
+ MtpTransactionID getTransactionID() const;
+ void setTransactionID(MtpTransactionID id);
+
+ uint32_t getParameter(int index) const;
+ void setParameter(int index, uint32_t value);
+
+#ifdef MTP_HOST
+ int transfer(struct usb_request* request);
+#endif
+
+protected:
+ uint16_t getUInt16(int offset) const;
+ uint32_t getUInt32(int offset) const;
+ void putUInt16(int offset, uint16_t value);
+ void putUInt32(int offset, uint32_t value);
+};
+
+
+#endif // _MTP_PACKET_H
diff --git a/mtp/MtpProperty.cpp b/mtp/MtpProperty.cpp
new file mode 100644
index 000000000..e105f2497
--- /dev/null
+++ b/mtp/MtpProperty.cpp
@@ -0,0 +1,531 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include "MtpDataPacket.h"
+#include "MtpDebug.h"
+#include "MtpProperty.h"
+#include "MtpStringBuffer.h"
+#include "MtpUtils.h"
+
+MtpProperty::MtpProperty()
+ : mCode(0),
+ mType(0),
+ mWriteable(false),
+ mDefaultArrayLength(0),
+ mDefaultArrayValues(NULL),
+ mCurrentArrayLength(0),
+ mCurrentArrayValues(NULL),
+ mGroupCode(0),
+ mFormFlag(kFormNone),
+ mEnumLength(0),
+ mEnumValues(NULL)
+{
+ memset(&mDefaultValue, 0, sizeof(mDefaultValue));
+ memset(&mCurrentValue, 0, sizeof(mCurrentValue));
+ memset(&mMinimumValue, 0, sizeof(mMinimumValue));
+ memset(&mMaximumValue, 0, sizeof(mMaximumValue));
+}
+
+MtpProperty::MtpProperty(MtpPropertyCode propCode,
+ MtpDataType type,
+ bool writeable,
+ int defaultValue)
+ : mCode(propCode),
+ mType(type),
+ mWriteable(writeable),
+ mDefaultArrayLength(0),
+ mDefaultArrayValues(NULL),
+ mCurrentArrayLength(0),
+ mCurrentArrayValues(NULL),
+ mGroupCode(0),
+ mFormFlag(kFormNone),
+ mEnumLength(0),
+ mEnumValues(NULL)
+{
+ memset(&mDefaultValue, 0, sizeof(mDefaultValue));
+ memset(&mCurrentValue, 0, sizeof(mCurrentValue));
+ memset(&mMinimumValue, 0, sizeof(mMinimumValue));
+ memset(&mMaximumValue, 0, sizeof(mMaximumValue));
+
+ if (defaultValue) {
+ switch (type) {
+ case MTP_TYPE_INT8:
+ mDefaultValue.u.i8 = defaultValue;
+ break;
+ case MTP_TYPE_UINT8:
+ mDefaultValue.u.u8 = defaultValue;
+ break;
+ case MTP_TYPE_INT16:
+ mDefaultValue.u.i16 = defaultValue;
+ break;
+ case MTP_TYPE_UINT16:
+ mDefaultValue.u.u16 = defaultValue;
+ break;
+ case MTP_TYPE_INT32:
+ mDefaultValue.u.i32 = defaultValue;
+ break;
+ case MTP_TYPE_UINT32:
+ mDefaultValue.u.u32 = defaultValue;
+ break;
+ case MTP_TYPE_INT64:
+ mDefaultValue.u.i64 = defaultValue;
+ break;
+ case MTP_TYPE_UINT64:
+ mDefaultValue.u.u64 = defaultValue;
+ break;
+ default:
+ MTPE("unknown type %04X in MtpProperty::MtpProperty", type);
+ }
+ }
+}
+
+MtpProperty::~MtpProperty() {
+ if (mType == MTP_TYPE_STR) {
+ // free all strings
+ free(mDefaultValue.str);
+ free(mCurrentValue.str);
+ free(mMinimumValue.str);
+ free(mMaximumValue.str);
+ if (mDefaultArrayValues) {
+ for (int i = 0; i < mDefaultArrayLength; i++)
+ free(mDefaultArrayValues[i].str);
+ }
+ if (mCurrentArrayValues) {
+ for (int i = 0; i < mCurrentArrayLength; i++)
+ free(mCurrentArrayValues[i].str);
+ }
+ if (mEnumValues) {
+ for (int i = 0; i < mEnumLength; i++)
+ free(mEnumValues[i].str);
+ }
+ }
+ delete[] mDefaultArrayValues;
+ delete[] mCurrentArrayValues;
+ delete[] mEnumValues;
+}
+
+void MtpProperty::read(MtpDataPacket& packet) {
+ mCode = packet.getUInt16();
+ bool deviceProp = isDeviceProperty();
+ mType = packet.getUInt16();
+ mWriteable = (packet.getUInt8() == 1);
+ switch (mType) {
+ case MTP_TYPE_AINT8:
+ case MTP_TYPE_AUINT8:
+ case MTP_TYPE_AINT16:
+ case MTP_TYPE_AUINT16:
+ case MTP_TYPE_AINT32:
+ case MTP_TYPE_AUINT32:
+ case MTP_TYPE_AINT64:
+ case MTP_TYPE_AUINT64:
+ case MTP_TYPE_AINT128:
+ case MTP_TYPE_AUINT128:
+ mDefaultArrayValues = readArrayValues(packet, mDefaultArrayLength);
+ if (deviceProp)
+ mCurrentArrayValues = readArrayValues(packet, mCurrentArrayLength);
+ break;
+ default:
+ readValue(packet, mDefaultValue);
+ if (deviceProp)
+ readValue(packet, mCurrentValue);
+ }
+ if (!deviceProp)
+ mGroupCode = packet.getUInt32();
+ mFormFlag = packet.getUInt8();
+
+ if (mFormFlag == kFormRange) {
+ readValue(packet, mMinimumValue);
+ readValue(packet, mMaximumValue);
+ readValue(packet, mStepSize);
+ } else if (mFormFlag == kFormEnum) {
+ mEnumLength = packet.getUInt16();
+ mEnumValues = new MtpPropertyValue[mEnumLength];
+ for (int i = 0; i < mEnumLength; i++)
+ readValue(packet, mEnumValues[i]);
+ }
+}
+
+void MtpProperty::write(MtpDataPacket& packet) {
+ bool deviceProp = isDeviceProperty();
+
+ packet.putUInt16(mCode);
+ packet.putUInt16(mType);
+ packet.putUInt8(mWriteable ? 1 : 0);
+
+ switch (mType) {
+ case MTP_TYPE_AINT8:
+ case MTP_TYPE_AUINT8:
+ case MTP_TYPE_AINT16:
+ case MTP_TYPE_AUINT16:
+ case MTP_TYPE_AINT32:
+ case MTP_TYPE_AUINT32:
+ case MTP_TYPE_AINT64:
+ case MTP_TYPE_AUINT64:
+ case MTP_TYPE_AINT128:
+ case MTP_TYPE_AUINT128:
+ writeArrayValues(packet, mDefaultArrayValues, mDefaultArrayLength);
+ if (deviceProp)
+ writeArrayValues(packet, mCurrentArrayValues, mCurrentArrayLength);
+ break;
+ default:
+ writeValue(packet, mDefaultValue);
+ if (deviceProp)
+ writeValue(packet, mCurrentValue);
+ }
+ packet.putUInt32(mGroupCode);
+ if (!deviceProp)
+ packet.putUInt8(mFormFlag);
+ if (mFormFlag == kFormRange) {
+ writeValue(packet, mMinimumValue);
+ writeValue(packet, mMaximumValue);
+ writeValue(packet, mStepSize);
+ } else if (mFormFlag == kFormEnum) {
+ packet.putUInt16(mEnumLength);
+ for (int i = 0; i < mEnumLength; i++)
+ writeValue(packet, mEnumValues[i]);
+ }
+}
+
+void MtpProperty::setDefaultValue(const uint16_t* string) {
+ free(mDefaultValue.str);
+ if (string) {
+ MtpStringBuffer buffer(string);
+ mDefaultValue.str = strdup(buffer);
+ }
+ else
+ mDefaultValue.str = NULL;
+}
+
+void MtpProperty::setCurrentValue(const uint16_t* string) {
+ free(mCurrentValue.str);
+ if (string) {
+ MtpStringBuffer buffer(string);
+ mCurrentValue.str = strdup(buffer);
+ }
+ else
+ mCurrentValue.str = NULL;
+}
+
+void MtpProperty::setFormRange(int min, int max, int step) {
+ mFormFlag = kFormRange;
+ switch (mType) {
+ case MTP_TYPE_INT8:
+ mMinimumValue.u.i8 = min;
+ mMaximumValue.u.i8 = max;
+ mStepSize.u.i8 = step;
+ break;
+ case MTP_TYPE_UINT8:
+ mMinimumValue.u.u8 = min;
+ mMaximumValue.u.u8 = max;
+ mStepSize.u.u8 = step;
+ break;
+ case MTP_TYPE_INT16:
+ mMinimumValue.u.i16 = min;
+ mMaximumValue.u.i16 = max;
+ mStepSize.u.i16 = step;
+ break;
+ case MTP_TYPE_UINT16:
+ mMinimumValue.u.u16 = min;
+ mMaximumValue.u.u16 = max;
+ mStepSize.u.u16 = step;
+ break;
+ case MTP_TYPE_INT32:
+ mMinimumValue.u.i32 = min;
+ mMaximumValue.u.i32 = max;
+ mStepSize.u.i32 = step;
+ break;
+ case MTP_TYPE_UINT32:
+ mMinimumValue.u.u32 = min;
+ mMaximumValue.u.u32 = max;
+ mStepSize.u.u32 = step;
+ break;
+ case MTP_TYPE_INT64:
+ mMinimumValue.u.i64 = min;
+ mMaximumValue.u.i64 = max;
+ mStepSize.u.i64 = step;
+ break;
+ case MTP_TYPE_UINT64:
+ mMinimumValue.u.u64 = min;
+ mMaximumValue.u.u64 = max;
+ mStepSize.u.u64 = step;
+ break;
+ default:
+ MTPE("unsupported type for MtpProperty::setRange");
+ break;
+ }
+}
+
+void MtpProperty::setFormEnum(const int* values, int count) {
+ mFormFlag = kFormEnum;
+ delete[] mEnumValues;
+ mEnumValues = new MtpPropertyValue[count];
+ mEnumLength = count;
+
+ for (int i = 0; i < count; i++) {
+ int value = *values++;
+ switch (mType) {
+ case MTP_TYPE_INT8:
+ mEnumValues[i].u.i8 = value;
+ break;
+ case MTP_TYPE_UINT8:
+ mEnumValues[i].u.u8 = value;
+ break;
+ case MTP_TYPE_INT16:
+ mEnumValues[i].u.i16 = value;
+ break;
+ case MTP_TYPE_UINT16:
+ mEnumValues[i].u.u16 = value;
+ break;
+ case MTP_TYPE_INT32:
+ mEnumValues[i].u.i32 = value;
+ break;
+ case MTP_TYPE_UINT32:
+ mEnumValues[i].u.u32 = value;
+ break;
+ case MTP_TYPE_INT64:
+ mEnumValues[i].u.i64 = value;
+ break;
+ case MTP_TYPE_UINT64:
+ mEnumValues[i].u.u64 = value;
+ break;
+ default:
+ MTPE("unsupported type for MtpProperty::setEnum");
+ break;
+ }
+ }
+}
+
+void MtpProperty::setFormDateTime() {
+ mFormFlag = kFormDateTime;
+}
+
+void MtpProperty::print() {
+ MtpString buffer;
+ bool deviceProp = isDeviceProperty();
+ if (deviceProp)
+ MTPI(" %s (%04X)", MtpDebug::getDevicePropCodeName(mCode), mCode);
+ else
+ MTPI(" %s (%04X)", MtpDebug::getObjectPropCodeName(mCode), mCode);
+ MTPI(" type %04X", mType);
+ MTPI(" writeable %s", (mWriteable ? "true" : "false"));
+ buffer = " default value: ";
+ print(mDefaultValue, buffer);
+ MTPI("%s", (const char *)buffer);
+ if (deviceProp) {
+ buffer = " current value: ";
+ print(mCurrentValue, buffer);
+ MTPI("%s", (const char *)buffer);
+ }
+ switch (mFormFlag) {
+ case kFormNone:
+ break;
+ case kFormRange:
+ buffer = " Range (";
+ print(mMinimumValue, buffer);
+ buffer += ", ";
+ print(mMaximumValue, buffer);
+ buffer += ", ";
+ print(mStepSize, buffer);
+ buffer += ")";
+ MTPI("%s", (const char *)buffer);
+ break;
+ case kFormEnum:
+ buffer = " Enum { ";
+ for (int i = 0; i < mEnumLength; i++) {
+ print(mEnumValues[i], buffer);
+ buffer += " ";
+ }
+ buffer += "}";
+ MTPI("%s", (const char *)buffer);
+ break;
+ case kFormDateTime:
+ MTPI(" DateTime\n");
+ break;
+ default:
+ MTPI(" form %d\n", mFormFlag);
+ break;
+ }
+}
+
+void MtpProperty::print(MtpPropertyValue& value, MtpString& buffer) {
+ switch (mType) {
+ case MTP_TYPE_INT8:
+ buffer.appendFormat("%d", value.u.i8);
+ break;
+ case MTP_TYPE_UINT8:
+ buffer.appendFormat("%d", value.u.u8);
+ break;
+ case MTP_TYPE_INT16:
+ buffer.appendFormat("%d", value.u.i16);
+ break;
+ case MTP_TYPE_UINT16:
+ buffer.appendFormat("%d", value.u.u16);
+ break;
+ case MTP_TYPE_INT32:
+ buffer.appendFormat("%d", value.u.i32);
+ break;
+ case MTP_TYPE_UINT32:
+ buffer.appendFormat("%d", value.u.u32);
+ break;
+ case MTP_TYPE_INT64:
+ buffer.appendFormat("%lld", value.u.i64);
+ break;
+ case MTP_TYPE_UINT64:
+ buffer.appendFormat("%lld", value.u.u64);
+ break;
+ case MTP_TYPE_INT128:
+ buffer.appendFormat("%08X%08X%08X%08X", value.u.i128[0], value.u.i128[1],
+ value.u.i128[2], value.u.i128[3]);
+ break;
+ case MTP_TYPE_UINT128:
+ buffer.appendFormat("%08X%08X%08X%08X", value.u.u128[0], value.u.u128[1],
+ value.u.u128[2], value.u.u128[3]);
+ break;
+ case MTP_TYPE_STR:
+ buffer.appendFormat("%s", value.str);
+ break;
+ default:
+ MTPE("unsupported type for MtpProperty::print\n");
+ break;
+ }
+}
+
+void MtpProperty::readValue(MtpDataPacket& packet, MtpPropertyValue& value) {
+ MtpStringBuffer stringBuffer;
+
+ switch (mType) {
+ case MTP_TYPE_INT8:
+ case MTP_TYPE_AINT8:
+ value.u.i8 = packet.getInt8();
+ break;
+ case MTP_TYPE_UINT8:
+ case MTP_TYPE_AUINT8:
+ value.u.u8 = packet.getUInt8();
+ break;
+ case MTP_TYPE_INT16:
+ case MTP_TYPE_AINT16:
+ value.u.i16 = packet.getInt16();
+ break;
+ case MTP_TYPE_UINT16:
+ case MTP_TYPE_AUINT16:
+ value.u.u16 = packet.getUInt16();
+ break;
+ case MTP_TYPE_INT32:
+ case MTP_TYPE_AINT32:
+ value.u.i32 = packet.getInt32();
+ break;
+ case MTP_TYPE_UINT32:
+ case MTP_TYPE_AUINT32:
+ value.u.u32 = packet.getUInt32();
+ break;
+ case MTP_TYPE_INT64:
+ case MTP_TYPE_AINT64:
+ value.u.i64 = packet.getInt64();
+ break;
+ case MTP_TYPE_UINT64:
+ case MTP_TYPE_AUINT64:
+ value.u.u64 = packet.getUInt64();
+ break;
+ case MTP_TYPE_INT128:
+ case MTP_TYPE_AINT128:
+ packet.getInt128(value.u.i128);
+ break;
+ case MTP_TYPE_UINT128:
+ case MTP_TYPE_AUINT128:
+ packet.getUInt128(value.u.u128);
+ break;
+ case MTP_TYPE_STR:
+ packet.getString(stringBuffer);
+ value.str = strdup(stringBuffer);
+ break;
+ default:
+ MTPE("unknown type %04X in MtpProperty::readValue", mType);
+ }
+}
+
+void MtpProperty::writeValue(MtpDataPacket& packet, MtpPropertyValue& value) {
+ MtpStringBuffer stringBuffer;
+
+ switch (mType) {
+ case MTP_TYPE_INT8:
+ case MTP_TYPE_AINT8:
+ packet.putInt8(value.u.i8);
+ break;
+ case MTP_TYPE_UINT8:
+ case MTP_TYPE_AUINT8:
+ packet.putUInt8(value.u.u8);
+ break;
+ case MTP_TYPE_INT16:
+ case MTP_TYPE_AINT16:
+ packet.putInt16(value.u.i16);
+ break;
+ case MTP_TYPE_UINT16:
+ case MTP_TYPE_AUINT16:
+ packet.putUInt16(value.u.u16);
+ break;
+ case MTP_TYPE_INT32:
+ case MTP_TYPE_AINT32:
+ packet.putInt32(value.u.i32);
+ break;
+ case MTP_TYPE_UINT32:
+ case MTP_TYPE_AUINT32:
+ packet.putUInt32(value.u.u32);
+ break;
+ case MTP_TYPE_INT64:
+ case MTP_TYPE_AINT64:
+ packet.putInt64(value.u.i64);
+ break;
+ case MTP_TYPE_UINT64:
+ case MTP_TYPE_AUINT64:
+ packet.putUInt64(value.u.u64);
+ break;
+ case MTP_TYPE_INT128:
+ case MTP_TYPE_AINT128:
+ packet.putInt128(value.u.i128);
+ break;
+ case MTP_TYPE_UINT128:
+ case MTP_TYPE_AUINT128:
+ packet.putUInt128(value.u.u128);
+ break;
+ case MTP_TYPE_STR:
+ if (value.str)
+ packet.putString(value.str);
+ else
+ packet.putEmptyString();
+ break;
+ default:
+ MTPE("unknown type %04X in MtpProperty::writeValue", mType);
+ }
+}
+
+MtpPropertyValue* MtpProperty::readArrayValues(MtpDataPacket& packet, int& length) {
+ length = packet.getUInt32();
+ if (length == 0)
+ return NULL;
+ MtpPropertyValue* result = new MtpPropertyValue[length];
+ for (int i = 0; i < length; i++)
+ readValue(packet, result[i]);
+ return result;
+}
+
+void MtpProperty::writeArrayValues(MtpDataPacket& packet, MtpPropertyValue* values, int length) {
+ packet.putUInt32(length);
+ for (int i = 0; i < length; i++)
+ writeValue(packet, values[i]);
+}
+
diff --git a/mtp/MtpProperty.h b/mtp/MtpProperty.h
new file mode 100644
index 000000000..017d87567
--- /dev/null
+++ b/mtp/MtpProperty.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_PROPERTY_H
+#define _MTP_PROPERTY_H
+
+#include "MtpTypes.h"
+
+
+class MtpDataPacket;
+
+struct MtpPropertyValue {
+ union {
+ int8_t i8;
+ uint8_t u8;
+ int16_t i16;
+ uint16_t u16;
+ int32_t i32;
+ uint32_t u32;
+ int64_t i64;
+ uint64_t u64;
+ int128_t i128;
+ uint128_t u128;
+ } u;
+ // string in UTF8 format
+ char* str;
+};
+
+class MtpProperty {
+public:
+ MtpPropertyCode mCode;
+ MtpDataType mType;
+ bool mWriteable;
+ MtpPropertyValue mDefaultValue;
+ MtpPropertyValue mCurrentValue;
+
+ // for array types
+ int mDefaultArrayLength;
+ MtpPropertyValue* mDefaultArrayValues;
+ int mCurrentArrayLength;
+ MtpPropertyValue* mCurrentArrayValues;
+
+ enum {
+ kFormNone = 0,
+ kFormRange = 1,
+ kFormEnum = 2,
+ kFormDateTime = 3,
+ };
+
+ uint32_t mGroupCode;
+ uint8_t mFormFlag;
+
+ // for range form
+ MtpPropertyValue mMinimumValue;
+ MtpPropertyValue mMaximumValue;
+ MtpPropertyValue mStepSize;
+
+ // for enum form
+ int mEnumLength;
+ MtpPropertyValue* mEnumValues;
+
+public:
+ MtpProperty();
+ MtpProperty(MtpPropertyCode propCode,
+ MtpDataType type,
+ bool writeable = false,
+ int defaultValue = 0);
+ virtual ~MtpProperty();
+
+ inline MtpPropertyCode getPropertyCode() const { return mCode; }
+
+ void read(MtpDataPacket& packet);
+ void write(MtpDataPacket& packet);
+
+ void setDefaultValue(const uint16_t* string);
+ void setCurrentValue(const uint16_t* string);
+
+ void setFormRange(int min, int max, int step);
+ void setFormEnum(const int* values, int count);
+ void setFormDateTime();
+
+ void print();
+ void print(MtpPropertyValue& value, MtpString& buffer);
+
+ inline bool isDeviceProperty() const {
+ return ( ((mCode & 0xF000) == 0x5000)
+ || ((mCode & 0xF800) == 0xD000));
+ }
+
+private:
+ void readValue(MtpDataPacket& packet, MtpPropertyValue& value);
+ void writeValue(MtpDataPacket& packet, MtpPropertyValue& value);
+ MtpPropertyValue* readArrayValues(MtpDataPacket& packet, int& length);
+ void writeArrayValues(MtpDataPacket& packet,
+ MtpPropertyValue* values, int length);
+};
+
+
+#endif // _MTP_PROPERTY_H
diff --git a/mtp/MtpRequestPacket.cpp b/mtp/MtpRequestPacket.cpp
new file mode 100644
index 000000000..754e205aa
--- /dev/null
+++ b/mtp/MtpRequestPacket.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "MtpRequestPacket.h"
+#include "MtpDebug.h"
+
+#include <usbhost/usbhost.h>
+
+
+MtpRequestPacket::MtpRequestPacket()
+ : MtpPacket(512)
+{
+}
+
+MtpRequestPacket::~MtpRequestPacket() {
+}
+
+#ifdef MTP_DEVICE
+int MtpRequestPacket::read(int fd) {
+ MTPD("block1 fd: %d\n", fd);
+ int ret = ::read(fd, mBuffer, mBufferSize);
+ MTPD("block2\n");
+ if (ret >= 0)
+ mPacketSize = ret;
+ else
+ mPacketSize = 0;
+ return ret;
+}
+#endif
+
+#ifdef MTP_HOST
+ // write our buffer to the given endpoint (host mode)
+int MtpRequestPacket::write(struct usb_request *request)
+{
+ putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+ putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_COMMAND);
+ request->buffer = mBuffer;
+ request->buffer_length = mPacketSize;
+ return transfer(request);
+}
+#endif
+
diff --git a/mtp/MtpRequestPacket.h b/mtp/MtpRequestPacket.h
new file mode 100644
index 000000000..8551dded0
--- /dev/null
+++ b/mtp/MtpRequestPacket.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_REQUEST_PACKET_H
+#define _MTP_REQUEST_PACKET_H
+
+#include "MtpPacket.h"
+#include "mtp.h"
+
+struct usb_request;
+
+
+class MtpRequestPacket : public MtpPacket {
+
+public:
+ MtpRequestPacket();
+ virtual ~MtpRequestPacket();
+#ifdef MTP_DEVICE
+ // fill our buffer with data from the given file descriptor
+ int read(int fd);
+#endif
+
+#ifdef MTP_HOST
+ // write our buffer to the given endpoint
+ int write(struct usb_request *request);
+#endif
+
+ inline MtpOperationCode getOperationCode() const { return getContainerCode(); }
+ inline void setOperationCode(MtpOperationCode code)
+ { return setContainerCode(code); }
+};
+
+
+#endif // _MTP_REQUEST_PACKET_H
diff --git a/mtp/MtpResponsePacket.cpp b/mtp/MtpResponsePacket.cpp
new file mode 100644
index 000000000..8eed13a14
--- /dev/null
+++ b/mtp/MtpResponsePacket.cpp
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "MtpResponsePacket.h"
+
+#include <usbhost/usbhost.h>
+
+
+MtpResponsePacket::MtpResponsePacket()
+ : MtpPacket(512)
+{
+}
+
+MtpResponsePacket::~MtpResponsePacket() {
+}
+
+#ifdef MTP_DEVICE
+int MtpResponsePacket::write(int fd) {
+ putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+ putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_RESPONSE);
+ int ret = ::write(fd, mBuffer, mPacketSize);
+ return (ret < 0 ? ret : 0);
+}
+#endif
+
+#ifdef MTP_HOST
+int MtpResponsePacket::read(struct usb_request *request) {
+ request->buffer = mBuffer;
+ request->buffer_length = mBufferSize;
+ int ret = transfer(request);
+ if (ret >= 0)
+ mPacketSize = ret;
+ else
+ mPacketSize = 0;
+ return ret;
+}
+#endif
+
+
diff --git a/mtp/MtpResponsePacket.h b/mtp/MtpResponsePacket.h
new file mode 100644
index 000000000..749b5344a
--- /dev/null
+++ b/mtp/MtpResponsePacket.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_RESPONSE_PACKET_H
+#define _MTP_RESPONSE_PACKET_H
+
+#include "MtpPacket.h"
+#include "mtp.h"
+
+
+class MtpResponsePacket : public MtpPacket {
+
+public:
+ MtpResponsePacket();
+ virtual ~MtpResponsePacket();
+
+#ifdef MTP_DEVICE
+ // write our data to the given file descriptor
+ int write(int fd);
+#endif
+
+#ifdef MTP_HOST
+ // read our buffer with the given request
+ int read(struct usb_request *request);
+#endif
+
+ inline MtpResponseCode getResponseCode() const { return getContainerCode(); }
+ inline void setResponseCode(MtpResponseCode code)
+ { return setContainerCode(code); }
+};
+
+
+#endif // _MTP_RESPONSE_PACKET_H
diff --git a/mtp/MtpServer.cpp b/mtp/MtpServer.cpp
new file mode 100644
index 000000000..11eca86bc
--- /dev/null
+++ b/mtp/MtpServer.cpp
@@ -0,0 +1,1379 @@
+/*
+ * Copyright (C) 2017 TeamWin
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include "../twcommon.h"
+#include "../set_metadata.h"
+#include <cutils/properties.h>
+
+#include "MtpTypes.h"
+#include "MtpDebug.h"
+#include "MtpDatabase.h"
+#include "MtpObjectInfo.h"
+#include "MtpProperty.h"
+#include "MtpServer.h"
+#include "MtpStorage.h"
+#include "MtpStringBuffer.h"
+
+#include <linux/usb/f_mtp.h>
+
+static const MtpOperationCode kSupportedOperationCodes[] = {
+ MTP_OPERATION_GET_DEVICE_INFO,
+ MTP_OPERATION_OPEN_SESSION,
+ MTP_OPERATION_CLOSE_SESSION,
+ MTP_OPERATION_GET_STORAGE_IDS,
+ MTP_OPERATION_GET_STORAGE_INFO,
+ MTP_OPERATION_GET_NUM_OBJECTS,
+ MTP_OPERATION_GET_OBJECT_HANDLES,
+ MTP_OPERATION_GET_OBJECT_INFO,
+ MTP_OPERATION_GET_OBJECT,
+ MTP_OPERATION_GET_THUMB,
+ MTP_OPERATION_DELETE_OBJECT,
+ MTP_OPERATION_SEND_OBJECT_INFO,
+ MTP_OPERATION_SEND_OBJECT,
+// MTP_OPERATION_INITIATE_CAPTURE,
+// MTP_OPERATION_FORMAT_STORE,
+// MTP_OPERATION_RESET_DEVICE,
+// MTP_OPERATION_SELF_TEST,
+// MTP_OPERATION_SET_OBJECT_PROTECTION,
+// MTP_OPERATION_POWER_DOWN,
+ MTP_OPERATION_GET_DEVICE_PROP_DESC,
+ MTP_OPERATION_GET_DEVICE_PROP_VALUE,
+ MTP_OPERATION_SET_DEVICE_PROP_VALUE,
+ MTP_OPERATION_RESET_DEVICE_PROP_VALUE,
+// MTP_OPERATION_TERMINATE_OPEN_CAPTURE,
+// MTP_OPERATION_MOVE_OBJECT,
+// MTP_OPERATION_COPY_OBJECT,
+ MTP_OPERATION_GET_PARTIAL_OBJECT,
+// MTP_OPERATION_INITIATE_OPEN_CAPTURE,
+ MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED,
+ MTP_OPERATION_GET_OBJECT_PROP_DESC,
+ MTP_OPERATION_GET_OBJECT_PROP_VALUE,
+ MTP_OPERATION_SET_OBJECT_PROP_VALUE,
+ MTP_OPERATION_GET_OBJECT_PROP_LIST,
+// MTP_OPERATION_SET_OBJECT_PROP_LIST,
+// MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC,
+// MTP_OPERATION_SEND_OBJECT_PROP_LIST,
+ MTP_OPERATION_GET_OBJECT_REFERENCES,
+ MTP_OPERATION_SET_OBJECT_REFERENCES,
+// MTP_OPERATION_SKIP,
+ // Android extension for direct file IO
+ MTP_OPERATION_GET_PARTIAL_OBJECT_64,
+ MTP_OPERATION_SEND_PARTIAL_OBJECT,
+ MTP_OPERATION_TRUNCATE_OBJECT,
+ MTP_OPERATION_BEGIN_EDIT_OBJECT,
+ MTP_OPERATION_END_EDIT_OBJECT,
+};
+
+static const MtpEventCode kSupportedEventCodes[] = {
+ MTP_EVENT_OBJECT_ADDED,
+ MTP_EVENT_OBJECT_REMOVED,
+ MTP_EVENT_STORE_ADDED,
+ MTP_EVENT_STORE_REMOVED,
+ MTP_EVENT_OBJECT_PROP_CHANGED,
+};
+
+MtpServer::MtpServer(MtpDatabase* database, bool ptp,
+ int fileGroup, int filePerm, int directoryPerm)
+ : mDatabase(database),
+ mPtp(ptp),
+ mFileGroup(fileGroup),
+ mFilePermission(filePerm),
+ mDirectoryPermission(directoryPerm),
+ mSessionID(0),
+ mSessionOpen(false),
+ mSendObjectHandle(kInvalidObjectHandle),
+ mSendObjectFormat(0),
+ mSendObjectFileSize(0)
+{
+ mFD = -1;
+}
+
+MtpServer::~MtpServer() {
+}
+
+void MtpServer::addStorage(MtpStorage* storage) {
+ android::Mutex::Autolock autoLock(mMutex);
+ MTPD("addStorage(): storage: %x\n", storage);
+ if (getStorage(storage->getStorageID()) != NULL) {
+ MTPE("MtpServer::addStorage Storage for storage ID %i already exists.\n", storage->getStorageID());
+ return;
+ }
+ mDatabase->createDB(storage, storage->getStorageID());
+ mStorages.push(storage);
+ sendStoreAdded(storage->getStorageID());
+}
+
+void MtpServer::removeStorage(MtpStorage* storage) {
+ android::Mutex::Autolock autoLock(mMutex);
+
+ for (size_t i = 0; i < mStorages.size(); i++) {
+ if (mStorages[i] == storage) {
+ MTPD("MtpServer::removeStorage calling sendStoreRemoved\n");
+ // First lock the mutex so that the inotify thread and main
+ // thread do not do anything while we remove the storage
+ // item, and to make sure we don't remove the item while an
+ // operation is in progress
+ mDatabase->lockMutex();
+ // Grab the storage ID before we delete the item from the
+ // database
+ MtpStorageID storageID = storage->getStorageID();
+ // Remove the item from the mStorages from the vector. At
+ // this point the main thread will no longer be able to find
+ // this storage item anymore.
+ mStorages.removeAt(i);
+ // Destroy the storage item, free up all the memory, kill
+ // the inotify thread.
+ mDatabase->destroyDB(storageID);
+ // Tell the host OS that the storage item is gone.
+ sendStoreRemoved(storageID);
+ // Unlock any remaining mutexes on other storage devices.
+ // If no storage devices exist anymore this will do nothing.
+ mDatabase->unlockMutex();
+ break;
+ }
+ }
+ MTPD("MtpServer::removeStorage DONE\n");
+}
+
+MtpStorage* MtpServer::getStorage(MtpStorageID id) {
+ MTPD("getStorage\n");
+ if (id == 0) {
+ MTPD("mStorages\n");
+ return mStorages[0];
+ }
+ for (size_t i = 0; i < mStorages.size(); i++) {
+ MtpStorage* storage = mStorages[i];
+ MTPD("id: %d\n", id);
+ MTPD("storage: %d\n", storage->getStorageID());
+ if (storage->getStorageID() == id) {
+ return storage;
+ }
+ }
+ return NULL;
+}
+
+bool MtpServer::hasStorage(MtpStorageID id) {
+ MTPD("in hasStorage\n");
+ if (id == 0 || id == 0xFFFFFFFF)
+ return mStorages.size() > 0;
+ return (getStorage(id) != NULL);
+}
+
+void MtpServer::run(int fd) {
+ if (fd < 0)
+ return;
+
+ mFD = fd;
+ MTPI("MtpServer::run fd: %d\n", fd);
+
+ while (1) {
+ MTPD("About to read device...\n");
+ int ret = mRequest.read(fd);
+ if (ret < 0) {
+ if (errno == ECANCELED) {
+ // return to top of loop and wait for next command
+ MTPD("request read returned %d ECANCELED, starting over\n", ret);
+ continue;
+ }
+ MTPE("request read returned %d, errno: %d, exiting MtpServer::run loop\n", ret, errno);
+ break;
+ }
+ MtpOperationCode operation = mRequest.getOperationCode();
+ MtpTransactionID transaction = mRequest.getTransactionID();
+
+ MTPD("operation: %s", MtpDebug::getOperationCodeName(operation));
+ mRequest.dump();
+
+ // FIXME need to generalize this
+ bool dataIn = (operation == MTP_OPERATION_SEND_OBJECT_INFO
+ || operation == MTP_OPERATION_SET_OBJECT_REFERENCES
+ || operation == MTP_OPERATION_SET_OBJECT_PROP_VALUE
+ || operation == MTP_OPERATION_SET_DEVICE_PROP_VALUE);
+ if (dataIn) {
+ int ret = mData.read(fd);
+ if (ret < 0) {
+ if (errno == ECANCELED) {
+ // return to top of loop and wait for next command
+ MTPD("data read returned %d ECANCELED, starting over\n", ret);
+ continue;
+ }
+ MTPD("data read returned %d, errno: %d, exiting MtpServer::run loop\n", ret, errno);
+ break;
+ }
+ MTPD("received data:");
+ mData.dump();
+ } else {
+ mData.reset();
+ }
+
+ if (handleRequest()) {
+ if (!dataIn && mData.hasData()) {
+ mData.setOperationCode(operation);
+ mData.setTransactionID(transaction);
+ MTPD("sending data:");
+ mData.dump();
+ ret = mData.write(fd);
+ if (ret < 0) {
+ if (errno == ECANCELED) {
+ // return to top of loop and wait for next command
+ MTPD("data write returned %d ECANCELED, starting over\n", ret);
+ continue;
+ }
+ MTPE("data write returned %d, errno: %d, exiting MtpServer::run loop\n", ret, errno);
+ break;
+ }
+ }
+
+ mResponse.setTransactionID(transaction);
+ MTPD("sending response %04X\n", mResponse.getResponseCode());
+ ret = mResponse.write(fd);
+ MTPD("ret: %d\n", ret);
+ mResponse.dump();
+ if (ret < 0) {
+ if (errno == ECANCELED) {
+ // return to top of loop and wait for next command
+ MTPD("response write returned %d ECANCELED, starting over\n", ret);
+ continue;
+ }
+ MTPE("response write returned %d, errno: %d, exiting MtpServer::run loop\n", ret, errno);
+ break;
+ }
+ } else {
+ MTPD("skipping response\n");
+ }
+ }
+
+ // commit any open edits
+ int count = mObjectEditList.size();
+ for (int i = 0; i < count; i++) {
+ ObjectEdit* edit = mObjectEditList[i];
+ commitEdit(edit);
+ delete edit;
+ }
+ mObjectEditList.clear();
+
+ if (mSessionOpen)
+ mDatabase->sessionEnded(); // This doesn't actually do anything but was carry over from AOSP
+ close(fd);
+ mFD = -1;
+}
+
+void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
+ MTPD("sendObjectAdded %d\n", handle);
+ sendEvent(MTP_EVENT_OBJECT_ADDED, handle);
+}
+
+void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
+ MTPD("sendObjectRemoved %d\n", handle);
+ sendEvent(MTP_EVENT_OBJECT_REMOVED, handle);
+}
+
+void MtpServer::sendObjectUpdated(MtpObjectHandle handle) {
+ MTPD("sendObjectUpdated %d\n", handle);
+ sendEvent(MTP_EVENT_OBJECT_PROP_CHANGED, handle);
+}
+
+void MtpServer::sendStoreAdded(MtpStorageID id) {
+ MTPD("sendStoreAdded %08X\n", id);
+ sendEvent(MTP_EVENT_STORE_ADDED, id);
+}
+
+void MtpServer::sendStoreRemoved(MtpStorageID id) {
+ MTPD("sendStoreRemoved %08X\n", id);
+ sendEvent(MTP_EVENT_STORE_REMOVED, id);
+ MTPD("MtpServer::sendStoreRemoved done\n");
+}
+
+void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) {
+ MTPD("MtpServer::sendEvent sending event code: %x\n", code);
+ if (mSessionOpen) {
+ mEvent.setEventCode(code);
+ mEvent.setTransactionID(mRequest.getTransactionID());
+ mEvent.setParameter(1, param1);
+ int ret = mEvent.write(mFD);
+ MTPD("mEvent.write returned %d\n", ret);
+ }
+}
+
+void MtpServer::addEditObject(MtpObjectHandle handle, MtpString& path,
+ uint64_t size, MtpObjectFormat format, int fd) {
+ ObjectEdit* edit = new ObjectEdit(handle, path, size, format, fd);
+ mObjectEditList.add(edit);
+}
+
+MtpServer::ObjectEdit* MtpServer::getEditObject(MtpObjectHandle handle) {
+ int count = mObjectEditList.size();
+ for (int i = 0; i < count; i++) {
+ ObjectEdit* edit = mObjectEditList[i];
+ if (edit->mHandle == handle) return edit;
+ }
+ return NULL;
+}
+
+void MtpServer::removeEditObject(MtpObjectHandle handle) {
+ int count = mObjectEditList.size();
+ for (int i = 0; i < count; i++) {
+ ObjectEdit* edit = mObjectEditList[i];
+ if (edit->mHandle == handle) {
+ delete edit;
+ mObjectEditList.removeAt(i);
+ return;
+ }
+ }
+ MTPE("ObjectEdit not found in removeEditObject");
+}
+
+void MtpServer::commitEdit(ObjectEdit* edit) {
+ mDatabase->endSendObject((const char *)edit->mPath, edit->mHandle, edit->mFormat, true);
+}
+
+
+bool MtpServer::handleRequest() {
+ android::Mutex::Autolock autoLock(mMutex);
+
+ MtpOperationCode operation = mRequest.getOperationCode();
+ MtpResponseCode response;
+
+ mResponse.reset();
+
+ if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
+ // FIXME - need to delete mSendObjectHandle from the database
+ MTPE("expected SendObject after SendObjectInfo");
+ mSendObjectHandle = kInvalidObjectHandle;
+ }
+
+ switch (operation) {
+ case MTP_OPERATION_GET_DEVICE_INFO:
+ MTPD("doGetDeviceInfo()\n");
+ response = doGetDeviceInfo();
+ break;
+ case MTP_OPERATION_OPEN_SESSION:
+ MTPD("doOpenSesion()\n");
+ response = doOpenSession();
+ break;
+ case MTP_OPERATION_CLOSE_SESSION:
+ MTPD("doCloseSession()\n");
+ response = doCloseSession();
+ break;
+ case MTP_OPERATION_GET_STORAGE_IDS:
+ MTPD("doGetStorageIDs()\n");
+ response = doGetStorageIDs();
+ break;
+ case MTP_OPERATION_GET_STORAGE_INFO:
+ MTPD("about to call doGetStorageInfo()\n");
+ response = doGetStorageInfo();
+ break;
+ case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
+ MTPD("about to call doGetObjectPropsSupported()\n");
+ response = doGetObjectPropsSupported();
+ break;
+ case MTP_OPERATION_GET_OBJECT_HANDLES:
+ MTPD("about to call doGetObjectHandles()\n");
+ response = doGetObjectHandles();
+ break;
+ case MTP_OPERATION_GET_NUM_OBJECTS:
+ MTPD("about to call doGetNumbObjects()\n");
+ response = doGetNumObjects();
+ break;
+ case MTP_OPERATION_GET_OBJECT_REFERENCES:
+ MTPD("about to call doGetObjectReferences()\n");
+ response = doGetObjectReferences();
+ break;
+ case MTP_OPERATION_SET_OBJECT_REFERENCES:
+ MTPD("about to call doSetObjectReferences()\n");
+ response = doSetObjectReferences();
+ break;
+ case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
+ MTPD("about to call doGetObjectPropValue()\n");
+ response = doGetObjectPropValue();
+ break;
+ case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
+ MTPD("about to call doSetObjectPropValue()\n");
+ response = doSetObjectPropValue();
+ break;
+ case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
+ MTPD("about to call doGetDevicPropValue()\n");
+ response = doGetDevicePropValue();
+ break;
+ case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
+ MTPD("about to call doSetDevicePropVaue()\n");
+ response = doSetDevicePropValue();
+ break;
+ case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
+ MTPD("about to call doResetDevicePropValue()\n");
+ response = doResetDevicePropValue();
+ break;
+ case MTP_OPERATION_GET_OBJECT_PROP_LIST:
+ MTPD("calling doGetObjectPropList()\n");
+ response = doGetObjectPropList();
+ break;
+ case MTP_OPERATION_GET_OBJECT_INFO:
+ MTPD("calling doGetObjectInfo()\n");
+ response = doGetObjectInfo();
+ break;
+ case MTP_OPERATION_GET_OBJECT:
+ MTPD("about to call doGetObject()\n");
+ response = doGetObject();
+ break;
+ case MTP_OPERATION_GET_THUMB:
+ response = doGetThumb();
+ break;
+ case MTP_OPERATION_GET_PARTIAL_OBJECT:
+ case MTP_OPERATION_GET_PARTIAL_OBJECT_64:
+ response = doGetPartialObject(operation);
+ break;
+ case MTP_OPERATION_SEND_OBJECT_INFO:
+ MTPD("about to call doSendObjectInfo()\n");
+ response = doSendObjectInfo();
+ break;
+ case MTP_OPERATION_SEND_OBJECT:
+ MTPD("about to call doSendObject()\n");
+ response = doSendObject();
+ break;
+ case MTP_OPERATION_DELETE_OBJECT:
+ response = doDeleteObject();
+ break;
+ case MTP_OPERATION_GET_OBJECT_PROP_DESC:
+ MTPD("about to call doGetObjectPropDesc()\n");
+ response = doGetObjectPropDesc();
+ break;
+ case MTP_OPERATION_GET_DEVICE_PROP_DESC:
+ MTPD("about to call doGetDevicePropDesc()\n");
+ response = doGetDevicePropDesc();
+ break;
+ case MTP_OPERATION_SEND_PARTIAL_OBJECT:
+ response = doSendPartialObject();
+ break;
+ case MTP_OPERATION_TRUNCATE_OBJECT:
+ response = doTruncateObject();
+ break;
+ case MTP_OPERATION_BEGIN_EDIT_OBJECT:
+ response = doBeginEditObject();
+ break;
+ case MTP_OPERATION_END_EDIT_OBJECT:
+ response = doEndEditObject();
+ break;
+ default:
+ MTPE("got unsupported command %s", MtpDebug::getOperationCodeName(operation));
+ response = MTP_RESPONSE_OPERATION_NOT_SUPPORTED;
+ break;
+ }
+
+ if (response == MTP_RESPONSE_TRANSACTION_CANCELLED)
+ return false;
+ mResponse.setResponseCode(response);
+ return true;
+}
+
+MtpResponseCode MtpServer::doGetDeviceInfo() {
+ MtpStringBuffer string;
+ char prop_value[PROPERTY_VALUE_MAX];
+
+ MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
+ MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
+ MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
+
+ // fill in device info
+ mData.putUInt16(MTP_STANDARD_VERSION);
+ if (mPtp) {
+ MTPD("doGetDeviceInfo putting 0\n");
+ mData.putUInt32(0);
+ } else {
+ // MTP Vendor Extension ID
+ MTPD("doGetDeviceInfo putting 6\n");
+ mData.putUInt32(6);
+ }
+ mData.putUInt16(MTP_STANDARD_VERSION);
+ if (mPtp) {
+ // no extensions
+ MTPD("doGetDeviceInfo no extensions\n");
+ string.set("");
+ } else {
+ // MTP extensions
+ MTPD("doGetDeviceInfo microsoft.com: 1.0; android.com: 1.0;\n");
+ string.set("microsoft.com: 1.0; android.com: 1.0;");
+ }
+ mData.putString(string); // MTP Extensions
+ mData.putUInt16(0); //Functional Mode
+ MTPD("doGetDeviceInfo opcodes, %i\n", sizeof(kSupportedOperationCodes) / sizeof(uint16_t));
+ MTPD("doGetDeviceInfo eventcodes, %i\n", sizeof(kSupportedEventCodes) / sizeof(uint16_t));
+ mData.putAUInt16(kSupportedOperationCodes,
+ sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
+ mData.putAUInt16(kSupportedEventCodes,
+ sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
+ mData.putAUInt16(deviceProperties); // Device Properties Supported
+ mData.putAUInt16(captureFormats); // Capture Formats
+ mData.putAUInt16(playbackFormats); // Playback Formats
+
+ property_get("ro.product.manufacturer", prop_value, "unknown manufacturer");
+ MTPD("prop: %s\n", prop_value);
+ string.set(prop_value);
+ mData.putString(string); // Manufacturer
+
+ property_get("ro.product.model", prop_value, "MTP Device");
+ string.set(prop_value);
+ mData.putString(string); // Model
+ string.set("1.0");
+ mData.putString(string); // Device Version
+
+ property_get("ro.serialno", prop_value, "????????");
+ MTPD("sn: %s\n", prop_value);
+ string.set(prop_value);
+ mData.putString(string); // Serial Number
+
+ delete playbackFormats;
+ delete captureFormats;
+ delete deviceProperties;
+
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doOpenSession() {
+ if (mSessionOpen) {
+ mResponse.setParameter(1, mSessionID);
+ return MTP_RESPONSE_SESSION_ALREADY_OPEN;
+ }
+ mSessionID = mRequest.getParameter(1);
+ mSessionOpen = true;
+
+ mDatabase->sessionStarted();
+
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doCloseSession() {
+ if (!mSessionOpen)
+ return MTP_RESPONSE_SESSION_NOT_OPEN;
+ mSessionID = 0;
+ mSessionOpen = false;
+ mDatabase->sessionEnded();
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetStorageIDs() {
+ MTPD("doGetStorageIDs()\n");
+ if (!mSessionOpen)
+ return MTP_RESPONSE_SESSION_NOT_OPEN;
+ int count = mStorages.size();
+ mData.putUInt32(count);
+ for (int i = 0; i < count; i++) {
+ MTPD("getting storageid %d\n", mStorages[i]->getStorageID());
+ mData.putUInt32(mStorages[i]->getStorageID());
+ }
+
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetStorageInfo() {
+ MtpStringBuffer string;
+ MTPD("doGetStorageInfo()\n");
+ if (!mSessionOpen)
+ return MTP_RESPONSE_SESSION_NOT_OPEN;
+ MtpStorageID id = mRequest.getParameter(1);
+ MtpStorage* storage = getStorage(id);
+ if (!storage) {
+ MTPE("invalid storage id\n");
+ return MTP_RESPONSE_INVALID_STORAGE_ID;
+ }
+
+ mData.putUInt16(storage->getType());
+ mData.putUInt16(storage->getFileSystemType());
+ mData.putUInt16(storage->getAccessCapability());
+ mData.putUInt64(storage->getMaxCapacity());
+ mData.putUInt64(storage->getFreeSpace());
+ mData.putUInt32(1024*1024*1024); // Free Space in Objects
+ string.set(storage->getDescription());
+ mData.putString(string);
+ mData.putEmptyString(); // Volume Identifier
+
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetObjectPropsSupported() {
+ MTPD("doGetObjectPropsSupported()\n");
+ if (!mSessionOpen)
+ return MTP_RESPONSE_SESSION_NOT_OPEN;
+ MtpObjectFormat format = mRequest.getParameter(1);
+ mDatabase->lockMutex();
+ MtpObjectPropertyList* properties = mDatabase->getSupportedObjectProperties(format);
+ mData.putAUInt16(properties);
+ delete properties;
+ mDatabase->unlockMutex();
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetObjectHandles() {
+ if (!mSessionOpen)
+ return MTP_RESPONSE_SESSION_NOT_OPEN;
+ MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
+ MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
+ MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
+ // 0x00000000 for all objects
+
+ if (!hasStorage(storageID))
+ return MTP_RESPONSE_INVALID_STORAGE_ID;
+
+ MTPD("calling MtpDatabase->getObjectList()\n");
+ mDatabase->lockMutex();
+ MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
+ mData.putAUInt32(handles);
+ delete handles;
+ mDatabase->unlockMutex();
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetNumObjects() {
+ if (!mSessionOpen)
+ return MTP_RESPONSE_SESSION_NOT_OPEN;
+ MtpStorageID storageID = mRequest.getParameter(1); // 0xFFFFFFFF for all storage
+ MtpObjectFormat format = mRequest.getParameter(2); // 0 for all formats
+ MtpObjectHandle parent = mRequest.getParameter(3); // 0xFFFFFFFF for objects with no parent
+ // 0x00000000 for all objects
+ if (!hasStorage(storageID))
+ return MTP_RESPONSE_INVALID_STORAGE_ID;
+
+ mDatabase->lockMutex();
+ int count = mDatabase->getNumObjects(storageID, format, parent);
+ mDatabase->unlockMutex();
+ if (count >= 0) {
+ mResponse.setParameter(1, count);
+ return MTP_RESPONSE_OK;
+ } else {
+ mResponse.setParameter(1, 0);
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ }
+}
+
+MtpResponseCode MtpServer::doGetObjectReferences() {
+ if (!mSessionOpen)
+ return MTP_RESPONSE_SESSION_NOT_OPEN;
+ if (!hasStorage())
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ MtpObjectHandle handle = mRequest.getParameter(1);
+
+ // FIXME - check for invalid object handle
+ mDatabase->lockMutex();
+ MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
+ if (handles) {
+ mData.putAUInt32(handles);
+ delete handles;
+ } else {
+ MTPD("MtpServer::doGetObjectReferences putEmptyArray\n");
+ mData.putEmptyArray();
+ }
+ mDatabase->unlockMutex();
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doSetObjectReferences() {
+ if (!mSessionOpen)
+ return MTP_RESPONSE_SESSION_NOT_OPEN;
+ if (!hasStorage())
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ MtpStorageID handle = mRequest.getParameter(1);
+
+ MtpObjectHandleList* references = mData.getAUInt32();
+ mDatabase->lockMutex();
+ MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
+ mDatabase->unlockMutex();
+ delete references;
+ return result;
+}
+
+MtpResponseCode MtpServer::doGetObjectPropValue() {
+ if (!hasStorage())
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ MtpObjectHandle handle = mRequest.getParameter(1);
+ MtpObjectProperty property = mRequest.getParameter(2);
+ MTPD("GetObjectPropValue %d %s\n", handle,
+ MtpDebug::getObjectPropCodeName(property));
+
+ mDatabase->lockMutex();
+ MtpResponseCode res = mDatabase->getObjectPropertyValue(handle, property, mData);
+ mDatabase->unlockMutex();
+ return res;
+}
+
+MtpResponseCode MtpServer::doSetObjectPropValue() {
+ if (!hasStorage())
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ MtpObjectHandle handle = mRequest.getParameter(1);
+ MtpObjectProperty property = mRequest.getParameter(2);
+ MTPD("SetObjectPropValue %d %s\n", handle,
+ MtpDebug::getObjectPropCodeName(property));
+
+ mDatabase->lockMutex();
+ MtpResponseCode res = mDatabase->setObjectPropertyValue(handle, property, mData);
+ mDatabase->unlockMutex();
+ return res;
+}
+
+MtpResponseCode MtpServer::doGetDevicePropValue() {
+ MtpDeviceProperty property = mRequest.getParameter(1);
+ MTPD("GetDevicePropValue %s\n",
+ MtpDebug::getDevicePropCodeName(property));
+
+ mDatabase->lockMutex();
+ MtpResponseCode res = mDatabase->getDevicePropertyValue(property, mData);
+ mDatabase->unlockMutex();
+ return res;
+}
+
+MtpResponseCode MtpServer::doSetDevicePropValue() {
+ MtpDeviceProperty property = mRequest.getParameter(1);
+ MTPD("SetDevicePropValue %s\n",
+ MtpDebug::getDevicePropCodeName(property));
+
+ mDatabase->lockMutex();
+ MtpResponseCode res = mDatabase->setDevicePropertyValue(property, mData);
+ mDatabase->unlockMutex();
+ return res;
+}
+
+MtpResponseCode MtpServer::doResetDevicePropValue() {
+ MtpDeviceProperty property = mRequest.getParameter(1);
+ MTPD("ResetDevicePropValue %s\n",
+ MtpDebug::getDevicePropCodeName(property));
+
+ mDatabase->lockMutex();
+ MtpResponseCode res = mDatabase->resetDeviceProperty(property);
+ mDatabase->unlockMutex();
+ return res;
+}
+
+MtpResponseCode MtpServer::doGetObjectPropList() {
+ if (!hasStorage())
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+
+ MtpObjectHandle handle = mRequest.getParameter(1);
+ // use uint32_t so we can support 0xFFFFFFFF
+ uint32_t format = mRequest.getParameter(2);
+ uint32_t property = mRequest.getParameter(3);
+ int groupCode = mRequest.getParameter(4);
+ int depth = mRequest.getParameter(5);
+ MTPD("GetObjectPropList %d format: %s property: %x group: %d depth: %d\n",
+ handle, MtpDebug::getFormatCodeName(format),
+ property, groupCode, depth);
+
+ mDatabase->lockMutex();
+ MtpResponseCode res = mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData);
+ mDatabase->unlockMutex();
+ return res;
+}
+
+MtpResponseCode MtpServer::doGetObjectInfo() {
+ MTPD("inside doGetObjectInfo()\n");
+ if (!hasStorage())
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ MtpObjectHandle handle = mRequest.getParameter(1);
+ MtpObjectInfo info(handle);
+ MTPD("calling mtpdatabase getObjectInfo()\n");
+ mDatabase->lockMutex();
+ MtpResponseCode result = mDatabase->getObjectInfo(handle, info);
+ mDatabase->unlockMutex();
+ if (result == MTP_RESPONSE_OK) {
+ char date[20];
+
+ mData.putUInt32(info.mStorageID);
+ mData.putUInt16(info.mFormat);
+ mData.putUInt16(info.mProtectionStatus);
+
+ // if object is being edited the database size may be out of date
+ uint32_t size = info.mCompressedSize;
+ ObjectEdit* edit = getEditObject(handle);
+ if (edit)
+ size = (edit->mSize > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)edit->mSize);
+ mData.putUInt32(size);
+
+ mData.putUInt16(info.mThumbFormat);
+ mData.putUInt32(info.mThumbCompressedSize);
+ mData.putUInt32(info.mThumbPixWidth);
+ mData.putUInt32(info.mThumbPixHeight);
+ mData.putUInt32(info.mImagePixWidth);
+ mData.putUInt32(info.mImagePixHeight);
+ mData.putUInt32(info.mImagePixDepth);
+ mData.putUInt32(info.mParent);
+ mData.putUInt16(info.mAssociationType);
+ mData.putUInt32(info.mAssociationDesc);
+ mData.putUInt32(info.mSequenceNumber);
+ MTPD("info.mName: %s\n", info.mName);
+ mData.putString(info.mName);
+ mData.putEmptyString(); // date created
+ formatDateTime(info.mDateModified, date, sizeof(date));
+ mData.putString(date); // date modified
+ mData.putEmptyString(); // keywords
+ }
+ return result;
+}
+
+MtpResponseCode MtpServer::doGetObject() {
+ if (!hasStorage())
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ MtpObjectHandle handle = mRequest.getParameter(1);
+ MtpString pathBuf;
+ int64_t fileLength;
+ MtpObjectFormat format;
+ MTPD("MtpServer::doGetObject calling getObjectFilePath\n");
+ mDatabase->lockMutex();
+ int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
+ mDatabase->unlockMutex();
+ if (result != MTP_RESPONSE_OK)
+ return result;
+
+ const char* filePath = (const char *)pathBuf;
+ MTPD("filePath: %s\n", filePath);
+ mtp_file_range mfr;
+ mfr.fd = open(filePath, O_RDONLY);
+ if (mfr.fd < 0) {
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+ mfr.offset = 0;
+ mfr.length = fileLength;
+ MTPD("mfr.length: %lld\n", mfr.length);
+ mfr.command = mRequest.getOperationCode();
+ mfr.transaction_id = mRequest.getTransactionID();
+
+ // then transfer the file
+ int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
+ MTPD("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
+ 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::doGetThumb() {
+ MtpObjectHandle handle = mRequest.getParameter(1);
+ size_t thumbSize;
+ mDatabase->lockMutex();
+ void* thumb = mDatabase->getThumbnail(handle, thumbSize);
+ mDatabase->unlockMutex();
+ if (thumb) {
+ // send data
+ mData.setOperationCode(mRequest.getOperationCode());
+ mData.setTransactionID(mRequest.getTransactionID());
+ mData.writeData(mFD, thumb, thumbSize);
+ free(thumb);
+ return MTP_RESPONSE_OK;
+ } else {
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+}
+
+MtpResponseCode MtpServer::doGetPartialObject(MtpOperationCode operation) {
+ if (!hasStorage())
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ MtpObjectHandle handle = mRequest.getParameter(1);
+ uint64_t offset;
+ uint32_t length;
+ offset = mRequest.getParameter(2);
+ if (operation == MTP_OPERATION_GET_PARTIAL_OBJECT_64) {
+ // android extension with 64 bit offset
+ uint64_t offset2 = mRequest.getParameter(3);
+ offset = offset | (offset2 << 32);
+ length = mRequest.getParameter(4);
+ } else {
+ // standard GetPartialObject
+ length = mRequest.getParameter(3);
+ }
+ MtpString pathBuf;
+ int64_t fileLength;
+ MtpObjectFormat format;
+ MTPD("MtpServer::doGetPartialObject calling getObjectFilePath\n");
+ mDatabase->lockMutex();
+ int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
+ mDatabase->unlockMutex();
+ if (result != MTP_RESPONSE_OK) {
+ return result;
+ }
+ if (offset + length > (uint64_t)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;
+ mfr.command = mRequest.getOperationCode();
+ mfr.transaction_id = mRequest.getTransactionID();
+ mResponse.setParameter(1, length);
+
+ // transfer the file
+ int ret = ioctl(mFD, MTP_SEND_FILE_WITH_HEADER, (unsigned long)&mfr);
+ MTPD("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
+ 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() {
+ MTPD("MtpServer::doSendObjectInfo starting\n");
+ MtpString path;
+ MtpStorageID storageID = mRequest.getParameter(1);
+ MtpStorage* storage = getStorage(storageID);
+ MtpObjectHandle parent = mRequest.getParameter(2);
+ if (!storage)
+ return MTP_RESPONSE_INVALID_STORAGE_ID;
+
+ // special case the root
+ if (parent == MTP_PARENT_ROOT) {
+ MTPD("MtpServer::doSendObjectInfo special case root\n");
+ path = storage->getPath();
+ parent = 0;
+ } else {
+ int64_t length;
+ MtpObjectFormat format;
+ MTPD("MtpServer::doSendObjectInfo calling getObjectFilePath\n");
+ mDatabase->lockMutex();
+ int result = mDatabase->getObjectFilePath(parent, path, length, format);
+ mDatabase->unlockMutex();
+ if (result != MTP_RESPONSE_OK) {
+ return result;
+ }
+ if (format != MTP_FORMAT_ASSOCIATION)
+ return MTP_RESPONSE_INVALID_PARENT_OBJECT;
+ }
+
+ // read only the fields we need
+ mData.getUInt32(); // storage ID
+ MtpObjectFormat format = mData.getUInt16();
+ mData.getUInt16(); // protection status
+ mSendObjectFileSize = mData.getUInt32();
+ mData.getUInt16(); // thumb format
+ mData.getUInt32(); // thumb compressed size
+ mData.getUInt32(); // thumb pix width
+ mData.getUInt32(); // thumb pix height
+ mData.getUInt32(); // image pix width
+ mData.getUInt32(); // image pix height
+ mData.getUInt32(); // image bit depth
+ mData.getUInt32(); // parent
+ uint16_t associationType = mData.getUInt16();
+ uint32_t associationDesc = mData.getUInt32(); // association desc
+ mData.getUInt32(); // sequence number
+ MtpStringBuffer name, created, modified;
+ mData.getString(name); // file name
+ mData.getString(created); // date created
+ mData.getString(modified); // date modified
+ // keywords follow
+
+ MTPD("name: %s format: %04X\n", (const char *)name, format);
+ time_t modifiedTime;
+ if (!parseDateTime(modified, modifiedTime)) {
+ modifiedTime = 0;
+ }
+ if (path[path.size() - 1] != '/') {
+ path += "/";
+ }
+ path += (const char *)name;
+
+ // check space first
+ if (mSendObjectFileSize > storage->getFreeSpace())
+ return MTP_RESPONSE_STORAGE_FULL;
+ uint64_t maxFileSize = storage->getMaxFileSize();
+ // check storage max file size
+ MTPD("maxFileSize: %ld\n", maxFileSize);
+ if (maxFileSize != 0) {
+ // if mSendObjectFileSize is 0xFFFFFFFF, then all we know is the file size
+ // is >= 0xFFFFFFFF
+ if (mSendObjectFileSize > maxFileSize || mSendObjectFileSize == 0xFFFFFFFF)
+ return MTP_RESPONSE_OBJECT_TOO_LARGE;
+ }
+
+ MTPD("MtpServer::doSendObjectInfo path: %s parent: %d storageID: %08X\n", (const char*)path, parent, storageID);
+ mDatabase->lockMutex();
+ MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
+ format, parent, storageID, mSendObjectFileSize, modifiedTime);
+ mDatabase->unlockMutex();
+ if (handle == kInvalidObjectHandle) {
+ MTPE("MtpServer::doSendObjectInfo returning MTP_RESPONSE_GENERAL_ERROR, handle == kInvalidObjectHandle\n");
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+
+ if (format == MTP_FORMAT_ASSOCIATION) {
+ mode_t mask = umask(0);
+ MTPD("MtpServer::doSendObjectInfo mkdir '%s'\n", (const char *)path);
+ int ret = mkdir((const char *)path, mDirectoryPermission);
+ umask(mask);
+ if (ret && ret != -EEXIST) {
+ MTPE("MtpServer::doSendObjectInfo returning MTP_RESPONSE_GENERAL_ERROR, ret && ret != -EEXIST\n");
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+ chown((const char *)path, getuid(), mFileGroup);
+ tw_set_default_metadata((const char *)path);
+
+ // SendObject does not get sent for directories, so call endSendObject here instead
+ mDatabase->lockMutex();
+ mDatabase->endSendObject(path, handle, MTP_FORMAT_ASSOCIATION, MTP_RESPONSE_OK);
+ mDatabase->unlockMutex();
+ } else {
+ mSendObjectFilePath = path;
+ // save the handle for the SendObject call, which should follow
+ mSendObjectHandle = handle;
+ mSendObjectFormat = format;
+ }
+
+ mResponse.setParameter(1, storageID);
+ mResponse.setParameter(2, parent);
+ mResponse.setParameter(3, handle);
+ MTPD("MtpServer::doSendObjectInfo returning MTP_RESPONSE_OK\n");
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doSendObject() {
+ if (!hasStorage())
+ return MTP_RESPONSE_GENERAL_ERROR;
+ MtpResponseCode result = MTP_RESPONSE_OK;
+ mode_t mask;
+ int ret = 0, initialData;
+
+ if (mSendObjectHandle == kInvalidObjectHandle) {
+ MTPE("Expected SendObjectInfo before SendObject");
+ result = MTP_RESPONSE_NO_VALID_OBJECT_INFO;
+ goto done;
+ }
+
+ // read the header, and possibly some data
+ ret = mData.read(mFD);
+ if (ret < MTP_CONTAINER_HEADER_SIZE) {
+ MTPE("MTP_RESPONSE_GENERAL_ERROR\n");
+ result = MTP_RESPONSE_GENERAL_ERROR;
+ goto done;
+ }
+ initialData = ret - MTP_CONTAINER_HEADER_SIZE;
+
+ mtp_file_range mfr;
+ mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC, 0640);
+ if (mfr.fd < 0) {
+ result = MTP_RESPONSE_GENERAL_ERROR;
+ MTPE("fd error\n");
+ goto done;
+ }
+ fchown(mfr.fd, getuid(), mFileGroup);
+ // set permissions
+ mask = umask(0);
+ fchmod(mfr.fd, mFilePermission);
+ umask(mask);
+
+ if (initialData > 0)
+ ret = write(mfr.fd, mData.getData(), initialData);
+
+ if (mSendObjectFileSize - initialData > 0) {
+ mfr.offset = initialData;
+ if (mSendObjectFileSize == 0xFFFFFFFF) {
+ // tell driver to read until it receives a short packet
+ mfr.length = 0xFFFFFFFF;
+ } else {
+ mfr.length = mSendObjectFileSize - initialData;
+ }
+
+ MTPD("receiving %s\n", (const char *)mSendObjectFilePath);
+ // transfer the file
+ ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
+ }
+ close(mfr.fd);
+ tw_set_default_metadata((const char *)mSendObjectFilePath);
+
+ if (ret < 0) {
+ unlink(mSendObjectFilePath);
+ if (errno == ECANCELED)
+ result = MTP_RESPONSE_TRANSACTION_CANCELLED;
+ else {
+ MTPD("errno: %d\n", errno);
+ result = MTP_RESPONSE_GENERAL_ERROR;
+ }
+ }
+
+done:
+ // reset so we don't attempt to send the data back
+ MTPD("MTP_RECEIVE_FILE returned %d\n", ret);
+ mData.reset();
+ mDatabase->lockMutex();
+ mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat,
+ result == MTP_RESPONSE_OK);
+ mDatabase->unlockMutex();
+ mSendObjectHandle = kInvalidObjectHandle;
+ MTPD("result: %d\n", result);
+ mSendObjectFormat = 0;
+ return result;
+}
+
+static void deleteRecursive(const char* path) {
+ char pathbuf[PATH_MAX];
+ size_t pathLength = strlen(path);
+ if (pathLength >= sizeof(pathbuf) - 1) {
+ MTPE("path too long: %s\n", path);
+ }
+ strcpy(pathbuf, path);
+ if (pathbuf[pathLength - 1] != '/') {
+ pathbuf[pathLength++] = '/';
+ }
+ char* fileSpot = pathbuf + pathLength;
+ int pathRemaining = sizeof(pathbuf) - pathLength - 1;
+
+ DIR* dir = opendir(path);
+ if (!dir) {
+ MTPE("opendir %s failed: %s", path, strerror(errno));
+ return;
+ }
+
+ struct dirent* entry;
+ while ((entry = readdir(dir))) {
+ const char* name = entry->d_name;
+
+ // ignore "." and ".."
+ if (name[0] == '.' && (name[1] == 0 || (name[1] == '.' && name[2] == 0))) {
+ continue;
+ }
+
+ int nameLength = strlen(name);
+ if (nameLength > pathRemaining) {
+ MTPE("path %s/%s too long\n", path, name);
+ continue;
+ }
+ strcpy(fileSpot, name);
+
+ int type = entry->d_type;
+ struct stat st;
+ if (lstat(pathbuf, &st)) {
+ MTPE("Failed to lstat '%s'\n", pathbuf);
+ continue;
+ }
+ if (st.st_mode & S_IFDIR) {
+ deleteRecursive(pathbuf);
+ rmdir(pathbuf);
+ } else {
+ unlink(pathbuf);
+ }
+ }
+ closedir(dir);
+}
+
+static void deletePath(const char* path) {
+ struct stat statbuf;
+ if (stat(path, &statbuf) == 0) {
+ if (S_ISDIR(statbuf.st_mode)) {
+ deleteRecursive(path);
+ rmdir(path);
+ } else {
+ unlink(path);
+ }
+ } else {
+ MTPE("deletePath stat failed for %s: %s", path, strerror(errno));
+ }
+}
+
+MtpResponseCode MtpServer::doDeleteObject() {
+ if (!hasStorage())
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ MtpObjectHandle handle = mRequest.getParameter(1);
+ MtpObjectFormat format = mRequest.getParameter(2);
+ // FIXME - support deleting all objects if handle is 0xFFFFFFFF
+ // FIXME - implement deleting objects by format
+
+ MtpString filePath;
+ int64_t fileLength;
+ MTPD("MtpServer::doDeleteObject calling getObjectFilePath\n");
+ mDatabase->lockMutex();
+ int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format);
+ if (result == MTP_RESPONSE_OK) {
+ MTPD("deleting %s", (const char *)filePath);
+ result = mDatabase->deleteFile(handle);
+ // Don't delete the actual files unless the database deletion is allowed
+ if (result == MTP_RESPONSE_OK) {
+ deletePath((const char *)filePath);
+ }
+ }
+ mDatabase->unlockMutex();
+ return result;
+}
+
+MtpResponseCode MtpServer::doGetObjectPropDesc() {
+ MtpObjectProperty propCode = mRequest.getParameter(1);
+ MtpObjectFormat format = mRequest.getParameter(2);
+ MTPD("MtpServer::doGetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
+ MtpDebug::getFormatCodeName(format));
+ mDatabase->lockMutex();
+ MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
+ mDatabase->unlockMutex();
+ if (!property) {
+ MTPE("MtpServer::doGetObjectPropDesc propery not supported\n");
+ return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+ }
+ property->write(mData);
+ delete property;
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetDevicePropDesc() {
+ MtpDeviceProperty propCode = mRequest.getParameter(1);
+ MTPD("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
+ mDatabase->lockMutex();
+ MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
+ mDatabase->unlockMutex();
+ if (!property) {
+ MTPE("MtpServer::doGetDevicePropDesc property not supported\n");
+ return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
+ }
+ property->write(mData);
+ delete property;
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doSendPartialObject() {
+ if (!hasStorage())
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ MtpObjectHandle handle = mRequest.getParameter(1);
+ uint64_t offset = mRequest.getParameter(2);
+ uint64_t offset2 = mRequest.getParameter(3);
+ offset = offset | (offset2 << 32);
+ uint32_t length = mRequest.getParameter(4);
+
+ ObjectEdit* edit = getEditObject(handle);
+ if (!edit) {
+ MTPE("object not open for edit in doSendPartialObject");
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+
+ // can't start writing past the end of the file
+ if (offset > edit->mSize) {
+ MTPE("writing past end of object, offset: %lld, edit->mSize: %lld", offset, edit->mSize);
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+
+ const char* filePath = (const char *)edit->mPath;
+ MTPD("receiving partial %s %lld %lld\n", filePath, offset, length);
+
+ // read the header, and possibly some data
+ int ret = mData.read(mFD);
+ if (ret < MTP_CONTAINER_HEADER_SIZE)
+ return MTP_RESPONSE_GENERAL_ERROR;
+ int initialData = ret - MTP_CONTAINER_HEADER_SIZE;
+
+ if (initialData > 0) {
+ ret = write(edit->mFD, mData.getData(), initialData);
+ offset += initialData;
+ length -= initialData;
+ }
+
+ if (length > 0) {
+ mtp_file_range mfr;
+ mfr.fd = edit->mFD;
+ mfr.offset = offset;
+ mfr.length = length;
+
+ // transfer the file
+ ret = ioctl(mFD, MTP_RECEIVE_FILE, (unsigned long)&mfr);
+ MTPD("MTP_RECEIVE_FILE returned %d", ret);
+ }
+ if (ret < 0) {
+ mResponse.setParameter(1, 0);
+ if (errno == ECANCELED)
+ return MTP_RESPONSE_TRANSACTION_CANCELLED;
+ else
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+
+ // reset so we don't attempt to send this back
+ mData.reset();
+ mResponse.setParameter(1, length);
+ uint64_t end = offset + length;
+ if (end > edit->mSize) {
+ edit->mSize = end;
+ }
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doTruncateObject() {
+ MtpObjectHandle handle = mRequest.getParameter(1);
+ ObjectEdit* edit = getEditObject(handle);
+ if (!edit) {
+ MTPE("object not open for edit in doTruncateObject");
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+
+ uint64_t offset = mRequest.getParameter(2);
+ uint64_t offset2 = mRequest.getParameter(3);
+ offset |= (offset2 << 32);
+ if (ftruncate(edit->mFD, offset) != 0) {
+ return MTP_RESPONSE_GENERAL_ERROR;
+ } else {
+ edit->mSize = offset;
+ return MTP_RESPONSE_OK;
+ }
+}
+
+MtpResponseCode MtpServer::doBeginEditObject() {
+ MtpObjectHandle handle = mRequest.getParameter(1);
+ if (getEditObject(handle)) {
+ MTPE("object already open for edit in doBeginEditObject");
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+
+ MtpString path;
+ int64_t fileLength;
+ MtpObjectFormat format;
+ MTPD("MtpServer::doBeginEditObject calling getObjectFilePath\n");
+ mDatabase->lockMutex();
+ int result = mDatabase->getObjectFilePath(handle, path, fileLength, format);
+ mDatabase->unlockMutex();
+ if (result != MTP_RESPONSE_OK)
+ return result;
+
+ int fd = open((const char *)path, O_RDWR | O_EXCL);
+ if (fd < 0) {
+ MTPE("open failed for %s in doBeginEditObject (%d)", (const char *)path, errno);
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+
+ addEditObject(handle, path, fileLength, format, fd);
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doEndEditObject() {
+ MtpObjectHandle handle = mRequest.getParameter(1);
+ ObjectEdit* edit = getEditObject(handle);
+ if (!edit) {
+ MTPE("object not open for edit in doEndEditObject");
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+
+ commitEdit(edit);
+ removeEditObject(handle);
+ return MTP_RESPONSE_OK;
+}
diff --git a/mtp/MtpServer.h b/mtp/MtpServer.h
new file mode 100644
index 000000000..944331134
--- /dev/null
+++ b/mtp/MtpServer.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_SERVER_H
+#define _MTP_SERVER_H
+
+#include <utils/threads.h>
+#include <utils/Vector.h>
+#include "MtpRequestPacket.h"
+#include "MtpDatabase.h"
+#include "MtpDataPacket.h"
+#include "MtpResponsePacket.h"
+#include "MtpEventPacket.h"
+#include "mtp.h"
+#include "MtpUtils.h"
+
+
+class MtpDatabase;
+class MtpStorage;
+
+class MtpServer {
+
+private:
+ // file descriptor for MTP kernel driver
+ int mFD;
+ android::Mutex mMutex;
+ MtpDatabase* mDatabase;
+
+ // appear as a PTP device
+ bool mPtp;
+
+ // group to own new files and folders
+ int mFileGroup;
+ // permissions for new files and directories
+ int mFilePermission;
+ int mDirectoryPermission;
+
+ // current session ID
+ MtpSessionID mSessionID;
+ // true if we have an open session and mSessionID is valid
+ bool mSessionOpen;
+
+ MtpRequestPacket mRequest;
+ MtpDataPacket mData;
+ MtpResponsePacket mResponse;
+ MtpEventPacket mEvent;
+
+ MtpStorageList mStorages;
+
+ // handle for new object, set by SendObjectInfo and used by SendObject
+ MtpObjectHandle mSendObjectHandle;
+ MtpObjectFormat mSendObjectFormat;
+ MtpString mSendObjectFilePath;
+ size_t mSendObjectFileSize;
+
+ pthread_mutex_t mtpMutex;
+
+ // represents an MTP object that is being edited using the android extensions
+ // for direct editing (BeginEditObject, SendPartialObject, TruncateObject and EndEditObject)
+ class ObjectEdit {
+ public:
+ MtpObjectHandle mHandle;
+ MtpString mPath;
+ uint64_t mSize;
+ MtpObjectFormat mFormat;
+ int mFD;
+
+ ObjectEdit(MtpObjectHandle handle, const char* path, uint64_t size,
+ MtpObjectFormat format, int fd)
+ : mHandle(handle), mPath(path), mSize(size), mFormat(format), mFD(fd) {
+ }
+
+ virtual ~ObjectEdit() {
+ close(mFD);
+ }
+ };
+ android::Vector<ObjectEdit*> mObjectEditList;
+
+public:
+ MtpServer(MtpDatabase* database, bool ptp,
+ int fileGroup, int filePerm, int directoryPerm);
+ virtual ~MtpServer();
+
+ MtpStorage* getStorage(MtpStorageID id);
+ inline bool hasStorage() { return mStorages.size() > 0; }
+ bool hasStorage(MtpStorageID id);
+ void addStorage(MtpStorage* storage);
+ void removeStorage(MtpStorage* storage);
+
+ void run(int fd);
+
+ void sendObjectAdded(MtpObjectHandle handle);
+ void sendObjectRemoved(MtpObjectHandle handle);
+ void sendObjectUpdated(MtpObjectHandle handle);
+
+private:
+ void sendStoreAdded(MtpStorageID id);
+ void sendStoreRemoved(MtpStorageID id);
+ void sendEvent(MtpEventCode code, uint32_t param1);
+
+ void addEditObject(MtpObjectHandle handle, MtpString& path,
+ uint64_t size, MtpObjectFormat format, int fd);
+ ObjectEdit* getEditObject(MtpObjectHandle handle);
+ void removeEditObject(MtpObjectHandle handle);
+ void commitEdit(ObjectEdit* edit);
+
+ bool handleRequest();
+
+ MtpResponseCode doGetDeviceInfo();
+ MtpResponseCode doOpenSession();
+ MtpResponseCode doCloseSession();
+ MtpResponseCode doGetStorageIDs();
+ MtpResponseCode doGetStorageInfo();
+ MtpResponseCode doGetObjectPropsSupported();
+ MtpResponseCode doGetObjectHandles();
+ MtpResponseCode doGetNumObjects();
+ MtpResponseCode doGetObjectReferences();
+ MtpResponseCode doSetObjectReferences();
+ MtpResponseCode doGetObjectPropValue();
+ MtpResponseCode doSetObjectPropValue();
+ MtpResponseCode doGetDevicePropValue();
+ MtpResponseCode doSetDevicePropValue();
+ MtpResponseCode doResetDevicePropValue();
+ MtpResponseCode doGetObjectPropList();
+ MtpResponseCode doGetObjectInfo();
+ MtpResponseCode doGetObject();
+ MtpResponseCode doGetThumb();
+ MtpResponseCode doGetPartialObject(MtpOperationCode operation);
+ MtpResponseCode doSendObjectInfo();
+ MtpResponseCode doSendObject();
+ MtpResponseCode doDeleteObject();
+ MtpResponseCode doGetObjectPropDesc();
+ MtpResponseCode doGetDevicePropDesc();
+ MtpResponseCode doSendPartialObject();
+ MtpResponseCode doTruncateObject();
+ MtpResponseCode doBeginEditObject();
+ MtpResponseCode doEndEditObject();
+};
+
+#endif // _MTP_SERVER_H
diff --git a/mtp/MtpStorage.cpp b/mtp/MtpStorage.cpp
new file mode 100644
index 000000000..5a6954822
--- /dev/null
+++ b/mtp/MtpStorage.cpp
@@ -0,0 +1,856 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include "MtpDebug.h"
+#include "MtpStorage.h"
+#include "MtpDataPacket.h"
+#include "MtpServer.h"
+#include "MtpEventPacket.h"
+#include "MtpDatabase.h"
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <limits.h>
+#include <pthread.h>
+#include <signal.h>
+#include <sys/inotify.h>
+#include <fcntl.h>
+#include "../tw_atomic.hpp"
+
+#define WATCH_FLAGS ( IN_CREATE | IN_DELETE | IN_MOVE | IN_MODIFY )
+
+MtpStorage::MtpStorage(MtpStorageID id, const char* filePath,
+ const char* description, uint64_t reserveSpace,
+ bool removable, uint64_t maxFileSize, MtpServer* refserver)
+ : mStorageID(id),
+ mFilePath(filePath),
+ mDescription(description),
+ mMaxCapacity(0),
+ mMaxFileSize(maxFileSize),
+ mReserveSpace(reserveSpace),
+ mRemovable(removable),
+ mServer(refserver)
+{
+ MTPI("MtpStorage id: %d path: %s\n", id, filePath);
+ inotify_thread = 0;
+ inotify_fd = -1;
+ // Threading has not started yet so we should be safe to set these directly instead of using atomics
+ inotify_thread_kill.set_value(0);
+ sendEvents = false;
+ handleCurrentlySending = 0;
+ use_mutex = true;
+ if (pthread_mutex_init(&mtpMutex, NULL) != 0) {
+ MTPE("Failed to init mtpMutex\n");
+ use_mutex = false;
+ }
+ if (pthread_mutex_init(&inMutex, NULL) != 0) {
+ MTPE("Failed to init inMutex\n");
+ pthread_mutex_destroy(&mtpMutex);
+ use_mutex = false;
+ }
+}
+
+MtpStorage::~MtpStorage() {
+ if (inotify_thread) {
+ inotify_thread_kill.set_value(1);
+ MTPD("joining inotify_thread after sending the kill notification.\n");
+ pthread_join(inotify_thread, NULL); // There's not much we can do if there's an error here
+ inotify_thread = 0;
+ MTPD("~MtpStorage removing inotify watches and closing inotify_fd\n");
+ for (std::map<int, Tree*>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
+ inotify_rm_watch(inotify_fd, i->first);
+ }
+ close(inotify_fd);
+ inotifymap.clear();
+ }
+ // Deleting the root tree causes a cascade in btree.cpp that ends up
+ // deleting all of the trees and nodes.
+ delete mtpmap[0];
+ mtpmap.clear();
+ if (use_mutex) {
+ use_mutex = false;
+ MTPD("~MtpStorage destroying mutexes\n");
+ pthread_mutex_destroy(&mtpMutex);
+ pthread_mutex_destroy(&inMutex);
+ }
+}
+
+int MtpStorage::getType() const {
+ return (mRemovable ? MTP_STORAGE_REMOVABLE_RAM : MTP_STORAGE_FIXED_RAM);
+}
+
+int MtpStorage::getFileSystemType() const {
+ return MTP_STORAGE_FILESYSTEM_HIERARCHICAL;
+}
+
+int MtpStorage::getAccessCapability() const {
+ return MTP_STORAGE_READ_WRITE;
+}
+
+uint64_t MtpStorage::getMaxCapacity() {
+ if (mMaxCapacity == 0) {
+ struct statfs stat;
+ if (statfs(getPath(), &stat))
+ return -1;
+ mMaxCapacity = (uint64_t)stat.f_blocks * (uint64_t)stat.f_bsize;
+ }
+ return mMaxCapacity;
+}
+
+uint64_t MtpStorage::getFreeSpace() {
+ struct statfs stat;
+ if (statfs(getPath(), &stat))
+ return -1;
+ uint64_t freeSpace = (uint64_t)stat.f_bavail * (uint64_t)stat.f_bsize;
+ return (freeSpace > mReserveSpace ? freeSpace - mReserveSpace : 0);
+}
+
+const char* MtpStorage::getDescription() const {
+ return (const char *)mDescription;
+}
+
+int MtpStorage::createDB() {
+ std::string mtpParent = "";
+ mtpstorageparent = getPath();
+ // root directory is special: handle 0, parent 0, and empty path
+ mtpmap[0] = new Tree(0, 0, "");
+ MTPD("MtpStorage::createDB DONE\n");
+ if (use_mutex) {
+ sendEvents = true;
+ MTPD("inotify_init\n");
+ inotify_fd = inotify_init();
+ if (inotify_fd < 0) {
+ MTPE("Can't run inotify_init for mtp server: %s\n", strerror(errno));
+ } else {
+ MTPD("Starting inotify thread\n");
+ inotify_thread = inotify();
+ }
+ } else {
+ MTPD("NOT starting inotify thread\n");
+ }
+ // for debugging and caching purposes, read the root dir already now
+ readDir(mtpstorageparent, mtpmap[0]);
+ // all other dirs are read on demand
+ return 0;
+}
+
+MtpObjectHandleList* MtpStorage::getObjectList(MtpStorageID storageID, MtpObjectHandle parent) {
+ MTPD("MtpStorage::getObjectList, parent: %u\n", parent);
+ //append object id (numerical #s) of database to int array
+ MtpObjectHandleList* list = new MtpObjectHandleList();
+ if (parent == MTP_PARENT_ROOT) {
+ MTPD("parent == MTP_PARENT_ROOT\n");
+ parent = 0;
+ }
+
+ if (mtpmap.find(parent) == mtpmap.end()) {
+ MTPE("parent handle not found, returning empty list\n");
+ return list;
+ }
+
+ Tree* tree = mtpmap[parent];
+ if (!tree->wasAlreadyRead())
+ {
+ std::string path = getNodePath(tree);
+ MTPD("reading directory on demand for tree %p (%u), path: %s\n", tree, tree->Mtpid(), path.c_str());
+ readDir(path, tree);
+ }
+
+ mtpmap[parent]->getmtpids(list);
+ MTPD("returning %u objects in %s.\n", list->size(), tree->getName().c_str());
+ return list;
+}
+
+int MtpStorage::getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info) {
+ struct stat st;
+ uint64_t size = 0;
+ MTPD("MtpStorage::getObjectInfo, handle: %u\n", handle);
+ Node* node = findNode(handle);
+ if (!node) {
+ // Item is not on this storage device
+ return -1;
+ }
+
+ info.mStorageID = getStorageID();
+ MTPD("info.mStorageID: %u\n", info.mStorageID);
+ info.mParent = node->getMtpParentId();
+ MTPD("mParent: %u\n", info.mParent);
+ // TODO: do we want to lstat again here, or read from the node properties?
+ if (lstat(getNodePath(node).c_str(), &st) == 0)
+ size = st.st_size;
+ MTPD("size is: %llu\n", size);
+ info.mCompressedSize = (size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size);
+ info.mDateModified = st.st_mtime;
+ if (S_ISDIR(st.st_mode)) {
+ info.mFormat = MTP_FORMAT_ASSOCIATION;
+ }
+ else {
+ info.mFormat = MTP_FORMAT_UNDEFINED;
+ }
+ info.mName = strdup(node->getName().c_str());
+ MTPD("MtpStorage::getObjectInfo found, Exiting getObjectInfo()\n");
+ return 0;
+}
+
+MtpObjectHandle MtpStorage::beginSendObject(const char* path,
+ MtpObjectFormat format,
+ MtpObjectHandle parent,
+ uint64_t size,
+ time_t modified) {
+ MTPD("MtpStorage::beginSendObject(), path: '%s', parent: %u, format: %04x\n", path, parent, format);
+ iter it = mtpmap.find(parent);
+ if (it == mtpmap.end()) {
+ MTPE("parent node not found, returning error\n");
+ return kInvalidObjectHandle;
+ }
+ Tree* tree = it->second;
+
+ std::string pathstr(path);
+ size_t slashpos = pathstr.find_last_of('/');
+ if (slashpos == std::string::npos) {
+ MTPE("path has no slash, returning error\n");
+ return kInvalidObjectHandle;
+ }
+ std::string parentdir = pathstr.substr(0, slashpos);
+ std::string basename = pathstr.substr(slashpos + 1);
+ if (parent != 0 && parentdir != getNodePath(tree)) {
+ MTPE("beginSendObject into path '%s' but parent tree has path '%s', returning error\n", parentdir.c_str(), getNodePath(tree).c_str());
+ return kInvalidObjectHandle;
+ }
+
+ MTPD("MtpStorage::beginSendObject() parentdir: %s basename: %s\n", parentdir.c_str(), basename.c_str());
+ // note: for directories, the mkdir call is done later in MtpServer, here we just reserve a handle
+ bool isDir = format == MTP_FORMAT_ASSOCIATION;
+ Node* node = addNewNode(isDir, tree, basename);
+ handleCurrentlySending = node->Mtpid(); // suppress inotify for this node while sending
+
+ return node->Mtpid();
+}
+
+void MtpStorage::endSendObject(const char* path, MtpObjectHandle handle, MtpObjectFormat format, bool succeeded)
+{
+ Node* node = findNode(handle);
+ if (!node)
+ return; // just ignore if this is for another storage
+
+ node->addProperties(path, mStorageID);
+ handleCurrentlySending = 0;
+ // TODO: are we supposed to send an event about an upload by the initiator?
+ if (sendEvents)
+ mServer->sendObjectAdded(node->Mtpid());
+}
+
+int MtpStorage::getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, int64_t& outFileLength, MtpObjectFormat& outFormat) {
+ MTPD("MtpStorage::getObjectFilePath handle: %u\n", handle);
+ Node* node = findNode(handle);
+ if (!node)
+ {
+ // Item is not on this storage device
+ return -1;
+ }
+ // TODO: do we want to lstat here, or just read the info from the node?
+ struct stat st;
+ if (lstat(getNodePath(node).c_str(), &st) == 0)
+ outFileLength = st.st_size;
+ else
+ outFileLength = 0;
+ outFilePath = getNodePath(node).c_str();
+ MTPD("outFilePath: %s\n", outFilePath.string());
+ outFormat = node->isDir() ? MTP_FORMAT_ASSOCIATION : MTP_FORMAT_UNDEFINED;
+ return 0;
+}
+
+int MtpStorage::readDir(const std::string& path, Tree* tree)
+{
+ struct dirent *de;
+ int storageID = getStorageID();
+ MtpObjectHandle parent = tree->Mtpid();
+
+ DIR *d = opendir(path.c_str());
+ MTPD("reading dir '%s', parent handle %u\n", path.c_str(), parent);
+ if (d == NULL) {
+ MTPE("error opening '%s' -- error: %s\n", path.c_str(), strerror(errno));
+ return -1;
+ }
+ // TODO: for refreshing dirs: capture old entries here
+ while ((de = readdir(d)) != NULL) {
+ // Because exfat-fuse causes issues with dirent, we will use stat
+ // for some things that dirent should be able to do
+ std::string item = path + "/" + de->d_name;
+ struct stat st;
+ if (lstat(item.c_str(), &st)) {
+ MTPE("Error running lstat on '%s'\n", item.c_str());
+ return -1;
+ }
+ // TODO: if we want to use this for refreshing dirs too, first find existing name and overwrite
+ if (strcmp(de->d_name, ".") == 0)
+ continue;
+ if (strcmp(de->d_name, "..") == 0)
+ continue;
+ Node* node = addNewNode(st.st_mode & S_IFDIR, tree, de->d_name);
+ node->addProperties(item, storageID);
+ //if (sendEvents)
+ // mServer->sendObjectAdded(node->Mtpid());
+ // sending events here makes simple-mtpfs very slow, and it is probably the wrong thing to do anyway
+ }
+ closedir(d);
+ // TODO: for refreshing dirs: remove entries that no longer exist (with their nodes)
+ tree->setAlreadyRead(true);
+ addInotify(tree);
+ return 0;
+}
+
+int MtpStorage::deleteFile(MtpObjectHandle handle) {
+ MTPD("MtpStorage::deleteFile handle: %u\n", handle);
+ Node* node = findNode(handle);
+ if (!node) {
+ // Item is not on this storage device
+ return -1;
+ }
+ MtpObjectHandle parent = node->getMtpParentId();
+ Tree* tree = mtpmap[parent];
+ if (!tree) {
+ MTPE("parent tree for handle %u not found\n", parent);
+ return -1;
+ }
+ if (node->isDir()) {
+ MTPD("deleting tree from mtpmap: %u\n", handle);
+ mtpmap.erase(handle);
+ }
+
+ MTPD("deleting handle: %u\n", handle);
+ tree->deleteNode(handle);
+ MTPD("deleted\n");
+ return 0;
+}
+
+void MtpStorage::queryNodeProperties(std::vector<MtpStorage::PropEntry>& results, Node* node, uint32_t property, int groupCode, MtpStorageID storageID)
+{
+ MTPD("queryNodeProperties handle %u, path: %s\n", node->Mtpid(), getNodePath(node).c_str());
+ PropEntry pe;
+ pe.handle = node->Mtpid();
+ pe.property = property;
+
+ if (property == 0xffffffff)
+ {
+ // add all properties
+ MTPD("MtpStorage::queryNodeProperties for all properties\n");
+ std::vector<Node::mtpProperty> mtpprop = node->getMtpProps();
+ for (size_t i = 0; i < mtpprop.size(); ++i) {
+ pe.property = mtpprop[i].property;
+ pe.datatype = mtpprop[i].dataType;
+ pe.intvalue = mtpprop[i].valueInt;
+ pe.strvalue = mtpprop[i].valueStr;
+ results.push_back(pe);
+ }
+ return;
+ }
+ else if (property == 0)
+ {
+ // TODO: use groupCode
+ }
+
+ // single property
+ // TODO: this should probably be moved to the Node class and/or merged with getObjectPropertyValue
+ switch (property) {
+// case MTP_PROPERTY_OBJECT_FORMAT:
+// pe.datatype = MTP_TYPE_UINT16;
+// pe.intvalue = node->getIntProperty(MTP_PROPERTY_OBJECT_FORMAT);
+// break;
+
+ case MTP_PROPERTY_STORAGE_ID:
+ pe.datatype = MTP_TYPE_UINT32;
+ pe.intvalue = storageID;
+ break;
+
+ case MTP_PROPERTY_PROTECTION_STATUS:
+ pe.datatype = MTP_TYPE_UINT16;
+ pe.intvalue = 0;
+ break;
+
+ case MTP_PROPERTY_OBJECT_SIZE:
+ {
+ pe.datatype = MTP_TYPE_UINT64;
+ struct stat st;
+ pe.intvalue = 0;
+ if (lstat(getNodePath(node).c_str(), &st) == 0)
+ pe.intvalue = st.st_size;
+ break;
+ }
+
+ default:
+ {
+ const Node::mtpProperty& prop = node->getProperty(property);
+ if (prop.property != property)
+ {
+ MTPD("queryNodeProperties: unknown property %x\n", property);
+ return;
+ }
+ pe.datatype = prop.dataType;
+ pe.intvalue = prop.valueInt;
+ pe.strvalue = prop.valueStr;
+ // TODO: all the special case stuff in MyMtpDatabase::getObjectPropertyValue is missing here
+ }
+
+ }
+ results.push_back(pe);
+}
+
+int MtpStorage::getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet) {
+ MTPD("MtpStorage::getObjectPropertyList handle: %u, format: %x, property: %x\n", handle, format, property);
+ if (groupCode != 0)
+ {
+ MTPE("getObjectPropertyList: groupCode unsupported\n");
+ return -1; // TODO: RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED
+ }
+ // TODO: support all the special stuff, like:
+ // handle == 0 -> all objects at the root level
+ // handle == 0xffffffff -> all objects (on all storages? how could we support that?)
+ // format == 0 -> all formats, otherwise filter by ObjectFormatCode
+ // property == 0xffffffff -> all properties except those with group code 0xffffffff
+ // if property == 0 then use groupCode
+ // groupCode == 0 -> return Specification_By_Group_Unsupported
+ // depth == 0xffffffff -> all objects incl. and below handle
+
+ std::vector<PropEntry> results;
+
+ if (handle == 0xffffffff) {
+ // TODO: all object on all storages (needs a different design, result packet needs to be built by server instead of storage)
+ } else if (handle == 0) {
+ // all objects at the root level
+ Tree* root = mtpmap[0];
+ MtpObjectHandleList list;
+ root->getmtpids(&list);
+ for (MtpObjectHandleList::iterator it = list.begin(); it != list.end(); ++it) {
+ Node* node = root->findNode(*it);
+ if (!node) {
+ MTPE("BUG: node not found for root entry with handle %u\n", *it);
+ break;
+ }
+ queryNodeProperties(results, node, property, groupCode, mStorageID);
+ }
+ } else {
+ // single object
+ Node* node = findNode(handle);
+ if (!node) {
+ // Item is not on this storage device
+ return -1;
+ }
+ queryNodeProperties(results, node, property, groupCode, mStorageID);
+ }
+
+ MTPD("count: %u\n", results.size());
+ packet.putUInt32(results.size());
+
+ for (size_t i = 0; i < results.size(); ++i) {
+ PropEntry& p = results[i];
+ MTPD("handle: %u, propertyCode: %x = %s, datatype: %x, value: %llu\n",
+ p.handle, p.property, MtpDebug::getObjectPropCodeName(p.property),
+ p.datatype, p.intvalue);
+ packet.putUInt32(p.handle);
+ packet.putUInt16(p.property);
+ packet.putUInt16(p.datatype);
+ switch (p.datatype) {
+ case MTP_TYPE_INT8:
+ MTPD("MTP_TYPE_INT8\n");
+ packet.putInt8(p.intvalue);
+ break;
+ case MTP_TYPE_UINT8:
+ MTPD("MTP_TYPE_UINT8\n");
+ packet.putUInt8(p.intvalue);
+ break;
+ case MTP_TYPE_INT16:
+ MTPD("MTP_TYPE_INT16\n");
+ packet.putInt16(p.intvalue);
+ break;
+ case MTP_TYPE_UINT16:
+ MTPD("MTP_TYPE_UINT16\n");
+ packet.putUInt16(p.intvalue);
+ break;
+ case MTP_TYPE_INT32:
+ MTPD("MTP_TYPE_INT32\n");
+ packet.putInt32(p.intvalue);
+ break;
+ case MTP_TYPE_UINT32:
+ MTPD("MTP_TYPE_UINT32\n");
+ packet.putUInt32(p.intvalue);
+ break;
+ case MTP_TYPE_INT64:
+ MTPD("MTP_TYPE_INT64\n");
+ packet.putInt64(p.intvalue);
+ break;
+ case MTP_TYPE_UINT64:
+ MTPD("MTP_TYPE_UINT64\n");
+ packet.putUInt64(p.intvalue);
+ break;
+ case MTP_TYPE_INT128:
+ MTPD("MTP_TYPE_INT128\n");
+ packet.putInt128(p.intvalue);
+ break;
+ case MTP_TYPE_UINT128:
+ MTPD("MTP_TYPE_UINT128\n");
+ packet.putUInt128(p.intvalue);
+ break;
+ case MTP_TYPE_STR:
+ MTPD("MTP_TYPE_STR: %s\n", p.strvalue.c_str());
+ packet.putString(p.strvalue.c_str());
+ break;
+ default:
+ MTPE("bad or unsupported data type: %x in MyMtpDatabase::getObjectPropertyList", p.datatype);
+ break;
+ }
+ }
+ return 0;
+}
+
+int MtpStorage::renameObject(MtpObjectHandle handle, std::string newName) {
+ MTPD("MtpStorage::renameObject, handle: %u, new name: '%s'\n", handle, newName.c_str());
+ if (handle == MTP_PARENT_ROOT) {
+ MTPE("parent == MTP_PARENT_ROOT, cannot rename root\n");
+ return -1;
+ } else {
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+ Node* node = i->second->findNode(handle);
+ if (node != NULL) {
+ std::string oldName = getNodePath(node);
+ std::string parentdir = oldName.substr(0, oldName.find_last_of('/'));
+ std::string newFullName = parentdir + "/" + newName;
+ MTPD("old: '%s', new: '%s'\n", oldName.c_str(), newFullName.c_str());
+ if (rename(oldName.c_str(), newFullName.c_str()) == 0) {
+ node->rename(newName);
+ return 0;
+ } else {
+ MTPE("MtpStorage::renameObject failed, handle: %u, new name: '%s'\n", handle, newName.c_str());
+ return -1;
+ }
+ }
+ }
+ }
+ // handle not found on this storage
+ return -1;
+}
+
+int MtpStorage::getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, MtpStorage::PropEntry& pe) {
+ Node *node;
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+ node = i->second->findNode(handle);
+ if (node != NULL) {
+ const Node::mtpProperty& prop = node->getProperty(property);
+ if (prop.property != property) {
+ MTPD("getObjectPropertyValue: unknown property %x for handle %u\n", property, handle);
+ return -1;
+ }
+ pe.datatype = prop.dataType;
+ pe.intvalue = prop.valueInt;
+ pe.strvalue = prop.valueStr;
+ pe.handle = handle;
+ pe.property = property;
+ return 0;
+ }
+ }
+ // handle not found on this storage
+ return -1;
+}
+
+pthread_t MtpStorage::inotify(void) {
+ pthread_t thread;
+ pthread_attr_t tattr;
+
+ if (pthread_attr_init(&tattr)) {
+ MTPE("Unable to pthread_attr_init\n");
+ return 0;
+ }
+ if (pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE)) {
+ MTPE("Error setting pthread_attr_setdetachstate\n");
+ return 0;
+ }
+ ThreadPtr inotifyptr = &MtpStorage::inotify_t;
+ PThreadPtr p = *(PThreadPtr*)&inotifyptr;
+ pthread_create(&thread, &tattr, p, this);
+ if (pthread_attr_destroy(&tattr)) {
+ MTPE("Failed to pthread_attr_destroy\n");
+ }
+ return thread;
+}
+
+int MtpStorage::addInotify(Tree* tree) {
+ if (inotify_fd < 0) {
+ MTPE("inotify_fd not set or error: %i\n", inotify_fd);
+ return -1;
+ }
+ std::string path = getNodePath(tree);
+ MTPD("adding inotify for tree %x, dir: %s\n", tree, path.c_str());
+ int wd = inotify_add_watch(inotify_fd, path.c_str(), WATCH_FLAGS);
+ if (wd < 0) {
+ MTPE("inotify_add_watch failed: %s\n", strerror(errno));
+ return -1;
+ }
+ inotifymap[wd] = tree;
+ return 0;
+}
+
+void MtpStorage::handleInotifyEvent(struct inotify_event* event)
+{
+ std::map<int, Tree*>::iterator it = inotifymap.find(event->wd);
+ if (it == inotifymap.end()) {
+ MTPE("Unable to locate inotify_wd: %i\n", event->wd);
+ return;
+ }
+ Tree* tree = it->second;
+ MTPD("inotify_t tree: %x '%s'\n", tree, tree->getName().c_str());
+ Node* node = tree->findEntryByName(basename(event->name));
+ if (node && node->Mtpid() == handleCurrentlySending) {
+ MTPD("ignoring inotify event for currently uploading file, handle: %u\n", node->Mtpid());
+ return;
+ }
+ if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) {
+ if (event->mask & IN_ISDIR) {
+ MTPD("inotify_t create is dir\n");
+ } else {
+ MTPD("inotify_t create is file\n");
+ }
+ if (node == NULL) {
+ node = addNewNode(event->mask & IN_ISDIR, tree, event->name);
+ std::string item = getNodePath(tree) + "/" + event->name;
+ node->addProperties(item, getStorageID());
+ mServer->sendObjectAdded(node->Mtpid());
+ } else {
+ MTPD("inotify_t item already exists.\n");
+ }
+ if (event->mask & IN_ISDIR) {
+ // TODO: do we need to do anything here? probably not until someone reads from the dir...
+ }
+ } else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) {
+ if (event->mask & IN_ISDIR) {
+ MTPD("inotify_t Directory %s deleted\n", event->name);
+ } else {
+ MTPD("inotify_t File %s deleted\n", event->name);
+ }
+ if (node)
+ {
+ if (event->mask & IN_ISDIR) {
+ for (std::map<int, Tree*>::iterator it = inotifymap.begin(); it != inotifymap.end(); ++it) {
+ if (it->second == node) {
+ inotify_rm_watch(inotify_fd, it->first);
+ MTPD("inotify_t removing watch on '%s'\n", getNodePath(it->second).c_str());
+ inotifymap.erase(it->first);
+ break;
+ }
+
+ }
+ }
+ MtpObjectHandle handle = node->Mtpid();
+ deleteFile(handle);
+ mServer->sendObjectRemoved(handle);
+ } else {
+ MTPD("inotify_t already removed.\n");
+ }
+ } else if (event->mask & IN_MODIFY) {
+ MTPD("inotify_t item %s modified.\n", event->name);
+ if (node != NULL) {
+ uint64_t orig_size = node->getProperty(MTP_PROPERTY_OBJECT_SIZE).valueInt;
+ struct stat st;
+ uint64_t new_size = 0;
+ if (lstat(getNodePath(node).c_str(), &st) == 0)
+ new_size = (uint64_t)st.st_size;
+ if (orig_size != new_size) {
+ MTPD("size changed from %llu to %llu on mtpid: %u\n", orig_size, new_size, node->Mtpid());
+ node->updateProperty(MTP_PROPERTY_OBJECT_SIZE, new_size, "", MTP_TYPE_UINT64);
+ mServer->sendObjectUpdated(node->Mtpid());
+ }
+ } else {
+ MTPE("inotify_t modified item not found\n");
+ }
+ } else if (event->mask & IN_DELETE_SELF || event->mask & IN_MOVE_SELF) {
+ // TODO: is this always already handled by IN_DELETE for the parent dir?
+ }
+}
+
+int MtpStorage::inotify_t(void) {
+ #define EVENT_SIZE ( sizeof(struct inotify_event) )
+ #define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16) )
+ char buf[EVENT_BUF_LEN];
+ fd_set fdset;
+ struct timeval seltmout;
+ int sel_ret;
+
+ MTPD("inotify thread starting.\n");
+
+ while (inotify_thread_kill.get_value() == 0) {
+ FD_ZERO(&fdset);
+ FD_SET(inotify_fd, &fdset);
+ seltmout.tv_sec = 0;
+ seltmout.tv_usec = 25000;
+ sel_ret = select(inotify_fd + 1, &fdset, NULL, NULL, &seltmout);
+ if (sel_ret == 0)
+ continue;
+ int i = 0;
+ int len = read(inotify_fd, buf, EVENT_BUF_LEN);
+
+ if (len < 0) {
+ if (errno == EINTR)
+ continue;
+ MTPE("inotify_t Can't read inotify events\n");
+ }
+
+ while (i < len && inotify_thread_kill.get_value() == 0) {
+ struct inotify_event *event = (struct inotify_event *) &buf[i];
+ if (event->len) {
+ MTPD("inotify event: wd: %i, mask: %x, name: %s\n", event->wd, event->mask, event->name);
+ lockMutex(1);
+ handleInotifyEvent(event);
+ unlockMutex(1);
+ }
+ i += EVENT_SIZE + event->len;
+ }
+ }
+ MTPD("inotify_thread_kill received!\n");
+ // This cleanup is handled in the destructor.
+ /*for (std::map<int, Tree*>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
+ inotify_rm_watch(inotify_fd, i->first);
+ }
+ close(inotify_fd);*/
+ return 0;
+}
+
+Node* MtpStorage::findNodeByPath(const std::string& path) {
+ MTPD("findNodeByPath: %s\n", path.c_str());
+ std::string match = path.substr(0, mtpstorageparent.size());
+ if (match != mtpstorageparent) {
+ // not on this device
+ MTPD("no match: %s is not on storage %s\n", match.c_str(), mtpstorageparent.c_str());
+ return NULL;
+ }
+
+ // TODO: fix and test this
+ std::string p = path.substr(mtpstorageparent.size()+1); // cut off "/" after storage root too
+ Tree* tree = mtpmap[0]; // start at storage root
+
+ Node* node = NULL;
+ while (!p.empty()) {
+ size_t slashpos = p.find('/');
+ std::string e;
+ if (slashpos != std::string::npos) {
+ e = p;
+ p.clear();
+ } else {
+ e = p.substr(0, slashpos);
+ p = p.substr(slashpos + 1);
+ }
+ MTPD("path element: %s, rest: %s\n", e.c_str(), p.c_str());
+ node = tree->findEntryByName(e);
+ if (!node) {
+ MTPE("path element of %s not found: %s\n", path.c_str(), e.c_str());
+ return NULL;
+ }
+ if (node->isDir())
+ tree = static_cast<Tree*>(node);
+ else if (!p.empty()) {
+ MTPE("path element of %s is not a directory: %s node: %p\n", path.c_str(), e.c_str(), node);
+ return NULL;
+ }
+ }
+ MTPD("findNodeByPath: found node %p, handle: %u, name: %s\n", node, node->Mtpid(), node->getName().c_str());
+ return node;
+}
+
+Node* MtpStorage::addNewNode(bool isDir, Tree* tree, const std::string& name)
+{
+ // global counter for new object handles
+ static MtpObjectHandle mtpid = 0;
+
+ ++mtpid;
+ MTPD("adding new %s node for %s, new handle: %u\n", isDir ? "dir" : "file", name.c_str(), mtpid);
+ MtpObjectHandle parent = tree->Mtpid();
+ MTPD("parent tree: %x, handle: %u, name: %s\n", tree, parent, tree->getName().c_str());
+ Node* node;
+ if (isDir)
+ node = mtpmap[mtpid] = new Tree(mtpid, parent, name);
+ else
+ node = new Node(mtpid, parent, name);
+ tree->addEntry(node);
+ return node;
+}
+
+Node* MtpStorage::findNode(MtpObjectHandle handle) {
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+ Node* node = i->second->findNode(handle);
+ if (node != NULL) {
+ MTPD("findNode: found node %p for handle %u, name: %s\n", node, handle, node->getName().c_str());
+ if (node->Mtpid() != handle)
+ {
+ MTPE("BUG: entry for handle %u points to node with handle %u\n", handle, node->Mtpid());
+ }
+ return node;
+ }
+ }
+ // Item is not on this storage device
+ MTPD("MtpStorage::findNode: no node found for handle %u on storage %u, searched %u trees\n", handle, mStorageID, mtpmap.size());
+ return NULL;
+}
+
+std::string MtpStorage::getNodePath(Node* node) {
+ std::string path;
+ MTPD("getNodePath: node %p, handle %u\n", node, node->Mtpid());
+ while (node)
+ {
+ path = "/" + node->getName() + path;
+ MtpObjectHandle parent = node->getMtpParentId();
+ if (parent == 0) // root
+ break;
+ node = findNode(parent);
+ }
+ path = mtpstorageparent + path;
+ MTPD("getNodePath: path %s\n", path.c_str());
+ return path;
+}
+
+void MtpStorage::lockMutex(int thread_type) {
+ if (!use_mutex)
+ return; // mutex is disabled
+ if (thread_type) {
+ // inotify thread
+ pthread_mutex_lock(&inMutex);
+ while (pthread_mutex_trylock(&mtpMutex)) {
+ pthread_mutex_unlock(&inMutex);
+ usleep(32000);
+ pthread_mutex_lock(&inMutex);
+ }
+ } else {
+ // main mtp thread
+ pthread_mutex_lock(&mtpMutex);
+ while (pthread_mutex_trylock(&inMutex)) {
+ pthread_mutex_unlock(&mtpMutex);
+ usleep(13000);
+ pthread_mutex_lock(&mtpMutex);
+ }
+ }
+}
+
+void MtpStorage::unlockMutex(int thread_type) {
+ if (!use_mutex)
+ return; // mutex is disabled
+ pthread_mutex_unlock(&inMutex);
+ pthread_mutex_unlock(&mtpMutex);
+}
diff --git a/mtp/MtpStorage.h b/mtp/MtpStorage.h
new file mode 100644
index 000000000..245debf60
--- /dev/null
+++ b/mtp/MtpStorage.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_STORAGE_H
+#define _MTP_STORAGE_H
+
+#include "mtp.h"
+#include "MtpObjectInfo.h"
+#include <string>
+#include <deque>
+#include <map>
+#include <libgen.h>
+#include <pthread.h>
+#include "btree.hpp"
+#include "MtpServer.h"
+#include "../tw_atomic.hpp"
+
+class MtpDatabase;
+struct inotify_event;
+
+class MtpStorage {
+
+private:
+ MtpStorageID mStorageID;
+ MtpString mFilePath;
+ MtpString mDescription;
+ uint64_t mMaxCapacity;
+ uint64_t mMaxFileSize;
+ // amount of free space to leave unallocated
+ uint64_t mReserveSpace;
+ bool mRemovable;
+ MtpServer* mServer;
+ typedef std::map<int, Tree*> maptree;
+ typedef maptree::iterator iter;
+ maptree mtpmap;
+ std::string mtpstorageparent;
+ android::Mutex mMutex;
+
+public:
+ MtpStorage(MtpStorageID id, const char* filePath,
+ const char* description, uint64_t reserveSpace,
+ bool removable, uint64_t maxFileSize, MtpServer* refserver);
+ virtual ~MtpStorage();
+
+ inline MtpStorageID getStorageID() const { return mStorageID; }
+ int getType() const;
+ int getFileSystemType() const;
+ int getAccessCapability() const;
+ uint64_t getMaxCapacity();
+ uint64_t getFreeSpace();
+ const char* getDescription() const;
+ inline const char* getPath() const { return (const char *)mFilePath; }
+ inline bool isRemovable() const { return mRemovable; }
+ inline uint64_t getMaxFileSize() const { return mMaxFileSize; }
+
+ struct PropEntry {
+ MtpObjectHandle handle;
+ uint16_t property;
+ uint16_t datatype;
+ uint64_t intvalue;
+ std::string strvalue;
+ };
+
+ int readDir(const std::string& path, Tree* tree);
+ int createDB();
+ MtpObjectHandleList* getObjectList(MtpStorageID storageID, MtpObjectHandle parent);
+ int getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info);
+ MtpObjectHandle beginSendObject(const char* path, MtpObjectFormat format, MtpObjectHandle parent, uint64_t size, time_t modified);
+ void endSendObject(const char* path, MtpObjectHandle handle, MtpObjectFormat format, bool succeeded);
+ int getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet);
+ int getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, int64_t& outFileLength, MtpObjectFormat& outFormat);
+ int deleteFile(MtpObjectHandle handle);
+ int renameObject(MtpObjectHandle handle, std::string newName);
+ int getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, PropEntry& prop);
+ void lockMutex(int thread_type);
+ void unlockMutex(int thread_type);
+
+private:
+ pthread_t inotify();
+ int inotify_t();
+ typedef int (MtpStorage::*ThreadPtr)(void);
+ typedef void* (*PThreadPtr)(void *);
+ std::map<int, Tree*> inotifymap; // inotify wd -> tree
+ pthread_t inotify_thread;
+ int inotify_fd;
+ int addInotify(Tree* tree);
+ void handleInotifyEvent(struct inotify_event* event);
+
+ bool sendEvents;
+ MtpObjectHandle handleCurrentlySending;
+
+ Node* addNewNode(bool isDir, Tree* tree, const std::string& name);
+ Node* findNode(MtpObjectHandle handle);
+ Node* findNodeByPath(const std::string& path);
+ std::string getNodePath(Node* node);
+
+ void queryNodeProperties(std::vector<PropEntry>& results, Node* node, uint32_t property, int groupCode, MtpStorageID storageID);
+
+ bool use_mutex;
+ pthread_mutex_t inMutex; // inotify mutex
+ pthread_mutex_t mtpMutex; // main mtp mutex
+ TWAtomicInt inotify_thread_kill;
+};
+
+#endif // _MTP_STORAGE_H
diff --git a/mtp/MtpStorageInfo.cpp b/mtp/MtpStorageInfo.cpp
new file mode 100644
index 000000000..a2b8ca2a1
--- /dev/null
+++ b/mtp/MtpStorageInfo.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include "MtpDebug.h"
+#include "MtpDataPacket.h"
+#include "MtpStorageInfo.h"
+#include "MtpStringBuffer.h"
+
+MtpStorageInfo::MtpStorageInfo(MtpStorageID id)
+ : mStorageID(id),
+ mStorageType(0),
+ mFileSystemType(0),
+ mAccessCapability(0),
+ mMaxCapacity(0),
+ mFreeSpaceBytes(0),
+ mFreeSpaceObjects(0),
+ mStorageDescription(NULL),
+ mVolumeIdentifier(NULL)
+{
+}
+
+MtpStorageInfo::~MtpStorageInfo() {
+ if (mStorageDescription)
+ free(mStorageDescription);
+ if (mVolumeIdentifier)
+ free(mVolumeIdentifier);
+}
+
+void MtpStorageInfo::read(MtpDataPacket& packet) {
+ MtpStringBuffer string;
+
+ // read the device info
+ mStorageType = packet.getUInt16();
+ mFileSystemType = packet.getUInt16();
+ mAccessCapability = packet.getUInt16();
+ mMaxCapacity = packet.getUInt64();
+ mFreeSpaceBytes = packet.getUInt64();
+ mFreeSpaceObjects = packet.getUInt32();
+
+ packet.getString(string);
+ mStorageDescription = strdup((const char *)string);
+ packet.getString(string);
+ mVolumeIdentifier = strdup((const char *)string);
+}
+
+void MtpStorageInfo::print() {
+ MTPI("Storage Info %08X:\n\tmStorageType: %d\n\tmFileSystemType: %d\n\tmAccessCapability: %d\n",
+ mStorageID, mStorageType, mFileSystemType, mAccessCapability);
+ MTPI("\tmMaxCapacity: %lld\n\tmFreeSpaceBytes: %lld\n\tmFreeSpaceObjects: %d\n",
+ mMaxCapacity, mFreeSpaceBytes, mFreeSpaceObjects);
+ MTPI("\tmStorageDescription: %s\n\tmVolumeIdentifier: %s\n",
+ mStorageDescription, mVolumeIdentifier);
+}
+
diff --git a/mtp/MtpStorageInfo.h b/mtp/MtpStorageInfo.h
new file mode 100644
index 000000000..8858328fb
--- /dev/null
+++ b/mtp/MtpStorageInfo.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_STORAGE_INFO_H
+#define _MTP_STORAGE_INFO_H
+
+#include "MtpTypes.h"
+
+
+class MtpDataPacket;
+
+class MtpStorageInfo {
+public:
+ MtpStorageID mStorageID;
+ uint16_t mStorageType;
+ uint16_t mFileSystemType;
+ uint16_t mAccessCapability;
+ uint64_t mMaxCapacity;
+ uint64_t mFreeSpaceBytes;
+ uint32_t mFreeSpaceObjects;
+ char* mStorageDescription;
+ char* mVolumeIdentifier;
+
+public:
+ MtpStorageInfo(MtpStorageID id);
+ virtual ~MtpStorageInfo();
+
+ void read(MtpDataPacket& packet);
+
+ void print();
+};
+
+
+#endif // _MTP_STORAGE_INFO_H
diff --git a/mtp/MtpStringBuffer.cpp b/mtp/MtpStringBuffer.cpp
new file mode 100644
index 000000000..8aeb3ca94
--- /dev/null
+++ b/mtp/MtpStringBuffer.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include <string.h>
+#include "MtpDataPacket.h"
+#include "MtpStringBuffer.h"
+
+MtpStringBuffer::MtpStringBuffer()
+ : mCharCount(0),
+ mByteCount(1)
+{
+ mBuffer[0] = 0;
+}
+
+MtpStringBuffer::MtpStringBuffer(const char* src)
+ : mCharCount(0),
+ mByteCount(1)
+{
+ set(src);
+}
+
+MtpStringBuffer::MtpStringBuffer(const uint16_t* src)
+ : mCharCount(0),
+ mByteCount(1)
+{
+ set(src);
+}
+
+MtpStringBuffer::MtpStringBuffer(const MtpStringBuffer& src)
+ : mCharCount(src.mCharCount),
+ mByteCount(src.mByteCount)
+{
+ memcpy(mBuffer, src.mBuffer, mByteCount);
+}
+
+
+MtpStringBuffer::~MtpStringBuffer() {
+}
+
+void MtpStringBuffer::set(const char* src) {
+ size_t length = strlen(src);
+ if (length >= sizeof(mBuffer))
+ length = sizeof(mBuffer) - 1;
+ memcpy(mBuffer, src, length);
+
+ // count the characters
+ int count = 0;
+ char ch;
+ while ((ch = *src++) != 0) {
+ if ((ch & 0x80) == 0) {
+ // single byte character
+ } else if ((ch & 0xE0) == 0xC0) {
+ // two byte character
+ if (! *src++) {
+ // last character was truncated, so ignore last byte
+ length--;
+ break;
+ }
+ } else if ((ch & 0xF0) == 0xE0) {
+ // 3 byte char
+ if (! *src++) {
+ // last character was truncated, so ignore last byte
+ length--;
+ break;
+ }
+ if (! *src++) {
+ // last character was truncated, so ignore last two bytes
+ length -= 2;
+ break;
+ }
+ }
+ count++;
+ }
+
+ mByteCount = length + 1;
+ mBuffer[length] = 0;
+ mCharCount = count;
+}
+
+void MtpStringBuffer::set(const uint16_t* src) {
+ int count = 0;
+ uint16_t ch;
+ uint8_t* dest = mBuffer;
+
+ while ((ch = *src++) != 0 && count < 255) {
+ if (ch >= 0x0800) {
+ *dest++ = (uint8_t)(0xE0 | (ch >> 12));
+ *dest++ = (uint8_t)(0x80 | ((ch >> 6) & 0x3F));
+ *dest++ = (uint8_t)(0x80 | (ch & 0x3F));
+ } else if (ch >= 0x80) {
+ *dest++ = (uint8_t)(0xC0 | (ch >> 6));
+ *dest++ = (uint8_t)(0x80 | (ch & 0x3F));
+ } else {
+ *dest++ = ch;
+ }
+ count++;
+ }
+ *dest++ = 0;
+ mCharCount = count;
+ mByteCount = dest - mBuffer;
+}
+
+void MtpStringBuffer::readFromPacket(MtpDataPacket* packet) {
+ int count = packet->getUInt8();
+ uint8_t* dest = mBuffer;
+ for (int i = 0; i < count; i++) {
+ uint16_t ch = packet->getUInt16();
+ if (ch >= 0x0800) {
+ *dest++ = (uint8_t)(0xE0 | (ch >> 12));
+ *dest++ = (uint8_t)(0x80 | ((ch >> 6) & 0x3F));
+ *dest++ = (uint8_t)(0x80 | (ch & 0x3F));
+ } else if (ch >= 0x80) {
+ *dest++ = (uint8_t)(0xC0 | (ch >> 6));
+ *dest++ = (uint8_t)(0x80 | (ch & 0x3F));
+ } else {
+ *dest++ = ch;
+ }
+ }
+ *dest++ = 0;
+ mCharCount = count;
+ mByteCount = dest - mBuffer;
+}
+
+void MtpStringBuffer::writeToPacket(MtpDataPacket* packet) const {
+ int count = mCharCount;
+ const uint8_t* src = mBuffer;
+ packet->putUInt8(count > 0 ? count + 1 : 0);
+
+ // expand utf8 to 16 bit chars
+ for (int i = 0; i < count; i++) {
+ uint16_t ch;
+ uint16_t ch1 = *src++;
+ if ((ch1 & 0x80) == 0) {
+ // single byte character
+ ch = ch1;
+ } else if ((ch1 & 0xE0) == 0xC0) {
+ // two byte character
+ uint16_t ch2 = *src++;
+ ch = ((ch1 & 0x1F) << 6) | (ch2 & 0x3F);
+ } else {
+ // three byte character
+ uint16_t ch2 = *src++;
+ uint16_t ch3 = *src++;
+ ch = ((ch1 & 0x0F) << 12) | ((ch2 & 0x3F) << 6) | (ch3 & 0x3F);
+ }
+ packet->putUInt16(ch);
+ }
+ // only terminate with zero if string is not empty
+ if (count > 0)
+ packet->putUInt16(0);
+}
+
diff --git a/mtp/MtpStringBuffer.h b/mtp/MtpStringBuffer.h
new file mode 100644
index 000000000..9d61ecf54
--- /dev/null
+++ b/mtp/MtpStringBuffer.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_STRING_BUFFER_H
+#define _MTP_STRING_BUFFER_H
+
+#include <stdint.h>
+
+class MtpDataPacket;
+
+// Represents a utf8 string, with a maximum of 255 characters
+class MtpStringBuffer {
+
+private:
+ // mBuffer contains string in UTF8 format
+ // maximum 3 bytes/character, with 1 extra for zero termination
+ uint8_t mBuffer[255 * 3 + 1];
+ int mCharCount;
+ int mByteCount;
+
+public:
+ MtpStringBuffer();
+ MtpStringBuffer(const char* src);
+ MtpStringBuffer(const uint16_t* src);
+ MtpStringBuffer(const MtpStringBuffer& src);
+ virtual ~MtpStringBuffer();
+
+ void set(const char* src);
+ void set(const uint16_t* src);
+
+ void readFromPacket(MtpDataPacket* packet);
+ void writeToPacket(MtpDataPacket* packet) const;
+
+ inline int getCharCount() const { return mCharCount; }
+ inline int getByteCount() const { return mByteCount; }
+
+ inline operator const char*() const { return (const char *)mBuffer; }
+};
+
+#endif // _MTP_STRING_BUFFER_H
diff --git a/mtp/MtpTypes.h b/mtp/MtpTypes.h
new file mode 100644
index 000000000..64e180cb4
--- /dev/null
+++ b/mtp/MtpTypes.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_TYPES_H
+#define _MTP_TYPES_H
+
+#include <stdint.h>
+#include <vector>
+#include <utils/Vector.h>
+#include <utils/String8.h>
+
+typedef int32_t int128_t[4];
+typedef uint32_t uint128_t[4];
+
+typedef uint16_t MtpOperationCode;
+typedef uint16_t MtpResponseCode;
+typedef uint16_t MtpEventCode;
+typedef uint32_t MtpSessionID;
+typedef uint32_t MtpStorageID;
+typedef uint32_t MtpTransactionID;
+typedef uint16_t MtpPropertyCode;
+typedef uint16_t MtpDataType;
+typedef uint16_t MtpObjectFormat;
+typedef MtpPropertyCode MtpDeviceProperty;
+typedef MtpPropertyCode MtpObjectProperty;
+
+// object handles are unique across all storage but only within a single session.
+// object handles cannot be reused after an object is deleted.
+// values 0x00000000 and 0xFFFFFFFF are reserved for special purposes.
+typedef uint32_t MtpObjectHandle;
+
+// Special values
+#define MTP_PARENT_ROOT 0xFFFFFFFF // parent is root of the storage
+#define kInvalidObjectHandle 0xFFFFFFFF
+
+class MtpStorage;
+class MtpDevice;
+class MtpProperty;
+
+typedef android::Vector<MtpStorage*> MtpStorageList;
+typedef android::Vector<MtpDevice*> MtpDeviceList;
+typedef android::Vector<MtpProperty*> MtpPropertyList;
+
+typedef android::Vector<uint8_t> UInt8List;
+typedef android::Vector<uint16_t> UInt16List;
+typedef android::Vector<uint32_t> UInt32List;
+typedef android::Vector<uint64_t> UInt64List;
+typedef android::Vector<int8_t> Int8List;
+typedef android::Vector<int16_t> Int16List;
+typedef android::Vector<int32_t> Int32List;
+typedef android::Vector<int64_t> Int64List;
+
+typedef UInt16List MtpObjectPropertyList;
+typedef UInt16List MtpDevicePropertyList;
+typedef UInt16List MtpObjectFormatList;
+typedef UInt32List MtpObjectHandleList;
+typedef UInt16List MtpObjectPropertyList;
+typedef UInt32List MtpStorageIDList;
+
+typedef android::String8 MtpString;
+
+
+#endif // _MTP_TYPES_H
diff --git a/mtp/MtpUtils.cpp b/mtp/MtpUtils.cpp
new file mode 100644
index 000000000..4ad59fd44
--- /dev/null
+++ b/mtp/MtpUtils.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include <stdio.h>
+#include <time.h>
+// Not available in 5.0
+//#include <cutils/tztime.h>
+#include "MtpUtils.h"
+#include "MtpDebug.h"
+
+
+/*
+DateTime strings follow a compatible subset of the definition found in ISO 8601, and
+take the form of a Unicode string formatted as: "YYYYMMDDThhmmss.s". In this
+representation, YYYY shall be replaced by the year, MM replaced by the month (01-12),
+DD replaced by the day (01-31), T is a constant character 'T' delimiting time from date,
+hh is replaced by the hour (00-23), mm is replaced by the minute (00-59), and ss by the
+second (00-59). The ".s" is optional, and represents tenths of a second.
+*/
+
+bool parseDateTime(const char* dateTime, time_t& outSeconds) {
+ int year, month, day, hour, minute, second;
+ struct tm tm;
+
+ if (sscanf(dateTime, "%04d%02d%02dT%02d%02d%02d",
+ &year, &month, &day, &hour, &minute, &second) != 6)
+ return false;
+ const char* tail = dateTime + 15;
+ // skip optional tenth of second
+ if (tail[0] == '.' && tail[1])
+ tail += 2;
+ //FIXME - support +/-hhmm
+ bool useUTC = (tail[0] == 'Z');
+
+ // hack to compute timezone
+ time_t dummy;
+ localtime_r(&dummy, &tm);
+
+ tm.tm_sec = second;
+ tm.tm_min = minute;
+ tm.tm_hour = hour;
+ tm.tm_mday = day;
+ tm.tm_mon = month - 1; // mktime uses months in 0 - 11 range
+ tm.tm_year = year - 1900;
+ tm.tm_wday = 0;
+ tm.tm_isdst = -1;
+ //if (useUTC) {
+ outSeconds = mktime(&tm);
+ //}
+ /* mktime_tz is blocking :P
+ else {
+ outSeconds = mktime_tz(&tm, tm.tm_zone);
+ }
+ */
+
+ return true;
+}
+
+void formatDateTime(time_t seconds, char* buffer, int bufferLength) {
+ struct tm tm;
+
+ localtime_r(&seconds, &tm);
+ snprintf(buffer, bufferLength, "%04d%02d%02dT%02d%02d%02d",
+ tm.tm_year + 1900,
+ tm.tm_mon + 1, // localtime_r uses months in 0 - 11 range
+ tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
+}
+
diff --git a/mtp/MtpUtils.h b/mtp/MtpUtils.h
new file mode 100644
index 000000000..2bca94b2e
--- /dev/null
+++ b/mtp/MtpUtils.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_UTILS_H
+#define _MTP_UTILS_H
+
+#include <stdint.h>
+
+bool parseDateTime(const char* dateTime, time_t& outSeconds);
+void formatDateTime(time_t seconds, char* buffer, int bufferLength);
+
+#endif // _MTP_UTILS_H
diff --git a/mtp/btree.cpp b/mtp/btree.cpp
new file mode 100644
index 000000000..b73789bd4
--- /dev/null
+++ b/mtp/btree.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/threads.h>
+#include "btree.hpp"
+#include "MtpDebug.h"
+
+// Constructor
+Tree::Tree(MtpObjectHandle handle, MtpObjectHandle parent, const std::string& name)
+ : Node(handle, parent, name), alreadyRead(false) {
+}
+
+// Destructor
+Tree::~Tree() {
+ for (std::map<MtpObjectHandle, Node*>::iterator it = entries.begin(); it != entries.end(); ++it)
+ delete it->second;
+ entries.clear();
+}
+
+int Tree::getCount(void) {
+ int count = entries.size();
+ MTPD("node count: %d\n", count);
+ return count;
+}
+
+void Tree::addEntry(Node* node) {
+ if (node->Mtpid() == 0) {
+ MTPE("Tree::addEntry: not adding node with 0 handle.\n");
+ return;
+ }
+ if (node->Mtpid() == node->getMtpParentId()) {
+ MTPE("Tree::addEntry: not adding node with handle %u == parent.\n", node->Mtpid());
+ return;
+ }
+ entries[node->Mtpid()] = node;
+}
+
+Node* Tree::findEntryByName(std::string name) {
+ for (std::map<MtpObjectHandle, Node*>::iterator it = entries.begin(); it != entries.end(); ++it)
+ {
+ Node* node = it->second;
+ if (node->getName().compare(name) == 0 && node->Mtpid() > 0)
+ return node;
+ }
+ return NULL;
+}
+
+Node* Tree::findNode(MtpObjectHandle handle) {
+ std::map<MtpObjectHandle, Node*>::iterator it = entries.find(handle);
+ if (it != entries.end())
+ return it->second;
+ return NULL;
+}
+
+void Tree::getmtpids(MtpObjectHandleList* mtpids) {
+ for (std::map<MtpObjectHandle, Node*>::iterator it = entries.begin(); it != entries.end(); ++it)
+ mtpids->push_back(it->second->Mtpid());
+}
+
+void Tree::deleteNode(MtpObjectHandle handle) {
+ std::map<MtpObjectHandle, Node*>::iterator it = entries.find(handle);
+ if (it != entries.end()) {
+ delete it->second;
+ entries.erase(it);
+ }
+}
diff --git a/mtp/btree.hpp b/mtp/btree.hpp
new file mode 100644
index 000000000..e1aad3636
--- /dev/null
+++ b/mtp/btree.hpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef BTREE_HPP
+#define BTREE_HPP
+
+#include <vector>
+#include <string>
+#include <map>
+#include "MtpTypes.h"
+
+// A directory entry
+class Node {
+ MtpObjectHandle handle;
+ MtpObjectHandle parent;
+ std::string name; // name only without path
+
+public:
+ Node();
+ Node(MtpObjectHandle handle, MtpObjectHandle parent, const std::string& name);
+ virtual ~Node() {}
+
+ virtual bool isDir() const { return false; }
+
+ void rename(const std::string& newName);
+ MtpObjectHandle Mtpid() const;
+ MtpObjectHandle getMtpParentId() const;
+ const std::string& getName() const;
+
+ void addProperty(MtpPropertyCode property, uint64_t valueInt, std::string valueStr, MtpDataType dataType);
+ void updateProperty(MtpPropertyCode property, uint64_t valueInt, std::string valueStr, MtpDataType dataType);
+ void addProperties(const std::string& path, int storageID);
+ uint64_t getIntProperty(MtpPropertyCode property);
+ struct mtpProperty {
+ MtpPropertyCode property;
+ MtpDataType dataType;
+ uint64_t valueInt;
+ std::string valueStr;
+ mtpProperty() : property(0), dataType(0), valueInt(0) {}
+ };
+ std::vector<mtpProperty>& getMtpProps();
+ std::vector<mtpProperty> mtpProp;
+ const mtpProperty& getProperty(MtpPropertyCode property);
+};
+
+// A directory
+class Tree : public Node {
+ std::map<MtpObjectHandle, Node*> entries;
+ bool alreadyRead;
+public:
+ Tree(MtpObjectHandle handle, MtpObjectHandle parent, const std::string& name);
+ ~Tree();
+
+ virtual bool isDir() const { return true; }
+
+ void addEntry(Node* node);
+ Node* findNode(MtpObjectHandle handle);
+ void getmtpids(MtpObjectHandleList* mtpids);
+ void deleteNode(MtpObjectHandle handle);
+ std::string getPath(Node* node);
+ int getMtpParentId() { return Node::getMtpParentId(); }
+ int getMtpParentId(Node* node);
+ Node* findEntryByName(std::string name);
+ int getCount();
+ bool wasAlreadyRead() const { return alreadyRead; }
+ void setAlreadyRead(bool b) { alreadyRead = b; }
+};
+
+#endif
diff --git a/mtp/mtp.h b/mtp/mtp.h
new file mode 100644
index 000000000..c3293319f
--- /dev/null
+++ b/mtp/mtp.h
@@ -0,0 +1,607 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_H
+#define _MTP_H
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#define MTP_STANDARD_VERSION 100
+
+// Container Types
+#define MTP_CONTAINER_TYPE_UNDEFINED 0
+#define MTP_CONTAINER_TYPE_COMMAND 1
+#define MTP_CONTAINER_TYPE_DATA 2
+#define MTP_CONTAINER_TYPE_RESPONSE 3
+#define MTP_CONTAINER_TYPE_EVENT 4
+
+// Container Offsets
+#define MTP_CONTAINER_LENGTH_OFFSET 0
+#define MTP_CONTAINER_TYPE_OFFSET 4
+#define MTP_CONTAINER_CODE_OFFSET 6
+#define MTP_CONTAINER_TRANSACTION_ID_OFFSET 8
+#define MTP_CONTAINER_PARAMETER_OFFSET 12
+#define MTP_CONTAINER_HEADER_SIZE 12
+
+// MTP Data Types
+#define MTP_TYPE_UNDEFINED 0x0000 // Undefined
+#define MTP_TYPE_INT8 0x0001 // Signed 8-bit integer
+#define MTP_TYPE_UINT8 0x0002 // Unsigned 8-bit integer
+#define MTP_TYPE_INT16 0x0003 // Signed 16-bit integer
+#define MTP_TYPE_UINT16 0x0004 // Unsigned 16-bit integer
+#define MTP_TYPE_INT32 0x0005 // Signed 32-bit integer
+#define MTP_TYPE_UINT32 0x0006 // Unsigned 32-bit integer
+#define MTP_TYPE_INT64 0x0007 // Signed 64-bit integer
+#define MTP_TYPE_UINT64 0x0008 // Unsigned 64-bit integer
+#define MTP_TYPE_INT128 0x0009 // Signed 128-bit integer
+#define MTP_TYPE_UINT128 0x000A // Unsigned 128-bit integer
+#define MTP_TYPE_AINT8 0x4001 // Array of signed 8-bit integers
+#define MTP_TYPE_AUINT8 0x4002 // Array of unsigned 8-bit integers
+#define MTP_TYPE_AINT16 0x4003 // Array of signed 16-bit integers
+#define MTP_TYPE_AUINT16 0x4004 // Array of unsigned 16-bit integers
+#define MTP_TYPE_AINT32 0x4005 // Array of signed 32-bit integers
+#define MTP_TYPE_AUINT32 0x4006 // Array of unsigned 32-bit integers
+#define MTP_TYPE_AINT64 0x4007 // Array of signed 64-bit integers
+#define MTP_TYPE_AUINT64 0x4008 // Array of unsigned 64-bit integers
+#define MTP_TYPE_AINT128 0x4009 // Array of signed 128-bit integers
+#define MTP_TYPE_AUINT128 0x400A // Array of unsigned 128-bit integers
+#define MTP_TYPE_STR 0xFFFF // Variable-length Unicode string
+
+// MTP Format Codes
+#define MTP_FORMAT_UNDEFINED 0x3000 // Undefined object
+#define MTP_FORMAT_ASSOCIATION 0x3001 // Association (for example, a folder)
+#define MTP_FORMAT_SCRIPT 0x3002 // Device model-specific script
+#define MTP_FORMAT_EXECUTABLE 0x3003 // Device model-specific binary executable
+#define MTP_FORMAT_TEXT 0x3004 // Text file
+#define MTP_FORMAT_HTML 0x3005 // Hypertext Markup Language file (text)
+#define MTP_FORMAT_DPOF 0x3006 // Digital Print Order Format file (text)
+#define MTP_FORMAT_AIFF 0x3007 // Audio clip
+#define MTP_FORMAT_WAV 0x3008 // Audio clip
+#define MTP_FORMAT_MP3 0x3009 // Audio clip
+#define MTP_FORMAT_AVI 0x300A // Video clip
+#define MTP_FORMAT_MPEG 0x300B // Video clip
+#define MTP_FORMAT_ASF 0x300C // Microsoft Advanced Streaming Format (video)
+#define MTP_FORMAT_DEFINED 0x3800 // Unknown image object
+#define MTP_FORMAT_EXIF_JPEG 0x3801 // Exchangeable File Format, JEIDA standard
+#define MTP_FORMAT_TIFF_EP 0x3802 // Tag Image File Format for Electronic Photography
+#define MTP_FORMAT_FLASHPIX 0x3803 // Structured Storage Image Format
+#define MTP_FORMAT_BMP 0x3804 // Microsoft Windows Bitmap file
+#define MTP_FORMAT_CIFF 0x3805 // Canon Camera Image File Format
+#define MTP_FORMAT_GIF 0x3807 // Graphics Interchange Format
+#define MTP_FORMAT_JFIF 0x3808 // JPEG File Interchange Format
+#define MTP_FORMAT_CD 0x3809 // PhotoCD Image Pac
+#define MTP_FORMAT_PICT 0x380A // Quickdraw Image Format
+#define MTP_FORMAT_PNG 0x380B // Portable Network Graphics
+#define MTP_FORMAT_TIFF 0x380D // Tag Image File Format
+#define MTP_FORMAT_TIFF_IT 0x380E // Tag Image File Format for Information Technology (graphic arts)
+#define MTP_FORMAT_JP2 0x380F // JPEG2000 Baseline File Format
+#define MTP_FORMAT_JPX 0x3810 // JPEG2000 Extended File Format
+#define MTP_FORMAT_UNDEFINED_FIRMWARE 0xB802
+#define MTP_FORMAT_WINDOWS_IMAGE_FORMAT 0xB881
+#define MTP_FORMAT_UNDEFINED_AUDIO 0xB900
+#define MTP_FORMAT_WMA 0xB901
+#define MTP_FORMAT_OGG 0xB902
+#define MTP_FORMAT_AAC 0xB903
+#define MTP_FORMAT_AUDIBLE 0xB904
+#define MTP_FORMAT_FLAC 0xB906
+#define MTP_FORMAT_UNDEFINED_VIDEO 0xB980
+#define MTP_FORMAT_WMV 0xB981
+#define MTP_FORMAT_MP4_CONTAINER 0xB982 // ISO 14496-1
+#define MTP_FORMAT_MP2 0xB983
+#define MTP_FORMAT_3GP_CONTAINER 0xB984 // 3GPP file format. Details: http://www.3gpp.org/ftp/Specs/html-info/26244.htm (page title - \u201cTransparent end-to-end packet switched streaming service, 3GPP file format\u201d).
+#define MTP_FORMAT_UNDEFINED_COLLECTION 0xBA00
+#define MTP_FORMAT_ABSTRACT_MULTIMEDIA_ALBUM 0xBA01
+#define MTP_FORMAT_ABSTRACT_IMAGE_ALBUM 0xBA02
+#define MTP_FORMAT_ABSTRACT_AUDIO_ALBUM 0xBA03
+#define MTP_FORMAT_ABSTRACT_VIDEO_ALBUM 0xBA04
+#define MTP_FORMAT_ABSTRACT_AV_PLAYLIST 0xBA05
+#define MTP_FORMAT_ABSTRACT_CONTACT_GROUP 0xBA06
+#define MTP_FORMAT_ABSTRACT_MESSAGE_FOLDER 0xBA07
+#define MTP_FORMAT_ABSTRACT_CHAPTERED_PRODUCTION 0xBA08
+#define MTP_FORMAT_ABSTRACT_AUDIO_PLAYLIST 0xBA09
+#define MTP_FORMAT_ABSTRACT_VIDEO_PLAYLIST 0xBA0A
+#define MTP_FORMAT_ABSTRACT_MEDIACAST 0xBA0B // For use with mediacasts; references multimedia enclosures of RSS feeds or episodic content
+#define MTP_FORMAT_WPL_PLAYLIST 0xBA10
+#define MTP_FORMAT_M3U_PLAYLIST 0xBA11
+#define MTP_FORMAT_MPL_PLAYLIST 0xBA12
+#define MTP_FORMAT_ASX_PLAYLIST 0xBA13
+#define MTP_FORMAT_PLS_PLAYLIST 0xBA14
+#define MTP_FORMAT_UNDEFINED_DOCUMENT 0xBA80
+#define MTP_FORMAT_ABSTRACT_DOCUMENT 0xBA81
+#define MTP_FORMAT_XML_DOCUMENT 0xBA82
+#define MTP_FORMAT_MS_WORD_DOCUMENT 0xBA83
+#define MTP_FORMAT_MHT_COMPILED_HTML_DOCUMENT 0xBA84
+#define MTP_FORMAT_MS_EXCEL_SPREADSHEET 0xBA85
+#define MTP_FORMAT_MS_POWERPOINT_PRESENTATION 0xBA86
+#define MTP_FORMAT_UNDEFINED_MESSAGE 0xBB00
+#define MTP_FORMAT_ABSTRACT_MESSSAGE 0xBB01
+#define MTP_FORMAT_UNDEFINED_CONTACT 0xBB80
+#define MTP_FORMAT_ABSTRACT_CONTACT 0xBB81
+#define MTP_FORMAT_VCARD_2 0xBB82
+
+// MTP Object Property Codes
+#define MTP_PROPERTY_STORAGE_ID 0xDC01
+#define MTP_PROPERTY_OBJECT_FORMAT 0xDC02
+#define MTP_PROPERTY_PROTECTION_STATUS 0xDC03
+#define MTP_PROPERTY_OBJECT_SIZE 0xDC04
+#define MTP_PROPERTY_ASSOCIATION_TYPE 0xDC05
+#define MTP_PROPERTY_ASSOCIATION_DESC 0xDC06
+#define MTP_PROPERTY_OBJECT_FILE_NAME 0xDC07
+#define MTP_PROPERTY_DATE_CREATED 0xDC08
+#define MTP_PROPERTY_DATE_MODIFIED 0xDC09
+#define MTP_PROPERTY_KEYWORDS 0xDC0A
+#define MTP_PROPERTY_PARENT_OBJECT 0xDC0B
+#define MTP_PROPERTY_ALLOWED_FOLDER_CONTENTS 0xDC0C
+#define MTP_PROPERTY_HIDDEN 0xDC0D
+#define MTP_PROPERTY_SYSTEM_OBJECT 0xDC0E
+#define MTP_PROPERTY_PERSISTENT_UID 0xDC41
+#define MTP_PROPERTY_SYNC_ID 0xDC42
+#define MTP_PROPERTY_PROPERTY_BAG 0xDC43
+#define MTP_PROPERTY_NAME 0xDC44
+#define MTP_PROPERTY_CREATED_BY 0xDC45
+#define MTP_PROPERTY_ARTIST 0xDC46
+#define MTP_PROPERTY_DATE_AUTHORED 0xDC47
+#define MTP_PROPERTY_DESCRIPTION 0xDC48
+#define MTP_PROPERTY_URL_REFERENCE 0xDC49
+#define MTP_PROPERTY_LANGUAGE_LOCALE 0xDC4A
+#define MTP_PROPERTY_COPYRIGHT_INFORMATION 0xDC4B
+#define MTP_PROPERTY_SOURCE 0xDC4C
+#define MTP_PROPERTY_ORIGIN_LOCATION 0xDC4D
+#define MTP_PROPERTY_DATE_ADDED 0xDC4E
+#define MTP_PROPERTY_NON_CONSUMABLE 0xDC4F
+#define MTP_PROPERTY_CORRUPT_UNPLAYABLE 0xDC50
+#define MTP_PROPERTY_PRODUCER_SERIAL_NUMBER 0xDC51
+#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_FORMAT 0xDC81
+#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_SIZE 0xDC82
+#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_HEIGHT 0xDC83
+#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_WIDTH 0xDC84
+#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DURATION 0xDC85
+#define MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DATA 0xDC86
+#define MTP_PROPERTY_WIDTH 0xDC87
+#define MTP_PROPERTY_HEIGHT 0xDC88
+#define MTP_PROPERTY_DURATION 0xDC89
+#define MTP_PROPERTY_RATING 0xDC8A
+#define MTP_PROPERTY_TRACK 0xDC8B
+#define MTP_PROPERTY_GENRE 0xDC8C
+#define MTP_PROPERTY_CREDITS 0xDC8D
+#define MTP_PROPERTY_LYRICS 0xDC8E
+#define MTP_PROPERTY_SUBSCRIPTION_CONTENT_ID 0xDC8F
+#define MTP_PROPERTY_PRODUCED_BY 0xDC90
+#define MTP_PROPERTY_USE_COUNT 0xDC91
+#define MTP_PROPERTY_SKIP_COUNT 0xDC92
+#define MTP_PROPERTY_LAST_ACCESSED 0xDC93
+#define MTP_PROPERTY_PARENTAL_RATING 0xDC94
+#define MTP_PROPERTY_META_GENRE 0xDC95
+#define MTP_PROPERTY_COMPOSER 0xDC96
+#define MTP_PROPERTY_EFFECTIVE_RATING 0xDC97
+#define MTP_PROPERTY_SUBTITLE 0xDC98
+#define MTP_PROPERTY_ORIGINAL_RELEASE_DATE 0xDC99
+#define MTP_PROPERTY_ALBUM_NAME 0xDC9A
+#define MTP_PROPERTY_ALBUM_ARTIST 0xDC9B
+#define MTP_PROPERTY_MOOD 0xDC9C
+#define MTP_PROPERTY_DRM_STATUS 0xDC9D
+#define MTP_PROPERTY_SUB_DESCRIPTION 0xDC9E
+#define MTP_PROPERTY_IS_CROPPED 0xDCD1
+#define MTP_PROPERTY_IS_COLOUR_CORRECTED 0xDCD2
+#define MTP_PROPERTY_IMAGE_BIT_DEPTH 0xDCD3
+#define MTP_PROPERTY_F_NUMBER 0xDCD4
+#define MTP_PROPERTY_EXPOSURE_TIME 0xDCD5
+#define MTP_PROPERTY_EXPOSURE_INDEX 0xDCD6
+#define MTP_PROPERTY_TOTAL_BITRATE 0xDE91
+#define MTP_PROPERTY_BITRATE_TYPE 0xDE92
+#define MTP_PROPERTY_SAMPLE_RATE 0xDE93
+#define MTP_PROPERTY_NUMBER_OF_CHANNELS 0xDE94
+#define MTP_PROPERTY_AUDIO_BIT_DEPTH 0xDE95
+#define MTP_PROPERTY_SCAN_TYPE 0xDE97
+#define MTP_PROPERTY_AUDIO_WAVE_CODEC 0xDE99
+#define MTP_PROPERTY_AUDIO_BITRATE 0xDE9A
+#define MTP_PROPERTY_VIDEO_FOURCC_CODEC 0xDE9B
+#define MTP_PROPERTY_VIDEO_BITRATE 0xDE9C
+#define MTP_PROPERTY_FRAMES_PER_THOUSAND_SECONDS 0xDE9D
+#define MTP_PROPERTY_KEYFRAME_DISTANCE 0xDE9E
+#define MTP_PROPERTY_BUFFER_SIZE 0xDE9F
+#define MTP_PROPERTY_ENCODING_QUALITY 0xDEA0
+#define MTP_PROPERTY_ENCODING_PROFILE 0xDEA1
+#define MTP_PROPERTY_DISPLAY_NAME 0xDCE0
+#define MTP_PROPERTY_BODY_TEXT 0xDCE1
+#define MTP_PROPERTY_SUBJECT 0xDCE2
+#define MTP_PROPERTY_PRIORITY 0xDCE3
+#define MTP_PROPERTY_GIVEN_NAME 0xDD00
+#define MTP_PROPERTY_MIDDLE_NAMES 0xDD01
+#define MTP_PROPERTY_FAMILY_NAME 0xDD02
+#define MTP_PROPERTY_PREFIX 0xDD03
+#define MTP_PROPERTY_SUFFIX 0xDD04
+#define MTP_PROPERTY_PHONETIC_GIVEN_NAME 0xDD05
+#define MTP_PROPERTY_PHONETIC_FAMILY_NAME 0xDD06
+#define MTP_PROPERTY_EMAIL_PRIMARY 0xDD07
+#define MTP_PROPERTY_EMAIL_PERSONAL_1 0xDD08
+#define MTP_PROPERTY_EMAIL_PERSONAL_2 0xDD09
+#define MTP_PROPERTY_EMAIL_BUSINESS_1 0xDD0A
+#define MTP_PROPERTY_EMAIL_BUSINESS_2 0xDD0B
+#define MTP_PROPERTY_EMAIL_OTHERS 0xDD0C
+#define MTP_PROPERTY_PHONE_NUMBER_PRIMARY 0xDD0D
+#define MTP_PROPERTY_PHONE_NUMBER_PERSONAL 0xDD0E
+#define MTP_PROPERTY_PHONE_NUMBER_PERSONAL_2 0xDD0F
+#define MTP_PROPERTY_PHONE_NUMBER_BUSINESS 0xDD10
+#define MTP_PROPERTY_PHONE_NUMBER_BUSINESS_2 0xDD11
+#define MTP_PROPERTY_PHONE_NUMBER_MOBILE 0xDD12
+#define MTP_PROPERTY_PHONE_NUMBER_MOBILE_2 0xDD13
+#define MTP_PROPERTY_FAX_NUMBER_PRIMARY 0xDD14
+#define MTP_PROPERTY_FAX_NUMBER_PERSONAL 0xDD15
+#define MTP_PROPERTY_FAX_NUMBER_BUSINESS 0xDD16
+#define MTP_PROPERTY_PAGER_NUMBER 0xDD17
+#define MTP_PROPERTY_PHONE_NUMBER_OTHERS 0xDD18
+#define MTP_PROPERTY_PRIMARY_WEB_ADDRESS 0xDD19
+#define MTP_PROPERTY_PERSONAL_WEB_ADDRESS 0xDD1A
+#define MTP_PROPERTY_BUSINESS_WEB_ADDRESS 0xDD1B
+#define MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS 0xDD1C
+#define MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_2 0xDD1D
+#define MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_3 0xDD1E
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_FULL 0xDD1F
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_1 0xDD20
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_2 0xDD21
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_CITY 0xDD22
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_REGION 0xDD23
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_POSTAL_CODE 0xDD24
+#define MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_COUNTRY 0xDD25
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_FULL 0xDD26
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_1 0xDD27
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_2 0xDD28
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_CITY 0xDD29
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_REGION 0xDD2A
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_POSTAL_CODE 0xDD2B
+#define MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_COUNTRY 0xDD2C
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_FULL 0xDD2D
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_1 0xDD2E
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_2 0xDD2F
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_CITY 0xDD30
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_REGION 0xDD31
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_POSTAL_CODE 0xDD32
+#define MTP_PROPERTY_POSTAL_ADDRESS_OTHER_COUNTRY 0xDD33
+#define MTP_PROPERTY_ORGANIZATION_NAME 0xDD34
+#define MTP_PROPERTY_PHONETIC_ORGANIZATION_NAME 0xDD35
+#define MTP_PROPERTY_ROLE 0xDD36
+#define MTP_PROPERTY_BIRTHDATE 0xDD37
+#define MTP_PROPERTY_MESSAGE_TO 0xDD40
+#define MTP_PROPERTY_MESSAGE_CC 0xDD41
+#define MTP_PROPERTY_MESSAGE_BCC 0xDD42
+#define MTP_PROPERTY_MESSAGE_READ 0xDD43
+#define MTP_PROPERTY_MESSAGE_RECEIVED_TIME 0xDD44
+#define MTP_PROPERTY_MESSAGE_SENDER 0xDD45
+#define MTP_PROPERTY_ACTIVITY_BEGIN_TIME 0xDD50
+#define MTP_PROPERTY_ACTIVITY_END_TIME 0xDD51
+#define MTP_PROPERTY_ACTIVITY_LOCATION 0xDD52
+#define MTP_PROPERTY_ACTIVITY_REQUIRED_ATTENDEES 0xDD54
+#define MTP_PROPERTY_ACTIVITY_OPTIONAL_ATTENDEES 0xDD55
+#define MTP_PROPERTY_ACTIVITY_RESOURCES 0xDD56
+#define MTP_PROPERTY_ACTIVITY_ACCEPTED 0xDD57
+#define MTP_PROPERTY_ACTIVITY_TENTATIVE 0xDD58
+#define MTP_PROPERTY_ACTIVITY_DECLINED 0xDD59
+#define MTP_PROPERTY_ACTIVITY_REMAINDER_TIME 0xDD5A
+#define MTP_PROPERTY_ACTIVITY_OWNER 0xDD5B
+#define MTP_PROPERTY_ACTIVITY_STATUS 0xDD5C
+#define MTP_PROPERTY_OWNER 0xDD5D
+#define MTP_PROPERTY_EDITOR 0xDD5E
+#define MTP_PROPERTY_WEBMASTER 0xDD5F
+#define MTP_PROPERTY_URL_SOURCE 0xDD60
+#define MTP_PROPERTY_URL_DESTINATION 0xDD61
+#define MTP_PROPERTY_TIME_BOOKMARK 0xDD62
+#define MTP_PROPERTY_OBJECT_BOOKMARK 0xDD63
+#define MTP_PROPERTY_BYTE_BOOKMARK 0xDD64
+#define MTP_PROPERTY_LAST_BUILD_DATE 0xDD70
+#define MTP_PROPERTY_TIME_TO_LIVE 0xDD71
+#define MTP_PROPERTY_MEDIA_GUID 0xDD72
+
+// MTP Device Property Codes
+#define MTP_DEVICE_PROPERTY_UNDEFINED 0x5000
+#define MTP_DEVICE_PROPERTY_BATTERY_LEVEL 0x5001
+#define MTP_DEVICE_PROPERTY_FUNCTIONAL_MODE 0x5002
+#define MTP_DEVICE_PROPERTY_IMAGE_SIZE 0x5003
+#define MTP_DEVICE_PROPERTY_COMPRESSION_SETTING 0x5004
+#define MTP_DEVICE_PROPERTY_WHITE_BALANCE 0x5005
+#define MTP_DEVICE_PROPERTY_RGB_GAIN 0x5006
+#define MTP_DEVICE_PROPERTY_F_NUMBER 0x5007
+#define MTP_DEVICE_PROPERTY_FOCAL_LENGTH 0x5008
+#define MTP_DEVICE_PROPERTY_FOCUS_DISTANCE 0x5009
+#define MTP_DEVICE_PROPERTY_FOCUS_MODE 0x500A
+#define MTP_DEVICE_PROPERTY_EXPOSURE_METERING_MODE 0x500B
+#define MTP_DEVICE_PROPERTY_FLASH_MODE 0x500C
+#define MTP_DEVICE_PROPERTY_EXPOSURE_TIME 0x500D
+#define MTP_DEVICE_PROPERTY_EXPOSURE_PROGRAM_MODE 0x500E
+#define MTP_DEVICE_PROPERTY_EXPOSURE_INDEX 0x500F
+#define MTP_DEVICE_PROPERTY_EXPOSURE_BIAS_COMPENSATION 0x5010
+#define MTP_DEVICE_PROPERTY_DATETIME 0x5011
+#define MTP_DEVICE_PROPERTY_CAPTURE_DELAY 0x5012
+#define MTP_DEVICE_PROPERTY_STILL_CAPTURE_MODE 0x5013
+#define MTP_DEVICE_PROPERTY_CONTRAST 0x5014
+#define MTP_DEVICE_PROPERTY_SHARPNESS 0x5015
+#define MTP_DEVICE_PROPERTY_DIGITAL_ZOOM 0x5016
+#define MTP_DEVICE_PROPERTY_EFFECT_MODE 0x5017
+#define MTP_DEVICE_PROPERTY_BURST_NUMBER 0x5018
+#define MTP_DEVICE_PROPERTY_BURST_INTERVAL 0x5019
+#define MTP_DEVICE_PROPERTY_TIMELAPSE_NUMBER 0x501A
+#define MTP_DEVICE_PROPERTY_TIMELAPSE_INTERVAL 0x501B
+#define MTP_DEVICE_PROPERTY_FOCUS_METERING_MODE 0x501C
+#define MTP_DEVICE_PROPERTY_UPLOAD_URL 0x501D
+#define MTP_DEVICE_PROPERTY_ARTIST 0x501E
+#define MTP_DEVICE_PROPERTY_COPYRIGHT_INFO 0x501F
+#define MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER 0xD401
+#define MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME 0xD402
+#define MTP_DEVICE_PROPERTY_VOLUME 0xD403
+#define MTP_DEVICE_PROPERTY_SUPPORTED_FORMATS_ORDERED 0xD404
+#define MTP_DEVICE_PROPERTY_DEVICE_ICON 0xD405
+#define MTP_DEVICE_PROPERTY_PLAYBACK_RATE 0xD410
+#define MTP_DEVICE_PROPERTY_PLAYBACK_OBJECT 0xD411
+#define MTP_DEVICE_PROPERTY_PLAYBACK_CONTAINER_INDEX 0xD412
+#define MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO 0xD406
+#define MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE 0xD407
+
+// MTP Operation Codes
+#define MTP_OPERATION_GET_DEVICE_INFO 0x1001
+#define MTP_OPERATION_OPEN_SESSION 0x1002
+#define MTP_OPERATION_CLOSE_SESSION 0x1003
+#define MTP_OPERATION_GET_STORAGE_IDS 0x1004
+#define MTP_OPERATION_GET_STORAGE_INFO 0x1005
+#define MTP_OPERATION_GET_NUM_OBJECTS 0x1006
+#define MTP_OPERATION_GET_OBJECT_HANDLES 0x1007
+#define MTP_OPERATION_GET_OBJECT_INFO 0x1008
+#define MTP_OPERATION_GET_OBJECT 0x1009
+#define MTP_OPERATION_GET_THUMB 0x100A
+#define MTP_OPERATION_DELETE_OBJECT 0x100B
+#define MTP_OPERATION_SEND_OBJECT_INFO 0x100C
+#define MTP_OPERATION_SEND_OBJECT 0x100D
+#define MTP_OPERATION_INITIATE_CAPTURE 0x100E
+#define MTP_OPERATION_FORMAT_STORE 0x100F
+#define MTP_OPERATION_RESET_DEVICE 0x1010
+#define MTP_OPERATION_SELF_TEST 0x1011
+#define MTP_OPERATION_SET_OBJECT_PROTECTION 0x1012
+#define MTP_OPERATION_POWER_DOWN 0x1013
+#define MTP_OPERATION_GET_DEVICE_PROP_DESC 0x1014
+#define MTP_OPERATION_GET_DEVICE_PROP_VALUE 0x1015
+#define MTP_OPERATION_SET_DEVICE_PROP_VALUE 0x1016
+#define MTP_OPERATION_RESET_DEVICE_PROP_VALUE 0x1017
+#define MTP_OPERATION_TERMINATE_OPEN_CAPTURE 0x1018
+#define MTP_OPERATION_MOVE_OBJECT 0x1019
+#define MTP_OPERATION_COPY_OBJECT 0x101A
+#define MTP_OPERATION_GET_PARTIAL_OBJECT 0x101B
+#define MTP_OPERATION_INITIATE_OPEN_CAPTURE 0x101C
+#define MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED 0x9801
+#define MTP_OPERATION_GET_OBJECT_PROP_DESC 0x9802
+#define MTP_OPERATION_GET_OBJECT_PROP_VALUE 0x9803
+#define MTP_OPERATION_SET_OBJECT_PROP_VALUE 0x9804
+#define MTP_OPERATION_GET_OBJECT_PROP_LIST 0x9805
+#define MTP_OPERATION_SET_OBJECT_PROP_LIST 0x9806
+#define MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC 0x9807
+#define MTP_OPERATION_SEND_OBJECT_PROP_LIST 0x9808
+#define MTP_OPERATION_GET_OBJECT_REFERENCES 0x9810
+#define MTP_OPERATION_SET_OBJECT_REFERENCES 0x9811
+#define MTP_OPERATION_SKIP 0x9820
+
+// Android extensions for direct file IO
+
+// Same as GetPartialObject, but with 64 bit offset
+#define MTP_OPERATION_GET_PARTIAL_OBJECT_64 0x95C1
+// Same as GetPartialObject64, but copying host to device
+#define MTP_OPERATION_SEND_PARTIAL_OBJECT 0x95C2
+// Truncates file to 64 bit length
+#define MTP_OPERATION_TRUNCATE_OBJECT 0x95C3
+// Must be called before using SendPartialObject and TruncateObject
+#define MTP_OPERATION_BEGIN_EDIT_OBJECT 0x95C4
+// Called to commit changes made by SendPartialObject and TruncateObject
+#define MTP_OPERATION_END_EDIT_OBJECT 0x95C5
+
+// MTP Response Codes
+#define MTP_RESPONSE_UNDEFINED 0x2000
+#define MTP_RESPONSE_OK 0x2001
+#define MTP_RESPONSE_GENERAL_ERROR 0x2002
+#define MTP_RESPONSE_SESSION_NOT_OPEN 0x2003
+#define MTP_RESPONSE_INVALID_TRANSACTION_ID 0x2004
+#define MTP_RESPONSE_OPERATION_NOT_SUPPORTED 0x2005
+#define MTP_RESPONSE_PARAMETER_NOT_SUPPORTED 0x2006
+#define MTP_RESPONSE_INCOMPLETE_TRANSFER 0x2007
+#define MTP_RESPONSE_INVALID_STORAGE_ID 0x2008
+#define MTP_RESPONSE_INVALID_OBJECT_HANDLE 0x2009
+#define MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED 0x200A
+#define MTP_RESPONSE_INVALID_OBJECT_FORMAT_CODE 0x200B
+#define MTP_RESPONSE_STORAGE_FULL 0x200C
+#define MTP_RESPONSE_OBJECT_WRITE_PROTECTED 0x200D
+#define MTP_RESPONSE_STORE_READ_ONLY 0x200E
+#define MTP_RESPONSE_ACCESS_DENIED 0x200F
+#define MTP_RESPONSE_NO_THUMBNAIL_PRESENT 0x2010
+#define MTP_RESPONSE_SELF_TEST_FAILED 0x2011
+#define MTP_RESPONSE_PARTIAL_DELETION 0x2012
+#define MTP_RESPONSE_STORE_NOT_AVAILABLE 0x2013
+#define MTP_RESPONSE_SPECIFICATION_BY_FORMAT_UNSUPPORTED 0x2014
+#define MTP_RESPONSE_NO_VALID_OBJECT_INFO 0x2015
+#define MTP_RESPONSE_INVALID_CODE_FORMAT 0x2016
+#define MTP_RESPONSE_UNKNOWN_VENDOR_CODE 0x2017
+#define MTP_RESPONSE_CAPTURE_ALREADY_TERMINATED 0x2018
+#define MTP_RESPONSE_DEVICE_BUSY 0x2019
+#define MTP_RESPONSE_INVALID_PARENT_OBJECT 0x201A
+#define MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT 0x201B
+#define MTP_RESPONSE_INVALID_DEVICE_PROP_VALUE 0x201C
+#define MTP_RESPONSE_INVALID_PARAMETER 0x201D
+#define MTP_RESPONSE_SESSION_ALREADY_OPEN 0x201E
+#define MTP_RESPONSE_TRANSACTION_CANCELLED 0x201F
+#define MTP_RESPONSE_SPECIFICATION_OF_DESTINATION_UNSUPPORTED 0x2020
+#define MTP_RESPONSE_INVALID_OBJECT_PROP_CODE 0xA801
+#define MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT 0xA802
+#define MTP_RESPONSE_INVALID_OBJECT_PROP_VALUE 0xA803
+#define MTP_RESPONSE_INVALID_OBJECT_REFERENCE 0xA804
+#define MTP_RESPONSE_GROUP_NOT_SUPPORTED 0xA805
+#define MTP_RESPONSE_INVALID_DATASET 0xA806
+#define MTP_RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED 0xA807
+#define MTP_RESPONSE_SPECIFICATION_BY_DEPTH_UNSUPPORTED 0xA808
+#define MTP_RESPONSE_OBJECT_TOO_LARGE 0xA809
+#define MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED 0xA80A
+
+// MTP Event Codes
+#define MTP_EVENT_UNDEFINED 0x4000
+#define MTP_EVENT_CANCEL_TRANSACTION 0x4001
+#define MTP_EVENT_OBJECT_ADDED 0x4002
+#define MTP_EVENT_OBJECT_REMOVED 0x4003
+#define MTP_EVENT_STORE_ADDED 0x4004
+#define MTP_EVENT_STORE_REMOVED 0x4005
+#define MTP_EVENT_DEVICE_PROP_CHANGED 0x4006
+#define MTP_EVENT_OBJECT_INFO_CHANGED 0x4007
+#define MTP_EVENT_DEVICE_INFO_CHANGED 0x4008
+#define MTP_EVENT_REQUEST_OBJECT_TRANSFER 0x4009
+#define MTP_EVENT_STORE_FULL 0x400A
+#define MTP_EVENT_DEVICE_RESET 0x400B
+#define MTP_EVENT_STORAGE_INFO_CHANGED 0x400C
+#define MTP_EVENT_CAPTURE_COMPLETE 0x400D
+#define MTP_EVENT_UNREPORTED_STATUS 0x400E
+#define MTP_EVENT_OBJECT_PROP_CHANGED 0xC801
+#define MTP_EVENT_OBJECT_PROP_DESC_CHANGED 0xC802
+#define MTP_EVENT_OBJECT_REFERENCES_CHANGED 0xC803
+
+// Storage Type
+#define MTP_STORAGE_FIXED_ROM 0x0001
+#define MTP_STORAGE_REMOVABLE_ROM 0x0002
+#define MTP_STORAGE_FIXED_RAM 0x0003
+#define MTP_STORAGE_REMOVABLE_RAM 0x0004
+
+// Storage File System
+#define MTP_STORAGE_FILESYSTEM_FLAT 0x0001
+#define MTP_STORAGE_FILESYSTEM_HIERARCHICAL 0x0002
+#define MTP_STORAGE_FILESYSTEM_DCF 0x0003
+
+// Storage Access Capability
+#define MTP_STORAGE_READ_WRITE 0x0000
+#define MTP_STORAGE_READ_ONLY_WITHOUT_DELETE 0x0001
+#define MTP_STORAGE_READ_ONLY_WITH_DELETE 0x0002
+
+// Association Type
+#define MTP_ASSOCIATION_TYPE_UNDEFINED 0x0000
+#define MTP_ASSOCIATION_TYPE_GENERIC_FOLDER 0x0001
+
+// Supported Playback Formats
+#define SUPPORTED_PLAYBACK_FORMAT_UNDEFINED 0x3000
+/** Format code for associations (folders and directories) */
+#define SUPPORTED_PLAYBACK_FORMAT_ASSOCIATION 0x3001
+/** Format code for script files */
+#define SUPPORTED_PLAYBACK_FORMAT_SCRIPT 0x3002
+/** Format code for executable files */
+#define SUPPORTED_PLAYBACK_FORMAT_EXECUTABLE 0x3003
+/** Format code for text files */
+#define SUPPORTED_PLAYBACK_FORMAT_TEXT 0x3004
+/** Format code for HTML files */
+#define SUPPORTED_PLAYBACK_FORMAT_HTML 0x3005
+/** Format code for DPOF files */
+#define SUPPORTED_PLAYBACK_FORMAT_DPOF 0x3006
+/** Format code for AIFF audio files */
+#define SUPPORTED_PLAYBACK_FORMAT_AIFF 0x3007
+/** Format code for WAV audio files */
+#define SUPPORTED_PLAYBACK_FORMAT_WAV 0x3008
+/** Format code for MP3 audio files */
+#define SUPPORTED_PLAYBACK_FORMAT_MP3 0x3009
+/** Format code for AVI video files */
+#define SUPPORTED_PLAYBACK_FORMAT_AVI 0x300A
+/** Format code for MPEG video files */
+#define SUPPORTED_PLAYBACK_FORMAT_MPEG 0x300B
+/** Format code for ASF files */
+#define SUPPORTED_PLAYBACK_FORMAT_ASF 0x300C
+/** Format code for JPEG image files */
+#define SUPPORTED_PLAYBACK_FORMAT_EXIF_JPEG 0x3801
+/** Format code for TIFF EP image files */
+#define SUPPORTED_PLAYBACK_FORMAT_TIFF_EP 0x3802
+/** Format code for BMP image files */
+#define SUPPORTED_PLAYBACK_FORMAT_BMP 0x3804
+/** Format code for GIF image files */
+#define SUPPORTED_PLAYBACK_FORMAT_GIF 0x3807
+/** Format code for JFIF image files */
+#define SUPPORTED_PLAYBACK_FORMAT_JFIF 0x3808
+/** Format code for PICT image files */
+#define SUPPORTED_PLAYBACK_FORMAT_PICT 0x380A
+/** Format code for PNG image files */
+#define SUPPORTED_PLAYBACK_FORMAT_PNG 0x380B
+/** Format code for TIFF image files */
+#define SUPPORTED_PLAYBACK_FORMAT_TIFF 0x380D
+/** Format code for JP2 files */
+#define SUPPORTED_PLAYBACK_FORMAT_JP2 0x380F
+/** Format code for JPX files */
+#define SUPPORTED_PLAYBACK_FORMAT_JPX 0x3810
+/** Format code for firmware files */
+#define SUPPORTED_PLAYBACK_FORMAT_UNDEFINED_FIRMWARE 0xB802
+/** Format code for Windows image files */
+#define SUPPORTED_PLAYBACK_FORMAT_WINDOWS_IMAGE_FORMAT 0xB881
+/** Format code for undefined audio files files */
+#define SUPPORTED_PLAYBACK_FORMAT_UNDEFINED_AUDIO 0xB900
+/** Format code for WMA audio files */
+#define SUPPORTED_PLAYBACK_FORMAT_WMA 0xB901
+/** Format code for OGG audio files */
+#define SUPPORTED_PLAYBACK_FORMAT_OGG 0xB902
+/** Format code for AAC audio files */
+#define SUPPORTED_PLAYBACK_FORMAT_AAC 0xB903
+/** Format code for Audible audio files */
+#define SUPPORTED_PLAYBACK_FORMAT_AUDIBLE 0xB904
+/** Format code for FLAC audio files */
+#define SUPPORTED_PLAYBACK_FORMAT_FLAC 0xB906
+/** Format code for undefined video files */
+#define SUPPORTED_PLAYBACK_FORMAT_UNDEFINED_VIDEO 0xB980
+/** Format code for WMV video files */
+#define SUPPORTED_PLAYBACK_FORMAT_WMV 0xB981
+/** Format code for MP4 files */
+#define SUPPORTED_PLAYBACK_FORMAT_MP4_CONTAINER 0xB982
+/** Format code for MP2 files */
+#define SUPPORTED_PLAYBACK_FORMAT_MP2 0xB983
+/** Format code for 3GP files */
+#define SUPPORTED_PLAYBACK_FORMAT_3GP_CONTAINER 0xB984
+/** Format code for undefined collections */
+#define SUPPORTED_PLAYBACK_FORMAT_UNDEFINED_COLLECTION 0xBA00
+/** Format code for multimedia albums */
+#define SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_MULTIMEDIA_ALBUM 0xBA01
+/** Format code for image albums */
+#define SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_IMAGE_ALBUM 0xBA02
+/** Format code for audio albums */
+#define SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_AUDIO_ALBUM 0xBA03
+/** Format code for video albums */
+#define SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_VIDEO_ALBUM 0xBA04
+/** Format code for abstract AV playlists */
+#define SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_AV_PLAYLIST 0xBA05
+/** Format code for abstract audio playlists */
+#define SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_AUDIO_PLAYLIST 0xBA09
+/** Format code for abstract video playlists */
+#define SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_VIDEO_PLAYLIST 0xBA0A
+/** Format code for abstract mediacasts */
+#define SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_MEDIACAST 0xBA0B
+/** Format code for WPL playlist files */
+#define SUPPORTED_PLAYBACK_FORMAT_WPL_PLAYLIST 0xBA10
+/** Format code for M3u playlist files */
+#define SUPPORTED_PLAYBACK_FORMAT_M3U_PLAYLIST 0xBA11
+/** Format code for MPL playlist files */
+#define SUPPORTED_PLAYBACK_FORMAT_MPL_PLAYLIST 0xBA12
+/** Format code for ASX playlist files */
+#define SUPPORTED_PLAYBACK_FORMAT_ASX_PLAYLIST 0xBA13
+/** Format code for PLS playlist files */
+#define SUPPORTED_PLAYBACK_FORMAT_PLS_PLAYLIST 0xBA14
+/** Format code for undefined document files */
+#define SUPPORTED_PLAYBACK_FORMAT_UNDEFINED_DOCUMENT 0xBA80
+/** Format code for abstract documents */
+#define SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_DOCUMENT 0xBA81
+/** Format code for XML documents */
+#define SUPPORTED_PLAYBACK_FORMAT_XML_DOCUMENT 0xBA82
+/** Format code for MS Word documents */
+#define SUPPORTED_PLAYBACK_FORMAT_MS_WORD_DOCUMENT 0xBA83
+/** Format code for MS Excel spreadsheets */
+#define SUPPORTED_PLAYBACK_FORMAT_MS_EXCEL_SPREADSHEET 0xBA85
+/** Format code for MS PowerPoint presentatiosn */
+#define SUPPORTED_PLAYBACK_FORMAT_MS_POWERPOINT_PRESENTATION 0xBA86
+
+#endif // _MTP_H
diff --git a/mtp/mtp_MtpDatabase.cpp b/mtp/mtp_MtpDatabase.cpp
new file mode 100644
index 000000000..5eb7d8e61
--- /dev/null
+++ b/mtp/mtp_MtpDatabase.cpp
@@ -0,0 +1,871 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include <utils/Log.h>
+
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string>
+#include <map>
+#include <libgen.h>
+#include <cutils/properties.h>
+
+#include "MtpDatabase.h"
+#include "MtpStorage.h"
+#include "MtpDataPacket.h"
+#include "MtpObjectInfo.h"
+#include "MtpProperty.h"
+#include "MtpDebug.h"
+#include "MtpStringBuffer.h"
+#include "MtpUtils.h"
+#include "mtp.h"
+#include "mtp_MtpDatabase.hpp"
+//#include "btree.hpp"
+
+MyMtpDatabase::MyMtpDatabase()
+{
+ storagenum = 0;
+ count = -1;
+}
+
+MyMtpDatabase::~MyMtpDatabase() {
+ std::map<int, MtpStorage*>::iterator i;
+ for (i = storagemap.begin(); i != storagemap.end(); i++) {
+ delete i->second;
+ }
+}
+
+int MyMtpDatabase::DEVICE_PROPERTIES[3] = {
+ MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,
+ MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,
+ MTP_DEVICE_PROPERTY_IMAGE_SIZE
+};
+
+int MyMtpDatabase::FILE_PROPERTIES[10] = {
+ // NOTE must match beginning of AUDIO_PROPERTIES, VIDEO_PROPERTIES
+ // and IMAGE_PROPERTIES below
+ MTP_PROPERTY_STORAGE_ID,
+ MTP_PROPERTY_OBJECT_FORMAT,
+ MTP_PROPERTY_PROTECTION_STATUS,
+ MTP_PROPERTY_OBJECT_SIZE,
+ MTP_PROPERTY_OBJECT_FILE_NAME,
+ MTP_PROPERTY_DATE_MODIFIED,
+ MTP_PROPERTY_PARENT_OBJECT,
+ MTP_PROPERTY_PERSISTENT_UID,
+ MTP_PROPERTY_NAME,
+ // TODO: why is DISPLAY_NAME not here?
+ MTP_PROPERTY_DATE_ADDED
+};
+
+int MyMtpDatabase::AUDIO_PROPERTIES[19] = {
+ // NOTE must match FILE_PROPERTIES above
+ MTP_PROPERTY_STORAGE_ID,
+ MTP_PROPERTY_OBJECT_FORMAT,
+ MTP_PROPERTY_PROTECTION_STATUS,
+ MTP_PROPERTY_OBJECT_SIZE,
+ MTP_PROPERTY_OBJECT_FILE_NAME,
+ MTP_PROPERTY_DATE_MODIFIED,
+ MTP_PROPERTY_PARENT_OBJECT,
+ MTP_PROPERTY_PERSISTENT_UID,
+ MTP_PROPERTY_NAME,
+ MTP_PROPERTY_DISPLAY_NAME,
+ MTP_PROPERTY_DATE_ADDED,
+
+ // audio specific properties
+ MTP_PROPERTY_ARTIST,
+ MTP_PROPERTY_ALBUM_NAME,
+ MTP_PROPERTY_ALBUM_ARTIST,
+ MTP_PROPERTY_TRACK,
+ MTP_PROPERTY_ORIGINAL_RELEASE_DATE,
+ MTP_PROPERTY_DURATION,
+ MTP_PROPERTY_GENRE,
+ MTP_PROPERTY_COMPOSER
+};
+
+int MyMtpDatabase::VIDEO_PROPERTIES[15] = {
+ // NOTE must match FILE_PROPERTIES above
+ MTP_PROPERTY_STORAGE_ID,
+ MTP_PROPERTY_OBJECT_FORMAT,
+ MTP_PROPERTY_PROTECTION_STATUS,
+ MTP_PROPERTY_OBJECT_SIZE,
+ MTP_PROPERTY_OBJECT_FILE_NAME,
+ MTP_PROPERTY_DATE_MODIFIED,
+ MTP_PROPERTY_PARENT_OBJECT,
+ MTP_PROPERTY_PERSISTENT_UID,
+ MTP_PROPERTY_NAME,
+ MTP_PROPERTY_DISPLAY_NAME,
+ MTP_PROPERTY_DATE_ADDED,
+
+ // video specific properties
+ MTP_PROPERTY_ARTIST,
+ MTP_PROPERTY_ALBUM_NAME,
+ MTP_PROPERTY_DURATION,
+ MTP_PROPERTY_DESCRIPTION
+};
+
+int MyMtpDatabase::IMAGE_PROPERTIES[12] = {
+ // NOTE must match FILE_PROPERTIES above
+ MTP_PROPERTY_STORAGE_ID,
+ MTP_PROPERTY_OBJECT_FORMAT,
+ MTP_PROPERTY_PROTECTION_STATUS,
+ MTP_PROPERTY_OBJECT_SIZE,
+ MTP_PROPERTY_OBJECT_FILE_NAME,
+ MTP_PROPERTY_DATE_MODIFIED,
+ MTP_PROPERTY_PARENT_OBJECT,
+ MTP_PROPERTY_PERSISTENT_UID,
+ MTP_PROPERTY_NAME,
+ MTP_PROPERTY_DISPLAY_NAME,
+ MTP_PROPERTY_DATE_ADDED,
+
+ // image specific properties
+ MTP_PROPERTY_DESCRIPTION
+};
+
+int MyMtpDatabase::ALL_PROPERTIES[25] = {
+ // NOTE must match FILE_PROPERTIES above
+ MTP_PROPERTY_STORAGE_ID,
+ MTP_PROPERTY_OBJECT_FORMAT,
+ MTP_PROPERTY_PROTECTION_STATUS,
+ MTP_PROPERTY_OBJECT_SIZE,
+ MTP_PROPERTY_OBJECT_FILE_NAME,
+ MTP_PROPERTY_DATE_MODIFIED,
+ MTP_PROPERTY_PARENT_OBJECT,
+ MTP_PROPERTY_PERSISTENT_UID,
+ MTP_PROPERTY_NAME,
+ MTP_PROPERTY_DISPLAY_NAME,
+ MTP_PROPERTY_DATE_ADDED,
+
+ // image specific properties
+ MTP_PROPERTY_DESCRIPTION,
+
+ // audio specific properties
+ MTP_PROPERTY_ARTIST,
+ MTP_PROPERTY_ALBUM_NAME,
+ MTP_PROPERTY_ALBUM_ARTIST,
+ MTP_PROPERTY_TRACK,
+ MTP_PROPERTY_ORIGINAL_RELEASE_DATE,
+ MTP_PROPERTY_DURATION,
+ MTP_PROPERTY_GENRE,
+ MTP_PROPERTY_COMPOSER,
+
+ // video specific properties
+ MTP_PROPERTY_ARTIST,
+ MTP_PROPERTY_ALBUM_NAME,
+ MTP_PROPERTY_DURATION,
+ MTP_PROPERTY_DESCRIPTION,
+
+ // image specific properties
+ MTP_PROPERTY_DESCRIPTION
+};
+
+int MyMtpDatabase::SUPPORTED_PLAYBACK_FORMATS[26] = {
+ SUPPORTED_PLAYBACK_FORMAT_UNDEFINED,
+ SUPPORTED_PLAYBACK_FORMAT_ASSOCIATION,
+ SUPPORTED_PLAYBACK_FORMAT_TEXT,
+ SUPPORTED_PLAYBACK_FORMAT_HTML,
+ SUPPORTED_PLAYBACK_FORMAT_WAV,
+ SUPPORTED_PLAYBACK_FORMAT_MP3,
+ SUPPORTED_PLAYBACK_FORMAT_MPEG,
+ SUPPORTED_PLAYBACK_FORMAT_EXIF_JPEG,
+ SUPPORTED_PLAYBACK_FORMAT_TIFF_EP,
+ SUPPORTED_PLAYBACK_FORMAT_BMP,
+ SUPPORTED_PLAYBACK_FORMAT_GIF,
+ SUPPORTED_PLAYBACK_FORMAT_JFIF,
+ SUPPORTED_PLAYBACK_FORMAT_PNG,
+ SUPPORTED_PLAYBACK_FORMAT_TIFF,
+ SUPPORTED_PLAYBACK_FORMAT_WMA,
+ SUPPORTED_PLAYBACK_FORMAT_OGG,
+ SUPPORTED_PLAYBACK_FORMAT_AAC,
+ SUPPORTED_PLAYBACK_FORMAT_MP4_CONTAINER,
+ SUPPORTED_PLAYBACK_FORMAT_MP2,
+ SUPPORTED_PLAYBACK_FORMAT_3GP_CONTAINER,
+ SUPPORTED_PLAYBACK_FORMAT_ABSTRACT_AV_PLAYLIST,
+ SUPPORTED_PLAYBACK_FORMAT_WPL_PLAYLIST,
+ SUPPORTED_PLAYBACK_FORMAT_M3U_PLAYLIST,
+ SUPPORTED_PLAYBACK_FORMAT_PLS_PLAYLIST,
+ SUPPORTED_PLAYBACK_FORMAT_XML_DOCUMENT,
+ SUPPORTED_PLAYBACK_FORMAT_FLAC
+};
+
+MtpObjectHandle MyMtpDatabase::beginSendObject(const char* path,
+ MtpObjectFormat format,
+ MtpObjectHandle parent,
+ MtpStorageID storage,
+ uint64_t size,
+ time_t modified) {
+ if (storagemap.find(storage) == storagemap.end())
+ return kInvalidObjectHandle;
+ return storagemap[storage]->beginSendObject(path, format, parent, size, modified);
+}
+
+void MyMtpDatabase::endSendObject(const char* path, MtpObjectHandle handle,
+ MtpObjectFormat format, bool succeeded) {
+ MTPD("endSendObject() %s\n", path);
+ if (!succeeded) {
+ MTPE("endSendObject() failed, unlinking %s\n", path);
+ unlink(path);
+ }
+ std::map<int, MtpStorage*>::iterator storit;
+ for (storit = storagemap.begin(); storit != storagemap.end(); storit++)
+ storit->second->endSendObject(path, handle, format, succeeded);
+}
+
+void MyMtpDatabase::createDB(MtpStorage* storage, MtpStorageID storageID) {
+ MTPD("MyMtpDatabase::createDB called\n");
+ storagemap[storageID] = storage;
+ storage->createDB();
+}
+
+void MyMtpDatabase::destroyDB(MtpStorageID storageID) {
+ MtpStorage* storage = storagemap[storageID];
+ storagemap.erase(storageID);
+ delete storage;
+}
+
+MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID,
+ MtpObjectFormat format,
+ MtpObjectHandle parent) {
+ MTPD("storageID: %d\n", storageID);
+ MtpObjectHandleList* list = storagemap[storageID]->getObjectList(storageID, parent);
+ MTPD("list: %d\n", list->size());
+ return list;
+}
+
+int MyMtpDatabase::getNumObjects(MtpStorageID storageID,
+ MtpObjectFormat format,
+ MtpObjectHandle parent) {
+ MtpObjectHandleList* list = storagemap[storageID]->getObjectList(storageID, parent);
+ int size = list->size();
+ delete list;
+ return size;
+}
+
+MtpObjectFormatList* MyMtpDatabase::getSupportedPlaybackFormats() {
+ // This function tells the host PC which file formats the device supports
+ MtpObjectFormatList* list = new MtpObjectFormatList();
+ int length = sizeof(SUPPORTED_PLAYBACK_FORMATS) / sizeof(SUPPORTED_PLAYBACK_FORMATS[0]);
+ MTPD("MyMtpDatabase::getSupportedPlaybackFormats length: %i\n", length);
+ for (int i = 0; i < length; i++) {
+ MTPD("supported playback format: %x\n", SUPPORTED_PLAYBACK_FORMATS[i]);
+ list->push(SUPPORTED_PLAYBACK_FORMATS[i]);
+ }
+ return list;
+}
+
+MtpObjectFormatList* MyMtpDatabase::getSupportedCaptureFormats() {
+ // Android OS implementation of this function returns NULL
+ // so we are not implementing this function either.
+ MTPD("MyMtpDatabase::getSupportedCaptureFormats returning NULL (This is what Android does as well).\n");
+ return NULL;
+}
+
+MtpObjectPropertyList* MyMtpDatabase::getSupportedObjectProperties(MtpObjectFormat format) {
+ int* properties;
+ MtpObjectPropertyList* list = new MtpObjectPropertyList();
+ int length = 0;
+ switch (format) {
+ case MTP_FORMAT_MP3:
+ case MTP_FORMAT_WAV:
+ case MTP_FORMAT_WMA:
+ case MTP_FORMAT_OGG:
+ case MTP_FORMAT_AAC:
+ properties = AUDIO_PROPERTIES;
+ length = sizeof(AUDIO_PROPERTIES) / sizeof(AUDIO_PROPERTIES[0]);
+ break;
+ case MTP_FORMAT_MPEG:
+ case MTP_FORMAT_3GP_CONTAINER:
+ case MTP_FORMAT_WMV:
+ properties = VIDEO_PROPERTIES;
+ length = sizeof(VIDEO_PROPERTIES) / sizeof(VIDEO_PROPERTIES[0]);
+ break;
+ case MTP_FORMAT_EXIF_JPEG:
+ case MTP_FORMAT_GIF:
+ case MTP_FORMAT_PNG:
+ case MTP_FORMAT_BMP:
+ properties = IMAGE_PROPERTIES;
+ length = sizeof(IMAGE_PROPERTIES) / sizeof(IMAGE_PROPERTIES[0]);
+ break;
+ case 0:
+ properties = ALL_PROPERTIES;
+ length = sizeof(ALL_PROPERTIES) / sizeof(ALL_PROPERTIES[0]);
+ break;
+ default:
+ properties = FILE_PROPERTIES;
+ length = sizeof(FILE_PROPERTIES) / sizeof(FILE_PROPERTIES[0]);
+ }
+ MTPD("MyMtpDatabase::getSupportedObjectProperties length is: %i, format: %x", length, format);
+ for (int i = 0; i < length; i++) {
+ MTPD("supported object property: %x\n", properties[i]);
+ list->push(properties[i]);
+ }
+ return list;
+}
+
+MtpDevicePropertyList* MyMtpDatabase::getSupportedDeviceProperties() {
+ MtpDevicePropertyList* list = new MtpDevicePropertyList();
+ int length = sizeof(DEVICE_PROPERTIES) / sizeof(DEVICE_PROPERTIES[0]);
+ MTPD("MyMtpDatabase::getSupportedDeviceProperties length was: %i\n", length);
+ for (int i = 0; i < length; i++)
+ list->push(DEVICE_PROPERTIES[i]);
+ return list;
+}
+
+MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle,
+ MtpObjectProperty property,
+ MtpDataPacket& packet) {
+ MTPD("MyMtpDatabase::getObjectPropertyValue mtpid: %u, property: %x\n", handle, property);
+ int type;
+ MtpResponseCode result = MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ MtpStorage::PropEntry prop;
+ if (!getObjectPropertyInfo(property, type)) {
+ MTPE("MyMtpDatabase::getObjectPropertyValue returning MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED\n");
+ return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+ }
+ std::map<int, MtpStorage*>::iterator storit;
+ for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+ if (storit->second->getObjectPropertyValue(handle, property, prop) == 0) {
+ result = MTP_RESPONSE_OK;
+ break;
+ }
+ }
+
+ if (result != MTP_RESPONSE_OK) {
+ MTPE("MyMtpDatabase::getObjectPropertyValue unable to locate handle: %u\n", handle);
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ }
+
+ uint64_t longValue = prop.intvalue;
+ // special case date properties, which are strings to MTP
+ // but stored internally as a uint64
+ if (property == MTP_PROPERTY_DATE_MODIFIED || property == MTP_PROPERTY_DATE_ADDED) {
+ char date[20];
+ formatDateTime(longValue, date, sizeof(date));
+ packet.putString(date);
+ goto out;
+ }
+ // release date is stored internally as just the year
+ if (property == MTP_PROPERTY_ORIGINAL_RELEASE_DATE) {
+ char date[20];
+ snprintf(date, sizeof(date), "%04lld0101T000000", longValue);
+ packet.putString(date);
+ goto out;
+ }
+
+ switch (type) {
+ case MTP_TYPE_INT8:
+ packet.putInt8(longValue);
+ break;
+ case MTP_TYPE_UINT8:
+ packet.putUInt8(longValue);
+ break;
+ case MTP_TYPE_INT16:
+ packet.putInt16(longValue);
+ break;
+ case MTP_TYPE_UINT16:
+ packet.putUInt16(longValue);
+ break;
+ case MTP_TYPE_INT32:
+ packet.putInt32(longValue);
+ break;
+ case MTP_TYPE_UINT32:
+ packet.putUInt32(longValue);
+ break;
+ case MTP_TYPE_INT64:
+ packet.putInt64(longValue);
+ break;
+ case MTP_TYPE_UINT64:
+ packet.putUInt64(longValue);
+ break;
+ case MTP_TYPE_INT128:
+ packet.putInt128(longValue);
+ break;
+ case MTP_TYPE_UINT128:
+ packet.putUInt128(longValue);
+ break;
+ case MTP_TYPE_STR:
+ {
+ /*std::string stringValue = (string)stringValuesArray[0];
+ if (stringValue) {
+ const char* str = stringValue.c_str();
+ if (str == NULL) {
+ return MTP_RESPONSE_GENERAL_ERROR;
+ }
+ packet.putString(str);
+ } else {
+ packet.putEmptyString();
+ }*/
+ packet.putString(prop.strvalue.c_str());
+ MTPD("MTP_TYPE_STR: %x = %s\n", prop.property, prop.strvalue.c_str());
+ //MTPE("STRING unsupported type in getObjectPropertyValue\n");
+ //result = MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
+ break;
+ }
+ default:
+ MTPE("unsupported type in getObjectPropertyValue\n");
+ result = MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
+ }
+out:
+ return result;
+}
+
+MtpResponseCode MyMtpDatabase::setObjectPropertyValue(MtpObjectHandle handle,
+ MtpObjectProperty property,
+ MtpDataPacket& packet) {
+ int type;
+ MTPD("MyMtpDatabase::setObjectPropertyValue start\n");
+ if (!getObjectPropertyInfo(property, type)) {
+ MTPE("MyMtpDatabase::setObjectPropertyValue returning MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED\n");
+ return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+ }
+ MTPD("MyMtpDatabase::setObjectPropertyValue continuing\n");
+ long longValue = 0;
+ std::string stringValue;
+
+ switch (type) {
+ case MTP_TYPE_INT8:
+ MTPD("int8\n");
+ longValue = packet.getInt8();
+ break;
+ case MTP_TYPE_UINT8:
+ MTPD("uint8\n");
+ longValue = packet.getUInt8();
+ break;
+ case MTP_TYPE_INT16:
+ MTPD("int16\n");
+ longValue = packet.getInt16();
+ break;
+ case MTP_TYPE_UINT16:
+ MTPD("uint16\n");
+ longValue = packet.getUInt16();
+ break;
+ case MTP_TYPE_INT32:
+ MTPD("int32\n");
+ longValue = packet.getInt32();
+ break;
+ case MTP_TYPE_UINT32:
+ MTPD("uint32\n");
+ longValue = packet.getUInt32();
+ break;
+ case MTP_TYPE_INT64:
+ MTPD("int64\n");
+ longValue = packet.getInt64();
+ break;
+ case MTP_TYPE_UINT64:
+ MTPD("uint64\n");
+ longValue = packet.getUInt64();
+ break;
+ case MTP_TYPE_STR:
+ {
+ MTPD("string\n");
+ MtpStringBuffer buffer;
+ packet.getString(buffer);
+ stringValue = (const char *)buffer;
+ break;
+ }
+ default:
+ MTPE("MyMtpDatabase::setObjectPropertyValue unsupported type %i in getObjectPropertyValue\n", type);
+ return MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
+ }
+
+ int result = MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+
+ switch (property) {
+ case MTP_PROPERTY_OBJECT_FILE_NAME:
+ {
+ MTPD("MyMtpDatabase::setObjectPropertyValue renaming file, handle: %d, new name: '%s'\n", handle, stringValue.c_str());
+ std::map<int, MtpStorage*>::iterator storit;
+ for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+ if (storit->second->renameObject(handle, stringValue) == 0) {
+ MTPD("MTP_RESPONSE_OK\n");
+ result = MTP_RESPONSE_OK;
+ break;
+ }
+ }
+ }
+ break;
+
+ default:
+ MTPE("MyMtpDatabase::setObjectPropertyValue property %x not supported.\n", property);
+ result = MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+ }
+ MTPD("MyMtpDatabase::setObjectPropertyValue returning %d\n", result);
+ return result;
+}
+
+MtpResponseCode MyMtpDatabase::getDevicePropertyValue(MtpDeviceProperty property,
+ MtpDataPacket& packet) {
+ int type, result = 0;
+ char prop_value[PROPERTY_VALUE_MAX];
+ MTPD("property %s\n",
+ MtpDebug::getDevicePropCodeName(property));
+ if (!getDevicePropertyInfo(property, type)) {
+ MTPE("MyMtpDatabase::getDevicePropertyValue MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED\n");
+ return MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
+ }
+ MTPD("property %s\n",
+ MtpDebug::getDevicePropCodeName(property));
+ MTPD("property %x\n", property);
+ MTPD("MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME %x\n", MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME);
+ switch (property) {
+ case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
+ case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
+ result = MTP_RESPONSE_OK;
+ break;
+ default:
+ {
+ MTPE("MyMtpDatabase::getDevicePropertyValue property %x not supported\n", property);
+ result = MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
+ break;
+ }
+ }
+
+ if (result != MTP_RESPONSE_OK) {
+ MTPD("MTP_REPONSE_OK NOT OK\n");
+ return result;
+ }
+
+ long longValue = 0;
+ property_get("ro.build.product", prop_value, "unknown manufacturer");
+ switch (type) {
+ case MTP_TYPE_INT8: {
+ MTPD("MTP_TYPE_INT8\n");
+ packet.putInt8(longValue);
+ break;
+ }
+ case MTP_TYPE_UINT8:
+ {
+ MTPD("MTP_TYPE_UINT8\n");
+ packet.putUInt8(longValue);
+ break;
+ }
+ case MTP_TYPE_INT16:
+ {
+ MTPD("MTP_TYPE_INT16\n");
+ packet.putInt16(longValue);
+ break;
+ }
+ case MTP_TYPE_UINT16:
+ {
+ MTPD("MTP_TYPE_UINT16\n");
+ packet.putUInt16(longValue);
+ break;
+ }
+ case MTP_TYPE_INT32:
+ {
+ MTPD("MTP_TYPE_INT32\n");
+ packet.putInt32(longValue);
+ break;
+ }
+ case MTP_TYPE_UINT32:
+ {
+ MTPD("MTP_TYPE_UINT32\n");
+ packet.putUInt32(longValue);
+ break;
+ }
+ case MTP_TYPE_INT64:
+ {
+ MTPD("MTP_TYPE_INT64\n");
+ packet.putInt64(longValue);
+ break;
+ }
+ case MTP_TYPE_UINT64:
+ {
+ MTPD("MTP_TYPE_UINT64\n");
+ packet.putUInt64(longValue);
+ break;
+ }
+ case MTP_TYPE_INT128:
+ {
+ MTPD("MTP_TYPE_INT128\n");
+ packet.putInt128(longValue);
+ break;
+ }
+ case MTP_TYPE_UINT128:
+ {
+ MTPD("MTP_TYPE_UINT128\n");
+ packet.putInt128(longValue);
+ break;
+ }
+ case MTP_TYPE_STR:
+ {
+ MTPD("MTP_TYPE_STR\n");
+ char* str = prop_value;
+ packet.putString(str);
+ break;
+ }
+ default:
+ MTPE("MyMtpDatabase::getDevicePropertyValue unsupported type %i in getDevicePropertyValue\n", type);
+ return MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT;
+ }
+
+ return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MyMtpDatabase::setDevicePropertyValue(MtpDeviceProperty property, MtpDataPacket& packet) {
+ int type;
+ MTPE("MyMtpDatabase::setDevicePropertyValue not implemented, returning 0\n");
+ return 0;
+}
+
+MtpResponseCode MyMtpDatabase::resetDeviceProperty(MtpDeviceProperty property) {
+ MTPE("MyMtpDatabase::resetDeviceProperty not implemented, returning -1\n");
+ return -1;
+}
+
+MtpResponseCode MyMtpDatabase::getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet) {
+ MTPD("getObjectPropertyList()\n");
+ MTPD("property: %x\n", property);
+ std::map<int, MtpStorage*>::iterator storit;
+ for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+ MTPD("MyMtpDatabase::getObjectPropertyList calling getObjectPropertyList\n");
+ if (storit->second->getObjectPropertyList(handle, format, property, groupCode, depth, packet) == 0) {
+ MTPD("MTP_RESPONSE_OK\n");
+ return MTP_RESPONSE_OK;
+ }
+ }
+ MTPE("MyMtpDatabase::getObjectPropertyList MTP_RESPOSNE_INVALID_OBJECT_HANDLE %i\n", handle);
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+}
+
+MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info) {
+ std::map<int, MtpStorage*>::iterator storit;
+ for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+ if (storit->second->getObjectInfo(handle, info) == 0) {
+ MTPD("MTP_RESPONSE_OK\n");
+ return MTP_RESPONSE_OK;
+ }
+ }
+ MTPE("MyMtpDatabase::getObjectInfo MTP_RESPONSE_INVALID_OBJECT_HANDLE %i\n", handle);
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+}
+
+void* MyMtpDatabase::getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) {
+ MtpString path;
+ int64_t length;
+ MtpObjectFormat format;
+ void* result = NULL;
+ outThumbSize = 0;
+ MTPE("MyMtpDatabase::getThumbnail not implemented, returning 0\n");
+ return 0;
+}
+
+MtpResponseCode MyMtpDatabase::getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, int64_t& outFileLength, MtpObjectFormat& outFormat) {
+ std::map<int, MtpStorage*>::iterator storit;
+ for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+ MTPD("MyMtpDatabase::getObjectFilePath calling getObjectFilePath\n");
+ if (storit->second->getObjectFilePath(handle, outFilePath, outFileLength, outFormat) == 0) {
+ MTPD("MTP_RESPONSE_OK\n");
+ return MTP_RESPONSE_OK;
+ }
+ }
+ MTPE("MyMtpDatabase::getObjectFilePath MTP_RESPOSNE_INVALID_OBJECT_HANDLE %i\n", handle);
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+}
+
+MtpResponseCode MyMtpDatabase::deleteFile(MtpObjectHandle handle) {
+ MTPD("deleteFile\n");
+ std::map<int, MtpStorage*>::iterator storit;
+ for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+ if (storit->second->deleteFile(handle) == 0) {
+ MTPD("MTP_RESPONSE_OK\n");
+ return MTP_RESPONSE_OK;
+ }
+ }
+ MTPE("MyMtpDatabase::deleteFile MTP_RESPONSE_INVALID_OBJECT_HANDLE %i\n", handle);
+ return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+}
+
+struct PropertyTableEntry {
+ MtpObjectProperty property;
+ int type;
+};
+
+static const PropertyTableEntry kObjectPropertyTable[] = {
+ { MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT32 },
+ { MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16 },
+ { MTP_PROPERTY_PROTECTION_STATUS, MTP_TYPE_UINT16 },
+ { MTP_PROPERTY_OBJECT_SIZE, MTP_TYPE_UINT64 },
+ { MTP_PROPERTY_OBJECT_FILE_NAME, MTP_TYPE_STR },
+ { MTP_PROPERTY_DATE_MODIFIED, MTP_TYPE_STR },
+ { MTP_PROPERTY_PARENT_OBJECT, MTP_TYPE_UINT32 },
+ { MTP_PROPERTY_PERSISTENT_UID, MTP_TYPE_UINT128 },
+ { MTP_PROPERTY_NAME, MTP_TYPE_STR },
+ { MTP_PROPERTY_DISPLAY_NAME, MTP_TYPE_STR },
+ { MTP_PROPERTY_DATE_ADDED, MTP_TYPE_STR },
+ { MTP_PROPERTY_ARTIST, MTP_TYPE_STR },
+ { MTP_PROPERTY_ALBUM_NAME, MTP_TYPE_STR },
+ { MTP_PROPERTY_ALBUM_ARTIST, MTP_TYPE_STR },
+ { MTP_PROPERTY_TRACK, MTP_TYPE_UINT16 },
+ { MTP_PROPERTY_ORIGINAL_RELEASE_DATE, MTP_TYPE_STR },
+ { MTP_PROPERTY_GENRE, MTP_TYPE_STR },
+ { MTP_PROPERTY_COMPOSER, MTP_TYPE_STR },
+ { MTP_PROPERTY_DURATION, MTP_TYPE_UINT32 },
+ { MTP_PROPERTY_DESCRIPTION, MTP_TYPE_STR },
+};
+
+static const PropertyTableEntry kDevicePropertyTable[] = {
+ { MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER, MTP_TYPE_STR },
+ { MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME, MTP_TYPE_STR },
+ { MTP_DEVICE_PROPERTY_IMAGE_SIZE, MTP_TYPE_STR },
+};
+
+bool MyMtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) {
+ int count = sizeof(kObjectPropertyTable) / sizeof(kObjectPropertyTable[0]);
+ const PropertyTableEntry* entry = kObjectPropertyTable;
+ MTPD("MyMtpDatabase::getObjectPropertyInfo size is: %i\n", count);
+ for (int i = 0; i < count; i++, entry++) {
+ if (entry->property == property) {
+ type = entry->type;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool MyMtpDatabase::getDevicePropertyInfo(MtpDeviceProperty property, int& type) {
+ int count = sizeof(kDevicePropertyTable) / sizeof(kDevicePropertyTable[0]);
+ const PropertyTableEntry* entry = kDevicePropertyTable;
+ MTPD("MyMtpDatabase::getDevicePropertyInfo count is: %i\n", count);
+ for (int i = 0; i < count; i++, entry++) {
+ if (entry->property == property) {
+ type = entry->type;
+ MTPD("type: %x\n", type);
+ return true;
+ }
+ }
+ return false;
+}
+
+MtpObjectHandleList* MyMtpDatabase::getObjectReferences(MtpObjectHandle handle) {
+ // call function and place files with associated handles into int array
+ MTPD("MyMtpDatabase::getObjectReferences returning null, this seems to be what Android always does.\n");
+ MTPD("handle: %d\n", handle);
+ // Windows + Android seems to always return a NULL in this function, c == null path
+ // The way that this is handled in Android then is to do this:
+ return NULL;
+}
+
+MtpResponseCode MyMtpDatabase::setObjectReferences(MtpObjectHandle handle,
+ MtpObjectHandleList* references) {
+ int count = references->size();
+ MTPE("MyMtpDatabase::setObjectReferences not implemented, returning 0\n");
+ return 0;
+}
+
+MtpProperty* MyMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property,
+ MtpObjectFormat format) {
+ MTPD("MyMtpDatabase::getObjectPropertyDesc start\n");
+ MtpProperty* result = NULL;
+ switch (property) {
+ case MTP_PROPERTY_OBJECT_FORMAT:
+ // use format as default value
+ result = new MtpProperty(property, MTP_TYPE_UINT16, false, format);
+ break;
+ case MTP_PROPERTY_PROTECTION_STATUS:
+ case MTP_PROPERTY_TRACK:
+ result = new MtpProperty(property, MTP_TYPE_UINT16);
+ break;
+ case MTP_PROPERTY_STORAGE_ID:
+ case MTP_PROPERTY_PARENT_OBJECT:
+ case MTP_PROPERTY_DURATION:
+ result = new MtpProperty(property, MTP_TYPE_UINT32);
+ break;
+ case MTP_PROPERTY_OBJECT_SIZE:
+ result = new MtpProperty(property, MTP_TYPE_UINT64);
+ break;
+ case MTP_PROPERTY_PERSISTENT_UID:
+ result = new MtpProperty(property, MTP_TYPE_UINT128);
+ break;
+ case MTP_PROPERTY_NAME:
+ case MTP_PROPERTY_DISPLAY_NAME:
+ case MTP_PROPERTY_ARTIST:
+ case MTP_PROPERTY_ALBUM_NAME:
+ case MTP_PROPERTY_ALBUM_ARTIST:
+ case MTP_PROPERTY_GENRE:
+ case MTP_PROPERTY_COMPOSER:
+ case MTP_PROPERTY_DESCRIPTION:
+ result = new MtpProperty(property, MTP_TYPE_STR);
+ break;
+ case MTP_PROPERTY_DATE_MODIFIED:
+ case MTP_PROPERTY_DATE_ADDED:
+ case MTP_PROPERTY_ORIGINAL_RELEASE_DATE:
+ result = new MtpProperty(property, MTP_TYPE_STR);
+ result->setFormDateTime();
+ break;
+ case MTP_PROPERTY_OBJECT_FILE_NAME:
+ // We allow renaming files and folders
+ result = new MtpProperty(property, MTP_TYPE_STR, true);
+ break;
+ }
+ return result;
+}
+
+MtpProperty* MyMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) {
+ MtpProperty* result = NULL;
+ int ret;
+ bool writable = false;
+ switch (property) {
+ case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
+ case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
+ writable = true;
+ // fall through
+ case MTP_DEVICE_PROPERTY_IMAGE_SIZE:
+ result = new MtpProperty(property, MTP_TYPE_STR, writable);
+
+ // get current value
+ // TODO: add actual values
+ result->setCurrentValue(0);
+ result->setDefaultValue(0);
+ break;
+ }
+
+ return result;
+}
+
+void MyMtpDatabase::sessionStarted() {
+ MTPD("MyMtpDatabase::sessionStarted not implemented or does nothing, returning\n");
+ return;
+}
+
+void MyMtpDatabase::sessionEnded() {
+ MTPD("MyMtpDatabase::sessionEnded not implemented or does nothing, returning\n");
+ return;
+}
+
+// ----------------------------------------------------------------------------
+
+void MyMtpDatabase::lockMutex(void) {
+ std::map<int, MtpStorage*>::iterator storit;
+ for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+ storit->second->lockMutex(0);
+ }
+}
+
+void MyMtpDatabase::unlockMutex(void) {
+ std::map<int, MtpStorage*>::iterator storit;
+ for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+ storit->second->unlockMutex(0);
+ }
+}
diff --git a/mtp/mtp_MtpDatabase.hpp b/mtp/mtp_MtpDatabase.hpp
new file mode 100644
index 000000000..49e59135a
--- /dev/null
+++ b/mtp/mtp_MtpDatabase.hpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef MTP_MTPDATABASE_HPP
+#define MTP_MTPDATABASE_HPP
+
+#include <utils/Log.h>
+
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <map>
+#include <string>
+#include <deque>
+
+#include "MtpDatabase.h"
+#include "MtpDataPacket.h"
+#include "MtpObjectInfo.h"
+#include "MtpProperty.h"
+#include "MtpStringBuffer.h"
+#include "MtpUtils.h"
+#include "mtp.h"
+
+class MyMtpDatabase : public MtpDatabase {
+private:
+ int* getSupportedObjectProperties(int format);
+
+ static int FILE_PROPERTIES[10];
+ static int DEVICE_PROPERTIES[3];
+ static int AUDIO_PROPERTIES[19];
+ static int VIDEO_PROPERTIES[15];
+ static int IMAGE_PROPERTIES[12];
+ static int ALL_PROPERTIES[25];
+ static int SUPPORTED_PLAYBACK_FORMATS[26];
+ int storagenum;
+ int count;
+ std::string lastfile;
+ std::map<int, MtpStorage*> storagemap;
+ void countDirs(std::string path);
+ int readParentDirs(std::string path, int storageID);
+
+public:
+ MyMtpDatabase();
+ virtual ~MyMtpDatabase();
+
+ void createDB(MtpStorage* storage, MtpStorageID storageID);
+ void destroyDB(MtpStorageID storageID);
+ virtual MtpObjectHandle beginSendObject(const char* path,
+ MtpObjectFormat format,
+ MtpObjectHandle parent,
+ MtpStorageID storage,
+ uint64_t size,
+ time_t modified);
+
+ virtual void endSendObject(const char* path,
+ MtpObjectHandle handle,
+ MtpObjectFormat format,
+ bool succeeded);
+
+ virtual MtpObjectHandleList* getObjectList(MtpStorageID storageID,
+ MtpObjectFormat format,
+ MtpObjectHandle parent);
+
+ virtual int getNumObjects(MtpStorageID storageID,
+ MtpObjectFormat format,
+ MtpObjectHandle parent);
+
+ // callee should delete[] the results from these
+ // results can be NULL
+ virtual MtpObjectFormatList* getSupportedPlaybackFormats();
+ virtual MtpObjectFormatList* getSupportedCaptureFormats();
+ virtual MtpObjectPropertyList* getSupportedObjectProperties(MtpObjectFormat format);
+ virtual MtpDevicePropertyList* getSupportedDeviceProperties();
+
+ virtual MtpResponseCode getObjectPropertyValue(MtpObjectHandle handle,
+ MtpObjectProperty property,
+ MtpDataPacket& packet);
+
+ virtual MtpResponseCode setObjectPropertyValue(MtpObjectHandle handle,
+ MtpObjectProperty property,
+ MtpDataPacket& packet);
+
+ virtual MtpResponseCode getDevicePropertyValue(MtpDeviceProperty property,
+ MtpDataPacket& packet);
+
+ virtual MtpResponseCode setDevicePropertyValue(MtpDeviceProperty property,
+ MtpDataPacket& packet);
+
+ virtual MtpResponseCode resetDeviceProperty(MtpDeviceProperty property);
+
+ virtual MtpResponseCode getObjectPropertyList(MtpObjectHandle handle,
+ uint32_t format, uint32_t property,
+ int groupCode, int depth,
+ MtpDataPacket& packet);
+
+ virtual MtpResponseCode getObjectInfo(MtpObjectHandle handle,
+ MtpObjectInfo& info);
+
+ virtual void* getThumbnail(MtpObjectHandle handle, size_t& outThumbSize);
+
+ virtual MtpResponseCode getObjectFilePath(MtpObjectHandle handle,
+ MtpString& outFilePath,
+ int64_t& outFileLength,
+ MtpObjectFormat& outFormat);
+ virtual MtpResponseCode deleteFile(MtpObjectHandle handle);
+
+ bool getObjectPropertyInfo(MtpObjectProperty property, int& type);
+ bool getDevicePropertyInfo(MtpDeviceProperty property, int& type);
+
+ virtual MtpObjectHandleList* getObjectReferences(MtpObjectHandle handle);
+
+ virtual MtpResponseCode setObjectReferences(MtpObjectHandle handle,
+ MtpObjectHandleList* references);
+
+ virtual MtpProperty* getObjectPropertyDesc(MtpObjectProperty property,
+ MtpObjectFormat format);
+
+ virtual MtpProperty* getDevicePropertyDesc(MtpDeviceProperty property);
+
+ virtual void sessionStarted();
+
+ virtual void sessionEnded();
+ virtual void lockMutex();
+ virtual void unlockMutex();
+};
+#endif
diff --git a/mtp/mtp_MtpServer.cpp b/mtp/mtp_MtpServer.cpp
new file mode 100644
index 000000000..1ebe5f55d
--- /dev/null
+++ b/mtp/mtp_MtpServer.cpp
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#include <utils/Log.h>
+
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <vector>
+#include <utils/threads.h>
+#include <pthread.h>
+
+#include "mtp_MtpServer.hpp"
+#include "MtpServer.h"
+#include "MtpStorage.h"
+#include "MtpDebug.h"
+#include "MtpMessage.hpp"
+
+#include <string>
+
+void twmtp_MtpServer::start()
+{
+ usePtp = false;
+ MyMtpDatabase* mtpdb = new MyMtpDatabase();
+ /* Sleep for a bit before we open the MTP USB device because some
+ * devices are not ready due to the kernel not responding to our
+ * sysfs requests right away.
+ */
+ usleep(800000);
+#ifdef USB_MTP_DEVICE
+#define STRINGIFY(x) #x
+#define EXPAND(x) STRINGIFY(x)
+ const char* mtp_device = EXPAND(USB_MTP_DEVICE);
+ MTPI("Using '%s' for MTP device.\n", EXPAND(USB_MTP_DEVICE));
+#else
+ const char* mtp_device = "/dev/mtp_usb";
+#endif
+ int fd = open(mtp_device, O_RDWR);
+ if (fd < 0) {
+ MTPE("could not open MTP driver, errno: %d\n", errno);
+ return;
+ }
+ MTPD("fd: %d\n", fd);
+ server = new MtpServer(mtpdb, usePtp, 0, 0664, 0775);
+ refserver = server;
+ MTPI("created new mtpserver object\n");
+ add_storage();
+ MTPD("Starting add / remove mtppipe monitor thread\n");
+ pthread_t thread;
+ ThreadPtr mtpptr = &twmtp_MtpServer::mtppipe_thread;
+ PThreadPtr p = *(PThreadPtr*)&mtpptr;
+ pthread_create(&thread, NULL, p, this);
+ // This loop restarts the MTP process if the device is unplugged and replugged in
+ while (true) {
+ server->run(fd);
+ fd = open(mtp_device, O_RDWR);
+ usleep(800000);
+ }
+}
+
+void twmtp_MtpServer::set_storages(storages* mtpstorages) {
+ stores = mtpstorages;
+}
+
+void twmtp_MtpServer::cleanup()
+{
+ android::Mutex sMutex;
+ android::Mutex::Autolock autoLock(sMutex);
+
+ if (server) {
+ delete server;
+ } else {
+ MTPD("server is null in cleanup");
+ }
+}
+
+void twmtp_MtpServer::send_object_added(int handle)
+{
+ android::Mutex sMutex;
+ android::Mutex::Autolock autoLock(sMutex);
+
+ if (server)
+ server->sendObjectAdded(handle);
+ else
+ MTPD("server is null in send_object_added");
+}
+
+void twmtp_MtpServer::send_object_removed(int handle)
+{
+ android::Mutex sMutex;
+ android::Mutex::Autolock autoLock(sMutex);
+
+ if (server)
+ server->sendObjectRemoved(handle);
+ else
+ MTPD("server is null in send_object_removed");
+}
+
+void twmtp_MtpServer::add_storage()
+{
+ android::Mutex sMutex;
+ android::Mutex::Autolock autoLock(sMutex);
+
+ MTPD("twmtp_MtpServer::add_storage count of storage devices: %i\n", stores->size());
+ for (unsigned int i = 0; i < stores->size(); ++i) {
+ std::string pathStr = stores->at(i)->mount;
+
+ if (!pathStr.empty()) {
+ std::string descriptionStr = stores->at(i)->display;
+ int storageID = stores->at(i)->mtpid;
+ long reserveSpace = 1;
+ bool removable = false;
+ uint64_t maxFileSize = stores->at(i)->maxFileSize;
+ if (descriptionStr != "") {
+ MtpStorage* storage = new MtpStorage(storageID, &pathStr[0], &descriptionStr[0], reserveSpace, removable, maxFileSize, refserver);
+ server->addStorage(storage);
+ }
+ }
+ }
+}
+
+void twmtp_MtpServer::remove_storage(int storageId)
+{
+ android::Mutex sMutex;
+ android::Mutex::Autolock autoLock(sMutex);
+
+ if (server) {
+ MtpStorage* storage = server->getStorage(storageId);
+ if (storage) {
+ MTPD("twmtp_MtpServer::remove_storage calling removeStorage\n");
+ server->removeStorage(storage);
+ }
+ } else
+ MTPD("server is null in remove_storage");
+ MTPD("twmtp_MtpServer::remove_storage DONE\n");
+}
+
+int twmtp_MtpServer::mtppipe_thread(void)
+{
+ if (mtp_read_pipe == -1) {
+ MTPD("mtppipe_thread exiting because mtp_read_pipe not set\n");
+ return 0;
+ }
+ MTPD("Starting twmtp_MtpServer::mtppipe_thread\n");
+ int read_count;
+ struct mtpmsg mtp_message;
+ while (1) {
+ read_count = ::read(mtp_read_pipe, &mtp_message, sizeof(mtp_message));
+ MTPD("read %i from mtppipe\n", read_count);
+ if (read_count == sizeof(mtp_message)) {
+ if (mtp_message.message_type == MTP_MESSAGE_ADD_STORAGE) {
+ MTPI("mtppipe add storage %i '%s'\n", mtp_message.storage_id, mtp_message.path);
+ if (mtp_message.storage_id) {
+ long reserveSpace = 1;
+ bool removable = false;
+ MtpStorage* storage = new MtpStorage(mtp_message.storage_id, &mtp_message.path[0], &mtp_message.display[0], reserveSpace, removable, mtp_message.maxFileSize, refserver);
+ server->addStorage(storage);
+ MTPD("mtppipe done adding storage\n");
+ } else {
+ MTPE("Invalid storage ID %i specified\n", mtp_message.storage_id);
+ }
+ } else if (mtp_message.message_type == MTP_MESSAGE_REMOVE_STORAGE) {
+ MTPI("mtppipe remove storage %i\n", mtp_message.storage_id);
+ remove_storage(mtp_message.storage_id);
+ MTPD("mtppipe done removing storage\n");
+ } else {
+ MTPE("Unknown mtppipe message value: %i\n", mtp_message.message_type);
+ }
+ } else {
+ MTPE("twmtp_MtpServer::mtppipe_thread unexpected read_count %i\n", read_count);
+ close(mtp_read_pipe);
+ break;
+ }
+ }
+ MTPD("twmtp_MtpServer::mtppipe_thread closing\n");
+ return 0;
+}
+
+void twmtp_MtpServer::set_read_pipe(int pipe)
+{
+ mtp_read_pipe = pipe;
+}
diff --git a/mtp/mtp_MtpServer.hpp b/mtp/mtp_MtpServer.hpp
new file mode 100644
index 000000000..99f63d510
--- /dev/null
+++ b/mtp/mtp_MtpServer.hpp
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef MTP_MTPSERVER_HPP
+#define MTP_MTPSERVER_HPP
+#include <utils/Log.h>
+
+#include <string>
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <utils/threads.h>
+
+#include "MtpServer.h"
+#include "MtpStorage.h"
+#include "mtp_MtpDatabase.hpp"
+
+typedef struct Storage {
+ std::string display;
+ std::string mount;
+ int mtpid;
+ uint64_t maxFileSize;
+} storage;
+
+typedef std::vector<storage*> storages;
+
+class twmtp_MtpServer {
+ public:
+ void start();
+ void cleanup();
+ void send_object_added(int handle);
+ void send_object_removed(int handle);
+ void add_storage();
+ void remove_storage(int storageId);
+ void set_storages(storages* mtpstorages);
+ void set_read_pipe(int pipe);
+ storages *stores;
+ private:
+ typedef int (twmtp_MtpServer::*ThreadPtr)(void);
+ typedef void* (*PThreadPtr)(void *);
+ int mtppipe_thread(void);
+ bool usePtp;
+ MtpServer* server;
+ MtpServer* refserver;
+ int mtp_read_pipe;
+
+};
+#endif
diff --git a/mtp/node.cpp b/mtp/node.cpp
new file mode 100644
index 000000000..207a37a08
--- /dev/null
+++ b/mtp/node.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <libgen.h>
+
+#include "btree.hpp"
+#include "mtp.h"
+#include "MtpDebug.h"
+
+
+Node::Node()
+ : handle(-1), parent(0), name("")
+{
+}
+
+Node::Node(MtpObjectHandle handle, MtpObjectHandle parent, const std::string& name)
+ : handle(handle), parent(parent), name(name)
+{
+}
+
+void Node::rename(const std::string& newName) {
+ name = newName;
+ updateProperty(MTP_PROPERTY_OBJECT_FILE_NAME, 0, name.c_str(), MTP_TYPE_STR);
+ updateProperty(MTP_PROPERTY_NAME, 0, name.c_str(), MTP_TYPE_STR);
+ updateProperty(MTP_PROPERTY_DISPLAY_NAME, 0, name.c_str(), MTP_TYPE_STR);
+}
+
+MtpObjectHandle Node::Mtpid() const { return handle; }
+MtpObjectHandle Node::getMtpParentId() const { return parent; }
+const std::string& Node::getName() const { return name; }
+
+uint64_t Node::getIntProperty(MtpPropertyCode property) {
+ for (unsigned index = 0; index < mtpProp.size(); ++index) {
+ if (mtpProp[index].property == property)
+ return mtpProp[index].valueInt;
+ }
+ MTPE("Node::getIntProperty failed to find property %x, returning -1\n", (unsigned)property);
+ return -1;
+}
+
+const Node::mtpProperty& Node::getProperty(MtpPropertyCode property) {
+ static const mtpProperty dummyProp;
+ for (size_t i = 0; i < mtpProp.size(); ++i) {
+ if (mtpProp[i].property == property)
+ return mtpProp[i];
+ }
+ MTPE("Node::getProperty failed to find property %x, returning dummy property\n", (unsigned)property);
+ return dummyProp;
+}
+
+void Node::addProperty(MtpPropertyCode property, uint64_t valueInt, std::string valueStr, MtpDataType dataType) {
+// MTPD("adding property: %lld, valueInt: %lld, valueStr: %s, dataType: %d\n", property, valueInt, valueStr.c_str(), dataType);
+ struct mtpProperty prop;
+ prop.property = property;
+ prop.valueInt = valueInt;
+ prop.valueStr = valueStr;
+ prop.dataType = dataType;
+ mtpProp.push_back(prop);
+}
+
+void Node::updateProperty(MtpPropertyCode property, uint64_t valueInt, std::string valueStr, MtpDataType dataType) {
+ for (unsigned i = 0; i < mtpProp.size(); i++) {
+ if (mtpProp[i].property == property) {
+ mtpProp[i].valueInt = valueInt;
+ mtpProp[i].valueStr = valueStr;
+ mtpProp[i].dataType = dataType;
+ return;
+ }
+ }
+ addProperty(property, valueInt, valueStr, dataType);
+}
+
+std::vector<Node::mtpProperty>& Node::getMtpProps() {
+ return mtpProp;
+}
+
+void Node::addProperties(const std::string& path, int storageID) {
+ MTPD("addProperties: handle: %u, filename: '%s'\n", handle, getName().c_str());
+ struct stat st;
+ int mFormat = 0;
+ uint64_t puid = ((uint64_t)storageID << 32) + handle;
+ off_t file_size = 0;
+
+ mFormat = MTP_FORMAT_UNDEFINED; // file
+ if (lstat(path.c_str(), &st) == 0) {
+ file_size = st.st_size;
+ if (S_ISDIR(st.st_mode))
+ mFormat = MTP_FORMAT_ASSOCIATION; // folder
+ }
+
+ // TODO: don't store properties with constant values at all, add them at query time instead
+ addProperty(MTP_PROPERTY_STORAGE_ID, storageID, "", MTP_TYPE_UINT32);
+ addProperty(MTP_PROPERTY_OBJECT_FORMAT, mFormat, "", MTP_TYPE_UINT16);
+ addProperty(MTP_PROPERTY_PROTECTION_STATUS, 0, "", MTP_TYPE_UINT16);
+ addProperty(MTP_PROPERTY_OBJECT_SIZE, file_size, "", MTP_TYPE_UINT64);
+ addProperty(MTP_PROPERTY_OBJECT_FILE_NAME, 0, getName().c_str(), MTP_TYPE_STR);
+ addProperty(MTP_PROPERTY_DATE_MODIFIED, st.st_mtime, "", MTP_TYPE_UINT64);
+ addProperty(MTP_PROPERTY_PARENT_OBJECT, parent, "", MTP_TYPE_UINT32);
+ addProperty(MTP_PROPERTY_PERSISTENT_UID, puid, "", MTP_TYPE_UINT128);
+ // TODO: we can't really support persistent UIDs without a persistent DB.
+ // probably a combination of volume UUID + st_ino would come close.
+ // doesn't help for fs with no native inodes numbers like fat though...
+ // however, Microsoft's own impl (Zune, etc.) does not support persistent UIDs either
+ addProperty(MTP_PROPERTY_NAME, 0, getName().c_str(), MTP_TYPE_STR);
+ addProperty(MTP_PROPERTY_DISPLAY_NAME, 0, getName().c_str(), MTP_TYPE_STR);
+ addProperty(MTP_PROPERTY_DATE_ADDED, st.st_mtime, "", MTP_TYPE_UINT64);
+ addProperty(MTP_PROPERTY_DESCRIPTION, 0, "", MTP_TYPE_STR);
+ addProperty(MTP_PROPERTY_ARTIST, 0, "", MTP_TYPE_STR);
+ addProperty(MTP_PROPERTY_ALBUM_NAME, 0, "", MTP_TYPE_STR);
+ addProperty(MTP_PROPERTY_ALBUM_ARTIST, 0, "", MTP_TYPE_STR);
+ addProperty(MTP_PROPERTY_TRACK, 0, "", MTP_TYPE_UINT16);
+ addProperty(MTP_PROPERTY_ORIGINAL_RELEASE_DATE, 2014, "", MTP_TYPE_UINT64); // TODO: extract year from st.st_mtime?
+ addProperty(MTP_PROPERTY_DURATION, 0, "", MTP_TYPE_UINT32);
+ addProperty(MTP_PROPERTY_GENRE, 0, "", MTP_TYPE_STR);
+ addProperty(MTP_PROPERTY_COMPOSER, 0, "", MTP_TYPE_STR);
+ addProperty(MTP_PROPERTY_ARTIST, 0, "", MTP_TYPE_STR);
+ addProperty(MTP_PROPERTY_ALBUM_NAME, 0, "", MTP_TYPE_STR);
+ addProperty(MTP_PROPERTY_DURATION, 0, "", MTP_TYPE_UINT32);
+ addProperty(MTP_PROPERTY_DESCRIPTION, 0, "", MTP_TYPE_STR);
+}
diff --git a/mtp/twrpMtp.cpp b/mtp/twrpMtp.cpp
new file mode 100644
index 000000000..2b2de01cd
--- /dev/null
+++ b/mtp/twrpMtp.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <string>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <pthread.h>
+#include <utils/Errors.h>
+#include <utils/threads.h>
+#include "MtpTypes.h"
+#include "MtpPacket.h"
+#include "MtpDataPacket.h"
+#include "MtpDatabase.h"
+#include "MtpRequestPacket.h"
+#include "MtpResponsePacket.h"
+#include "mtp_MtpDatabase.hpp"
+#include "mtp_MtpServer.hpp"
+#include "twrpMtp.hpp"
+#include "MtpDebug.h"
+
+#ifdef TWRPMTP
+static void usage(std::string prg) {
+ printf("Usage: %s <OPTIONS>\n", prg.c_str());
+ printf("Options:\n");
+ printf("\t-h, --help\t\tShow Usage\n");
+ printf("\t-s1, --storage1 /path/to/dir\t\tDestination to first storage directory\n");
+ printf("\t-s2, --storage2 /path/to/dir\t\tDestination to first storage directory\n");
+ printf("\t-sN, --storageN /path/to/dir\t\tDestination to first storage directory\n");
+}
+
+int main(int argc, char* argv[]) {
+ printf("argc: %d\n", argc);
+ if (argc < 2) {
+ usage(argv[0]);
+ return 1;
+ }
+
+ std::vector <std::string> storages;
+
+ for (int i = 1; i < argc; ++i) {
+ std::string arg = argv[i];
+ if ((arg == "-h") || (arg == "--help")) {
+ usage(argv[0]);
+ }
+ else {
+ storages.push_back(arg);
+ }
+ }
+ printf("starting\n");
+ twmtp_MtpServer* mtp = new twmtp_MtpServer();
+ mtp->set_storages(storages);
+ mtp->start();
+ return 0;
+}
+#endif //def TWRPMTP
+
+twrpMtp::twrpMtp(int debug_enabled) {
+ if (debug_enabled)
+ MtpDebug::enableDebug();
+ mtpstorages = new storages;
+ mtp_read_pipe = -1;
+}
+
+int twrpMtp::start(void) {
+ MTPI("Starting MTP\n");
+ twmtp_MtpServer *mtp = new twmtp_MtpServer();
+ mtp->set_storages(mtpstorages);
+ mtp->set_read_pipe(mtp_read_pipe);
+ mtp->start();
+ return 0;
+}
+
+pthread_t twrpMtp::threadserver(void) {
+ pthread_t thread;
+ ThreadPtr mtpptr = &twrpMtp::start;
+ PThreadPtr p = *(PThreadPtr*)&mtpptr;
+ pthread_create(&thread, NULL, p, this);
+ return thread;
+}
+
+pid_t twrpMtp::forkserver(int mtppipe[2]) {
+ pid_t pid;
+ if ((pid = fork()) == -1) {
+ MTPE("MTP fork failed.\n");
+ return 0;
+ }
+ if (pid == 0) {
+ // Child process
+ close(mtppipe[1]); // Child closes write side
+ mtp_read_pipe = mtppipe[0];
+ start();
+ MTPD("MTP child process exited.\n");
+ close(mtppipe[0]);
+ _exit(0);
+ } else {
+ return pid;
+ }
+ return 0;
+}
+
+void twrpMtp::addStorage(std::string display, std::string path, int mtpid, uint64_t maxFileSize) {
+ s = new storage;
+ s->display = display;
+ s->mount = path;
+ s->mtpid = mtpid;
+ s->maxFileSize = maxFileSize;
+ MTPD("twrpMtp mtpid: %d\n", s->mtpid);
+ mtpstorages->push_back(s);
+}
diff --git a/mtp/twrpMtp.hpp b/mtp/twrpMtp.hpp
new file mode 100644
index 000000000..9ad270c72
--- /dev/null
+++ b/mtp/twrpMtp.hpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TWRPMTP_HPP
+#define TWRPMTP_HPP
+
+#include <fcntl.h>
+#include <utils/Errors.h>
+#include <utils/threads.h>
+#include <string>
+#include <vector>
+#include <pthread.h>
+#include "MtpTypes.h"
+#include "MtpPacket.h"
+#include "MtpDataPacket.h"
+#include "MtpDatabase.h"
+#include "MtpRequestPacket.h"
+#include "MtpResponsePacket.h"
+#include "mtp_MtpDatabase.hpp"
+
+class twrpMtp {
+ public:
+ twrpMtp(int debug_enabled = 0);
+ pthread_t threadserver(void);
+ pid_t forkserver(int mtppipe[2]);
+ void addStorage(std::string display, std::string path, int mtpid, uint64_t maxFileSize);
+ private:
+ int start(void);
+ typedef int (twrpMtp::*ThreadPtr)(void);
+ typedef void* (*PThreadPtr)(void *);
+ storages *mtpstorages;
+ storage *s;
+ int mtp_read_pipe;
+};
+#endif