diff options
Diffstat (limited to 'mtp')
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 |