summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbigbiff bigbiff <bigbiff@teamw.in>2014-09-03 00:59:01 +0200
committerbigbiff bigbiff <bigbiff@teamw.in>2014-09-03 03:44:49 +0200
commitc7eee6fef0263ed4eb5aa0d934a0a8c972d41b30 (patch)
treea994f2110aeeb58ef2bc1f23c31f695cd840ac46
parentFix derp (diff)
downloadandroid_bootable_recovery-c7eee6fef0263ed4eb5aa0d934a0a8c972d41b30.tar
android_bootable_recovery-c7eee6fef0263ed4eb5aa0d934a0a8c972d41b30.tar.gz
android_bootable_recovery-c7eee6fef0263ed4eb5aa0d934a0a8c972d41b30.tar.bz2
android_bootable_recovery-c7eee6fef0263ed4eb5aa0d934a0a8c972d41b30.tar.lz
android_bootable_recovery-c7eee6fef0263ed4eb5aa0d934a0a8c972d41b30.tar.xz
android_bootable_recovery-c7eee6fef0263ed4eb5aa0d934a0a8c972d41b30.tar.zst
android_bootable_recovery-c7eee6fef0263ed4eb5aa0d934a0a8c972d41b30.zip
-rw-r--r--Android.mk13
-rw-r--r--data.cpp8
-rw-r--r--etc/init.rc23
-rw-r--r--gui/Android.mk2
-rw-r--r--gui/action.cpp27
-rw-r--r--gui/devices/landscape/res/landscape.xml39
-rw-r--r--gui/devices/portrait/res/portrait.xml28
-rw-r--r--gui/devices/watch/res/watch.xml26
-rwxr-xr-xmtp/Android.mk65
-rwxr-xr-xmtp/MtpDataPacket.cpp488
-rwxr-xr-xmtp/MtpDataPacket.h123
-rwxr-xr-xmtp/MtpDatabase.h115
-rwxr-xr-xmtp/MtpDebug.cpp419
-rwxr-xr-xmtp/MtpDebug.h49
-rwxr-xr-xmtp/MtpDevice.cpp833
-rwxr-xr-xmtp/MtpDevice.h116
-rwxr-xr-xmtp/MtpDeviceInfo.cpp94
-rwxr-xr-xmtp/MtpDeviceInfo.h54
-rwxr-xr-xmtp/MtpEventPacket.cpp68
-rwxr-xr-xmtp/MtpEventPacket.h46
-rwxr-xr-xmtp/MtpObjectInfo.cpp105
-rwxr-xr-xmtp/MtpObjectInfo.h60
-rwxr-xr-xmtp/MtpPacket.cpp164
-rwxr-xr-xmtp/MtpPacket.h72
-rwxr-xr-xmtp/MtpProperty.cpp531
-rwxr-xr-xmtp/MtpProperty.h114
-rwxr-xr-xmtp/MtpRequestPacket.cpp61
-rwxr-xr-xmtp/MtpRequestPacket.h49
-rwxr-xr-xmtp/MtpResponsePacket.cpp58
-rwxr-xr-xmtp/MtpResponsePacket.h48
-rwxr-xr-xmtp/MtpServer.cpp1337
-rwxr-xr-xmtp/MtpServer.h155
-rwxr-xr-xmtp/MtpStorage.cpp941
-rwxr-xr-xmtp/MtpStorage.h103
-rwxr-xr-xmtp/MtpStorageInfo.cpp69
-rwxr-xr-xmtp/MtpStorageInfo.h49
-rwxr-xr-xmtp/MtpStringBuffer.cpp167
-rwxr-xr-xmtp/MtpStringBuffer.h55
-rwxr-xr-xmtp/MtpTypes.h78
-rwxr-xr-xmtp/MtpUtils.cpp78
-rwxr-xr-xmtp/MtpUtils.h27
-rwxr-xr-xmtp/btree.cpp308
-rwxr-xr-xmtp/btree.hpp94
-rwxr-xr-xmtp/mtp.h607
-rwxr-xr-xmtp/mtp_MtpDatabase.cpp884
-rwxr-xr-xmtp/mtp_MtpDatabase.hpp144
-rwxr-xr-xmtp/mtp_MtpServer.cpp143
-rwxr-xr-xmtp/mtp_MtpServer.hpp61
-rwxr-xr-xmtp/node.cpp139
-rwxr-xr-xmtp/twrpMtp.cpp98
-rwxr-xr-xmtp/twrpMtp.hpp46
-rw-r--r--partition.cpp34
-rw-r--r--partitionmanager.cpp115
-rw-r--r--partitions.hpp11
-rw-r--r--prebuilt/Android.mk10
-rw-r--r--twrp-functions.cpp18
-rw-r--r--twrp-functions.hpp1
-rw-r--r--twrp.cpp19
-rw-r--r--variables.h2
59 files changed, 9663 insertions, 28 deletions
diff --git a/Android.mk b/Android.mk
index 921a84bd9..ea6bb57c9 100644
--- a/Android.mk
+++ b/Android.mk
@@ -73,7 +73,8 @@ LOCAL_STATIC_LIBRARIES :=
LOCAL_SHARED_LIBRARIES :=
LOCAL_STATIC_LIBRARIES += libcrecovery libguitwrp
-LOCAL_SHARED_LIBRARIES += libz libc libstlport libcutils libstdc++ libtar libblkid libminuitwrp libminadbd libmtdutils libminzip libaosprecovery
+LOCAL_SHARED_LIBRARIES += libz libc libstlport libcutils libstdc++ libtar libblkid libminuitwrp libminadbd libmtdutils libminzip libaosprecovery libcorkscrew
+LOCAL_SHARED_LIBRARIES += libgccdemangle
ifneq ($(wildcard system/core/libsparse/Android.mk),)
LOCAL_SHARED_LIBRARIES += libsparse
@@ -84,6 +85,7 @@ ifeq ($(TW_OEM_BUILD),true)
BOARD_HAS_NO_REAL_SDCARD := true
TW_USE_TOOLBOX := true
TW_EXCLUDE_SUPERSU := true
+ TW_EXCLUDE_MTP := true
endif
ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
LOCAL_CFLAGS += -DUSE_EXT4
@@ -124,6 +126,10 @@ LOCAL_MODULE_TAGS := eng
LOCAL_C_INCLUDES += system/extras/ext4_utils
#TWRP Build Flags
+ifeq ($(TW_EXCLUDE_MTP),)
+ LOCAL_SHARED_LIBRARIES += libtwrpmtp
+ LOCAL_CFLAGS += -DTW_HAS_MTP
+endif
ifneq ($(TW_NO_SCREEN_TIMEOUT),)
LOCAL_CFLAGS += -DTW_NO_SCREEN_TIMEOUT
endif
@@ -373,7 +379,8 @@ include $(commands_recovery_local_path)/injecttwrp/Android.mk \
$(commands_recovery_local_path)/openaes/Android.mk \
$(commands_recovery_local_path)/toolbox/Android.mk \
$(commands_recovery_local_path)/libmincrypt/Android.mk \
- $(commands_recovery_local_path)/twrpTarMain/Android.mk
+ $(commands_recovery_local_path)/twrpTarMain/Android.mk \
+ $(commands_recovery_local_path)/mtp/Android.mk
ifeq ($(TW_INCLUDE_CRYPTO_SAMSUNG), true)
include $(commands_recovery_local_path)/crypto/libcrypt_samsung/Android.mk
@@ -411,5 +418,3 @@ endif
ifeq ($(TW_INCLUDE_FB2PNG), true)
include $(commands_recovery_local_path)/fb2png/Android.mk
endif
-
-commands_recovery_local_path :=
diff --git a/data.cpp b/data.cpp
index afa676d1e..7b85d2ca8 100644
--- a/data.cpp
+++ b/data.cpp
@@ -996,6 +996,14 @@ void DataManager::SetDefaultValues()
LOGINFO("TW_EXCLUDE_ENCRYPTED_BACKUPS := true\n");
mValues.insert(make_pair("tw_include_encrypted_backup", make_pair("0", 0)));
#endif
+#ifdef TW_HAS_MTP
+ mConstValues.insert(make_pair("tw_has_mtp", "1"));
+ mValues.insert(make_pair("tw_mtp_enabled", make_pair("1", 1)));
+#else
+ LOGINFO("TW_EXCLUDE_MTP := true\n");
+ mConstValues.insert(make_pair("tw_has_mtp", "0"));
+ mConstValues.insert(make_pair("tw_mtp_enabled", "0"));
+#endif
}
// Magic Values
diff --git a/etc/init.rc b/etc/init.rc
index 39724d066..dda009b84 100644
--- a/etc/init.rc
+++ b/etc/init.rc
@@ -23,7 +23,7 @@ on init
write /sys/class/android_usb/android0/enable 0
write /sys/class/android_usb/android0/idVendor 18D1
- write /sys/class/android_usb/android0/idProduct D001
+ write /sys/class/android_usb/android0/idProduct D002
write /sys/class/android_usb/android0/functions adb
write /sys/class/android_usb/android0/iManufacturer ${ro.product.manufacturer}
write /sys/class/android_usb/android0/iProduct ${ro.product.model}
@@ -51,6 +51,25 @@ on property:sys.storage.ums_enabled=0
write /sys/class/android_usb/android0/functions adb
write /sys/class/android_usb/android0/enable ${service.adb.root}
+on property:sys.usb.config=none
+ stop adbd
+ write /sys/class/android_usb/android0/enable 0
+ write /sys/class/android_usb/android0/bDeviceClass 0
+
+on property:sys.usb.config=mtp,adb
+ stop adbd
+ write /sys/class/android_usb/android0/enable 0
+ write /sys/class/android_usb/android0/functions mtp,adb
+ write /sys/class/android_usb/android0/enable 1
+ start adbd
+
+on property:sys.usb.config=adb
+ stop adbd
+ write /sys/class/android_usb/android0/enable 0
+ write /sys/class/android_usb/android0/functions adb
+ write /sys/class/android_usb/android0/enable ${service.adb.root}
+ start adbd
+
service ueventd /sbin/ueventd
critical
@@ -73,4 +92,4 @@ on property:ro.debuggable=1
on property:service.adb.root=1
write /sys/class/android_usb/android0/enable 0
restart adbd
- write /sys/class/android_usb/android0/enable 1
+ write /sys/class/android_usb/android0/enable 1 \ No newline at end of file
diff --git a/gui/Android.mk b/gui/Android.mk
index b172b2b25..3e499abe3 100644
--- a/gui/Android.mk
+++ b/gui/Android.mk
@@ -62,7 +62,7 @@ endif
ifeq ($(HAVE_SELINUX), true)
LOCAL_CFLAGS += -DHAVE_SELINUX
endif
-ifeq ($(TW_OEM_BUILD),true)
+ifeq ($(TW_OEM_BUILD), true)
LOCAL_CFLAGS += -DTW_OEM_BUILD
endif
diff --git a/gui/action.cpp b/gui/action.cpp
index c471533ef..7e432226e 100644
--- a/gui/action.cpp
+++ b/gui/action.cpp
@@ -1305,7 +1305,6 @@ int GUIAction::doAction(Action action, int isThreaded /* = 0 */)
} else {
ret = 1; // failure
}
- PartitionManager.Update_System_Details();
if (DataManager::GetIntValue(TW_HAS_INJECTTWRP) == 1 && DataManager::GetIntValue(TW_INJECT_AFTER_ZIP) == 1) {
operation_start("ReinjectTWRP");
gui_print("Injecting TWRP into boot image...\n");
@@ -1469,6 +1468,32 @@ int GUIAction::doAction(Action action, int isThreaded /* = 0 */)
operation_end(op_status, simulate);
return 0;
}
+ if (function == "startmtp")
+ {
+ int op_status = 0;
+
+ operation_start("Start MTP");
+ if (PartitionManager.Enable_MTP())
+ op_status = 0; // success
+ else
+ op_status = 1; // fail
+
+ operation_end(op_status, simulate);
+ return 0;
+ }
+ if (function == "stopmtp")
+ {
+ int op_status = 0;
+
+ operation_start("Stop MTP");
+ if (PartitionManager.Disable_MTP())
+ op_status = 0; // success
+ else
+ op_status = 1; // fail
+
+ operation_end(op_status, simulate);
+ return 0;
+ }
}
else
{
diff --git a/gui/devices/landscape/res/landscape.xml b/gui/devices/landscape/res/landscape.xml
index 2bed2a578..aea32f1b7 100644
--- a/gui/devices/landscape/res/landscape.xml
+++ b/gui/devices/landscape/res/landscape.xml
@@ -903,6 +903,45 @@
<object type="button">
<highlight color="%highlight_color%" />
+ <conditions>
+ <condition var1="tw_has_mtp" var2="1" />
+ <condition var1="tw_mtp_enabled" var2="0" />
+ </conditions>
+ <placement x="%col3_x%" y="row1_y" />
+ <font resource="font" color="%button_text_color%" />
+ <text>Enable MTP</text>
+ <image resource="main_button" />
+ <action function="startmtp"></action>
+ </object>
+
+ <object type="button">
+ <highlight color="%highlight_color%" />
+ <conditions>
+ <condition var1="tw_has_mtp" var2="1" />
+ <condition var1="tw_mtp_enabled" var2="1" />
+ </conditions>
+ <placement x="%col3_x%" y="row1_y" />
+ <font resource="font" color="%button_text_color%" />
+ <text>Disable MTP</text>
+ <image resource="main_button" />
+ <action function="stopmtp"></action>
+ </object>
+
+ <object type="button">
+ <highlight color="%highlight_color%" />
+ <conditions>
+ <condition var1="tw_is_encrypted" var2="1" />
+ <condition var1="tw_is_decrypted" var2="0" />
+ </conditions>
+ <placement x="%col3_x%" y="row1_y" />
+ <font resource="font" color="%button_text_color%" />
+ <text>Decrypt Data</text>
+ <image resource="main_button" />
+ <action function="page">decrypt</action>
+ </object>
+
+ <object type="button">
+ <highlight color="%highlight_color%" />
<fill color="%button_fill_color%" />
<placement x="%col3_x%" y="%backup_storage_y%" w="%button_fill_main_width%" h="%button_fill_half_height%" />
<font resource="font" color="%button_text_color%" />
diff --git a/gui/devices/portrait/res/portrait.xml b/gui/devices/portrait/res/portrait.xml
index 0be685517..e07a217bf 100644
--- a/gui/devices/portrait/res/portrait.xml
+++ b/gui/devices/portrait/res/portrait.xml
@@ -2146,6 +2146,32 @@
<object type="button">
<highlight color="%highlight_color%" />
<conditions>
+ <condition var1="tw_has_mtp" var2="1" />
+ <condition var1="tw_mtp_enabled" var2="0" />
+ </conditions>
+ <placement x="%col2_x%" y="row4_y" />
+ <font resource="font" color="%button_text_color%" />
+ <text>Enable MTP</text>
+ <image resource="main_button" />
+ <action function="startmtp"></action>
+ </object>
+
+ <object type="button">
+ <highlight color="%highlight_color%" />
+ <conditions>
+ <condition var1="tw_has_mtp" var2="1" />
+ <condition var1="tw_mtp_enabled" var2="1" />
+ </conditions>
+ <placement x="%col2_x%" y="row4_y" />
+ <font resource="font" color="%button_text_color%" />
+ <text>Disable MTP</text>
+ <image resource="main_button" />
+ <action function="stopmtp"></action>
+ </object>
+
+ <object type="button">
+ <highlight color="%highlight_color%" />
+ <conditions>
<condition var1="tw_is_encrypted" var2="1" />
<condition var1="tw_is_decrypted" var2="0" />
</conditions>
@@ -2201,6 +2227,7 @@
<object type="action">
<action function="mount">usb</action>
+ <action function="set">tw_busy=1</action>
</object>
<object type="template" name="footer" />
@@ -2213,6 +2240,7 @@
<object type="action">
<action function="page">mount</action>
+ <action function="set">tw_busy=0</action>
</object>
</page>
diff --git a/gui/devices/watch/res/watch.xml b/gui/devices/watch/res/watch.xml
index 686b24b3f..00f449ea8 100644
--- a/gui/devices/watch/res/watch.xml
+++ b/gui/devices/watch/res/watch.xml
@@ -2118,6 +2118,32 @@
<object type="button">
<highlight color="%highlight_color%" />
<conditions>
+ <condition var1="tw_has_mtp" var2="1" />
+ <condition var1="tw_mtp_enabled" var2="0" />
+ </conditions>
+ <placement x="%col2_x%" y="row4_y" />
+ <font resource="font" color="%button_text_color%" />
+ <text>Enable MTP</text>
+ <image resource="main_button" />
+ <action function="startmtp"></action>
+ </object>
+
+ <object type="button">
+ <highlight color="%highlight_color%" />
+ <conditions>
+ <condition var1="tw_has_mtp" var2="1" />
+ <condition var1="tw_mtp_enabled" var2="1" />
+ </conditions>
+ <placement x="%col2_x%" y="row4_y" />
+ <font resource="font" color="%button_text_color%" />
+ <text>Disable MTP</text>
+ <image resource="main_button" />
+ <action function="stopmtp"></action>
+ </object>
+
+ <object type="button">
+ <highlight color="%highlight_color%" />
+ <conditions>
<condition var1="tw_is_encrypted" var2="1" />
<condition var1="tw_is_decrypted" var2="0" />
</conditions>
diff --git a/mtp/Android.mk b/mtp/Android.mk
new file mode 100755
index 000000000..ebf1a6d36
--- /dev/null
+++ b/mtp/Android.mk
@@ -0,0 +1,65 @@
+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
+LOCAL_C_INCLUDES += $(LOCAL_PATH) bionic external/stlport/stlport 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++ libstlport libdl libcutils libutils
+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 external/stlport/stlport 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++ libstlport libdl libcutils libutils
+include $(BUILD_EXECUTABLE)
diff --git a/mtp/MtpDataPacket.cpp b/mtp/MtpDataPacket.cpp
new file mode 100755
index 000000000..2c517252a
--- /dev/null
+++ b/mtp/MtpDataPacket.cpp
@@ -0,0 +1,488 @@
+/*
+ * 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) {
+ struct usb_request *req = usb_request_wait(device);
+ 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 100755
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 100755
index 000000000..c25e9b24c
--- /dev/null
+++ b/mtp/MtpDatabase.h
@@ -0,0 +1,115 @@
+/*
+ * 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 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 100755
index 000000000..c3a3d8a5a
--- /dev/null
+++ b/mtp/MtpDebug.cpp
@@ -0,0 +1,419 @@
+/*
+ * 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 <stdarg.h>
+#include <stdio.h>
+
+#define MTP_DEBUG_BUFFER_SIZE 2048
+//#define MTP_DEBUG 1
+
+extern "C" void mtpdebug(const char *fmt, ...)
+{
+#ifdef MTP_DEBUG
+ 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);
+#endif
+}
+
+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);
+}
diff --git a/mtp/MtpDebug.h b/mtp/MtpDebug.h
new file mode 100755
index 000000000..12a23ccc8
--- /dev/null
+++ b/mtp/MtpDebug.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_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, __VA_ARGS__)
+#define MTPD(...) mtpdebug(__VA_ARGS__)
+#define MTPE(...) fprintf(stdout, "E:" __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);
+};
+
+
+#endif // _MTP_DEBUG_H
diff --git a/mtp/MtpDevice.cpp b/mtp/MtpDevice.cpp
new file mode 100755
index 000000000..53f8b2e9f
--- /dev/null
+++ b/mtp/MtpDevice.cpp
@@ -0,0 +1,833 @@
+/*
+ * 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
+
+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)
+ {
+ char* manufacturerName = usb_device_get_manufacturer_name(device);
+ char* productName = usb_device_get_product_name(device);
+ MTPD("Found camera: \"%s\" \"%s\"\n", manufacturerName, productName);
+ free(manufacturerName);
+ free(productName);
+ } else if (interface->bInterfaceClass == 0xFF &&
+ interface->bInterfaceSubClass == 0xFF &&
+ interface->bInterfaceProtocol == 0) {
+ char* interfaceName = usb_device_get_string(device, interface->iInterface);
+ if (!interfaceName) {
+ continue;
+ } else if (strcmp(interfaceName, "MTP")) {
+ free(interfaceName);
+ continue;
+ }
+ free(interfaceName);
+
+ // Looks like an android style MTP device
+ char* manufacturerName = usb_device_get_manufacturer_name(device);
+ char* productName = usb_device_get_product_name(device);
+ 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 100755
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 100755
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 100755
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 100755
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 100755
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/MtpObjectInfo.cpp b/mtp/MtpObjectInfo.cpp
new file mode 100755
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 100755
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 100755
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 100755
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 100755
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 100755
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 100755
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 100755
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 100755
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 100755
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 100755
index 000000000..5dec5801e
--- /dev/null
+++ b/mtp/MtpServer.cpp
@@ -0,0 +1,1337 @@
+/*
+ * 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 <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 <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(int fd, MtpDatabase* database, bool ptp,
+ int fileGroup, int filePerm, int directoryPerm)
+ : mFD(fd),
+ mDatabase(database),
+ mPtp(ptp),
+ mFileGroup(fileGroup),
+ mFilePermission(filePerm),
+ mDirectoryPermission(directoryPerm),
+ mSessionID(0),
+ mSessionOpen(false),
+ mSendObjectHandle(kInvalidObjectHandle),
+ mSendObjectFormat(0),
+ mSendObjectFileSize(0)
+{
+}
+
+MtpServer::~MtpServer() {
+}
+
+void MtpServer::addStorage(MtpStorage* storage) {
+ MTPD("addStorage(): storage: %x\n", storage);
+ mDatabase->createDB(storage, storage->getStorageID());
+ android::Mutex::Autolock autoLock(mMutex);
+ 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) {
+ mStorages.removeAt(i);
+ sendStoreRemoved(storage->getStorageID());
+ break;
+ }
+ }
+}
+
+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 = mFD;
+
+ MTPI("MtpServer::run fd: %d\n", fd);
+
+ while (1) {
+ MTPD("About to read device...\n");
+ int ret = mRequest.read(fd);
+ if (ret < 0) {
+ MTPD("request read returned %d, errno: %d", ret, errno);
+ if (errno == ECANCELED) {
+ // return to top of loop and wait for next command
+ continue;
+ }
+ 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) {
+ MTPD("data read returned %d, errno: %d", ret, errno);
+ if (errno == ECANCELED) {
+ // return to top of loop and wait for next command
+ continue;
+ }
+ 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) {
+ MTPD("request write returned %d, errno: %d", ret, errno);
+ if (errno == ECANCELED) {
+ // return to top of loop and wait for next command
+ continue;
+ }
+ 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) {
+ MTPD("request write returned %d, errno: %d", ret, errno);
+ if (errno == ECANCELED) {
+ // return to top of loop and wait for next command
+ continue;
+ }
+ 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();
+ 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);
+}
+
+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
+ 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);
+
+ // 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);
+
+ if (ret < 0) {
+ unlink(mSendObjectFilePath);
+ if (errno == ECANCELED)
+ result = MTP_RESPONSE_TRANSACTION_CANCELLED;
+ else
+ 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 MTP_RESPONSE_OK;
+}
+
+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;
+ if (entry->d_type == DT_DIR) {
+ 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 100755
index 000000000..61f5ccfac
--- /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(int fd, 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();
+
+ 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 100755
index 000000000..e20dab402
--- /dev/null
+++ b/mtp/MtpStorage.cpp
@@ -0,0 +1,941 @@
+/*
+ * 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 <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 <sstream>
+
+#define WATCH_FLAGS ( IN_CREATE | IN_DELETE | IN_MOVE | IN_MODIFY )
+
+static int mtpid = 0;
+
+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);
+ mtpparentid = 0;
+ inotify_thread = 0;
+ sendEvents = false;
+ 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");
+ use_mutex = false;
+ }
+
+}
+
+MtpStorage::~MtpStorage() {
+ if (inotify_thread) {
+ pthread_kill(inotify_thread, 0);
+ for (std::map<int, std::string>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
+ inotify_rm_watch(inotify_fd, i->first);
+ }
+ close(inotify_fd);
+ }
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+ delete i->second;
+ }
+ if (use_mutex) {
+ use_mutex = false;
+ 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();
+ readParentDirs(getPath());
+ while (!mtpParentList.empty()) {
+ mtpParent = mtpParentList.front();
+ mtpParentList.pop_front();
+ readParentDirs(mtpParent);
+ }
+ MTPD("MtpStorage::createDB DONE\n");
+ if (use_mutex) {
+ MTPD("Starting inotify thread\n");
+ sendEvents = true;
+ inotify_thread = inotify();
+ } else {
+ MTPD("NOT starting inotify thread\n");
+ }
+ return 0;
+}
+
+MtpObjectHandleList* MtpStorage::getObjectList(MtpStorageID storageID, MtpObjectHandle parent) {
+ std::vector<int> mtpids;
+ int local_mtpparentid;
+ MTPD("MtpStorage::getObjectList\n");
+ MTPD("parent: %d\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");
+ local_mtpparentid = 1;
+ }
+ else {
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) {
+ MTPD("root: %d\n", i->second->Root());
+ Node* node = i->second->findNode(parent, i->second->Root());
+ if (node != NULL) {
+ local_mtpparentid = i->second->getMtpParentId(node);
+ MTPD("path: %s\n", i->second->getPath(node).c_str());
+ MTPD("mtpparentid: %d going to endloop\n", local_mtpparentid);
+ goto endloop;
+ }
+ }
+ }
+ MTPD("got to endloop\n");
+ endloop:
+
+ if (mtpmap[local_mtpparentid] == NULL) {
+ MTPD("mtpmap[mtpparentid] == NULL, returning\n");
+ return list;
+ }
+
+ MTPD("root: %d\n", mtpmap[local_mtpparentid]->Root());
+ mtpmap[local_mtpparentid]->getmtpids(mtpmap[local_mtpparentid]->Root(), &mtpids);
+ MTPD("here, mtpids->size(): %i\n", mtpids.size());
+
+ for (unsigned index = 0; index < mtpids.size(); index++) {
+ MTPD("mtpidhere[%i]: %d\n", index, mtpids.at(index));
+ list->push(mtpids.at(index));
+ }
+ return list;
+}
+
+int MtpStorage::getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info) {
+ struct stat st;
+ uint64_t size;
+ MTPD("MtpStorage::getObjectInfo handle: %d\n", handle);
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+ Node* node = i->second->findNode(handle, i->second->Root());
+ MTPD("node returned: %d\n", node);
+ if (node != NULL) {
+ MTPD("found mtpid: %d\n", node->Mtpid());
+ info.mStorageID = getStorageID();
+ MTPD("info.mStorageID: %d\n", info.mStorageID);
+ info.mParent = node->getMtpParentId();
+ MTPD("mParent: %d\n", info.mParent);
+ lstat(node->getPath().c_str(), &st);
+ size = st.st_size;
+ MTPD("size is: %llu\n", size);
+ info.mCompressedSize = size;//(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(basename(node->getPath().c_str()));
+ MTPD("MtpStorage::getObjectInfo found, Exiting getObjectInfo()\n");
+ return 0;
+ }
+ }
+ MTPE("MtpStorage::getObjectInfo no object found, error!\n");
+ return -1;
+}
+
+MtpObjectHandle MtpStorage::beginSendObject(const char* path,
+ MtpObjectFormat format,
+ MtpObjectHandle parent,
+ MtpStorageID storage,
+ uint64_t size,
+ time_t modified) {
+ MTPD("MtpStorage::beginSendObject(), path: '%s', parent: %d, storage: %d, format: %04x\n", path, parent, storage, format);
+ Node* node;
+ std::string parentdir;
+ std::string pathstr(path);
+ int parent_id;
+ parentdir = pathstr.substr(0, pathstr.find_last_of('/'));
+ MTPD("MtpStorage::beginSendObject() parentdir: %s\n", parentdir.c_str());
+ if (parentdir.compare(mtpstorageparent) == 0) {
+ // root directory
+ MTPD("MtpStorage::beginSendObject() root dir\n");
+ parent_id = 1;
+ ++mtpid;
+ node = mtpmap[parent_id]->addNode(mtpid, path);
+ MTPD("node: %d\n", node);
+ node->addProperties(storage, 0);
+ if (format == MTP_FORMAT_ASSOCIATION) {
+ createEmptyDir(path);
+ }
+ return mtpid;
+ } else {
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+ node = i->second->findNodePath(parentdir, i->second->Root());
+ if (node != NULL) {
+ MTPD("mtpid: %d\n", mtpid);
+ MTPD("path: %s\n", i->second->getPath(node).c_str());
+ parentdir = i->second->getPath(node);
+ parent = i->second->getMtpParentId(node);
+ if (parent == 0) {
+ MTPD("MtpStorage::beginSendObject parent is 0, error.\n");
+ return -1;
+ } else {
+ ++mtpid;
+ node = mtpmap[parent]->addNode(mtpid, path);
+ node->addProperties(getStorageID(), getParentObject(parentdir));
+ for (iter i2 = mtpmap.begin(); i2 != mtpmap.end(); i2++) {
+ node = i2->second->findNodePath(path, i2->second->Root());
+ if (node != NULL) {
+ i2->second->setMtpParentId(parent, node);
+ }
+ }
+ if (format == MTP_FORMAT_ASSOCIATION) {
+ createEmptyDir(path);
+ }
+ }
+ return mtpid;
+ }
+ }
+ }
+ MTPE("MtpStorage::beginSendObject(), path: '%s', parent: %d, storage: %d, format: %04x\n", path, parent, storage, format);
+ return -1;
+}
+
+int MtpStorage::getObjectFilePath(MtpObjectHandle handle, MtpString& outFilePath, int64_t& outFileLength, MtpObjectFormat& outFormat) {
+ struct stat st;
+ Node* node;
+ MTPD("MtpStorage::getObjectFilePath handle: %i\n", handle);
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+ MTPD("handle: %d\n", handle);
+ node = i->second->findNode(handle, i->second->Root());
+ MTPD("node returned: %d\n", node);
+ if (node != NULL) {
+ lstat(node->getPath().c_str(), &st);
+ outFileLength = st.st_size;
+ outFilePath = strdup(node->getPath().c_str());
+ MTPD("outFilePath: %s\n", node->getPath().c_str());
+ goto end;
+ }
+ }
+ MTPE("MtpStorage::getObjectFilePath fauled to find handle: %i\n", handle);
+ return -1;
+end:
+ outFormat = MTP_FORMAT_ASSOCIATION;
+ return 0;
+}
+
+int MtpStorage::readParentDirs(std::string path) {
+ struct dirent *de;
+ struct stat st;
+ DIR *d;
+ std::string parent, item, prevparent = "";
+ Node* node;
+ int storageID = getStorageID();
+
+ d = opendir(path.c_str());
+ MTPD("opening '%s'\n", path.c_str());
+ if (d == NULL) {
+ MTPD("error opening '%s' -- error: %s\n", path.c_str(), strerror(errno));
+ closedir(d);
+ }
+ while ((de = readdir(d)) != NULL) {
+ if (de->d_type == DT_DIR && strcmp(de->d_name, ".") == 0)
+ continue;
+ if (de->d_type == DT_DIR && strcmp(de->d_name, "..") != 0) {
+ // Handle dirs
+ item = path + "/" + de->d_name;
+ MTPD("dir: %s\n", item.c_str());
+ mtpParentList.push_back(item);
+ parent = item.substr(0, item.find_last_of('/'));
+ ++mtpid;
+ MTPD("parent: %s\n", parent.c_str());
+ MTPD("mtpid: %d\n", mtpid);
+ if (prevparent != parent) {
+ mtpparentid++;
+ MTPD("Handle dirs, prevparent != parent, mtpparentid: %d\n", mtpparentid);
+ mtpmap[mtpparentid] = new Tree();
+ MTPD("prevparent addNode\n");
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) {
+ node = i->second->findNodePath(parent, i->second->Root());
+ if (node != NULL) {
+ i->second->setMtpParentId(mtpparentid, node);
+ }
+ }
+ node = mtpmap[mtpparentid]->addNode(mtpid, item);
+ node->addProperties(storageID, getParentObject(path));
+ if (sendEvents)
+ mServer->sendObjectAdded(mtpid);
+ }
+ else {
+ MTPD("add node\n");
+ mtpmap[mtpparentid]->addNode(mtpid, item);
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) {
+ node = i->second->findNodePath(item, i->second->Root());
+ if (node != NULL) {
+ i->second->setMtpParentId(mtpparentid, node);
+ node->addProperties(storageID, getParentObject(path));
+ }
+ }
+ if (sendEvents)
+ mServer->sendObjectAdded(mtpid);
+ }
+ prevparent = parent;
+ }
+ else {
+ if (strcmp(de->d_name, "..") != 0) {
+ // Handle files
+ item = path + "/" + de->d_name;
+ MTPD("file: %s\n", item.c_str());
+ parent = item.substr(0, item.find_last_of('/'));
+ MTPD("parent: %s\n", parent.c_str());
+ ++mtpid;
+ MTPD("mtpid: %d\n", mtpid);
+ if (prevparent != parent) {
+ mtpparentid++;
+ MTPD("mtpparentid1: %d\n", mtpparentid);
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) {
+ node = i->second->findNodePath(path, i->second->Root());
+ if (node != NULL) {
+ i->second->setMtpParentId(mtpparentid, node);
+ node->addProperties(storageID, getParentObject(path));
+ }
+ }
+ }
+ if (mtpmap[mtpparentid] == NULL) {
+ mtpmap[mtpparentid] = new Tree();
+ }
+ MTPD("blank addNode\n");
+ node = mtpmap[mtpparentid]->addNode(mtpid, item);
+ node->addProperties(storageID, getParentObject(path));
+ prevparent = parent;
+ if (sendEvents)
+ mServer->sendObjectAdded(mtpid);
+ }
+ else {
+ // Handle empty dirs?
+ MTPD("checking for empty dir '%s'\n", path.c_str());
+ int count = 0;
+ DIR *dirc;
+ struct dirent *ep;
+ dirc = opendir(path.c_str());
+ if (dirc != NULL) {
+ while ((ep = readdir(dirc)))
+ ++count;
+ MTPD("count: %d\n", count);
+ closedir(dirc);
+ }
+ if (count == 2) {
+ MTPD("'%s' is an empty dir\n", path.c_str());
+ createEmptyDir(path.c_str());
+ goto end;
+ }
+ }
+ }
+ }
+ end:
+ closedir(d);
+ return 0;
+}
+
+void MtpStorage::deleteTrees(int parent) {
+ Node* node = mtpmap[parent]->Root();
+ MTPD("MtpStorage::deleteTrees deleting %i\n", parent);
+ while (node != NULL) {
+ if (node->getIntProperty(MTP_PROPERTY_OBJECT_FORMAT) == MTP_FORMAT_ASSOCIATION) {
+ deleteTrees(node->getMtpParentId());
+ }
+ node = mtpmap[parent]->getNext(node);
+ }
+ delete mtpmap[parent];
+ mtpmap.erase(parent);
+ MTPD("MtpStorage::deleteTrees deleted %i\n", parent);
+}
+
+int MtpStorage::deleteFile(MtpObjectHandle handle) {
+ int local_parent_id = 0;
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+ MTPD("MtpStorage::deleteFile handle: %d\n", handle);
+ Node* node = i->second->findNode(handle, i->second->Root());
+ MTPD("MtpStorage::deleteFile node returned: %d\n", node);
+ if (node != NULL) {
+ if (node->getIntProperty(MTP_PROPERTY_OBJECT_FORMAT) == MTP_FORMAT_ASSOCIATION) {
+ local_parent_id = node->getMtpParentId();
+ }
+ MTPD("deleting handle: %d\n", handle);
+ i->second->deleteNode(handle);
+ MTPD("deleted\n");
+ goto end;
+ }
+ }
+ MTPE("MtpStorage::deleteFile deleting handle: %d FAILED\n", handle);
+ return -1;
+end:
+ if (local_parent_id) {
+ deleteTrees(local_parent_id);
+ }
+ return 0;
+}
+
+int MtpStorage::getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet) {
+ Node *n;
+ int local_mtpid = 0;
+ int local_mtpparentid = 0;
+ std::vector<int> propertyCodes;
+ std::vector<int> dataTypes;
+ std::vector<std::string> valueStrs;
+ std::vector<int> longValues;
+ int count = 0;
+ MTPD("MtpStorage::getObjectPropertyList handle: %d, format: %d, property: %lx\n", handle, format, property);
+ if (property == MTP_PROPERTY_OBJECT_FORMAT) {
+ MTPD("MtpStorage::getObjectPropertyList MTP_PROPERTY_OBJECT_FORMAT\n");
+ MTPD("mtpmap count: %d\n", mtpmap.size());
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+ MTPD("root: %d\n", i->second->Root());
+ Node *node = i->second->findNode(handle, i->second->Root());
+ MTPD("index: %d\n", index);
+ MTPD("node: %d\n", node);
+ if (node != NULL) {
+ uint64_t longval = node->getIntProperty(MTP_PROPERTY_OBJECT_FORMAT);
+ local_mtpparentid = i->second->getMtpParentId(node);
+ MTPD("object format longValue: %llu\n", longval);
+ propertyCodes.push_back(MTP_PROPERTY_OBJECT_FORMAT);
+ longValues.push_back(node->getIntProperty(MTP_PROPERTY_OBJECT_FORMAT));
+ valueStrs.push_back("");
+ dataTypes.push_back(4);
+ count = 1;
+ local_mtpid = node->Mtpid();
+ goto endloop;
+ }
+ }
+ }
+ else if (property == MTP_PROPERTY_STORAGE_ID) {
+ MTPD("MtpStorage::getObjectPropertyList MTP_PROPERTY_STORAGE_ID\n");
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+ MTPD("root: %d\n", i->second->Root());
+ Node *node = i->second->findNode(handle, i->second->Root());
+ if (node != NULL) {
+ propertyCodes.push_back(MTP_PROPERTY_STORAGE_ID);
+ longValues.push_back(getStorageID());
+ valueStrs.push_back("");
+ dataTypes.push_back(4);
+ count = 1;
+ local_mtpid = node->Mtpid();
+ goto endloop;
+ }
+ }
+ }
+ else if (property == MTP_PARENT_ROOT) {
+ MTPD("MtpStorage::getObjectPropertyList MTP_PARENT_ROOT\n");
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+ MTPD("root: %d\n", i->second->Root());
+ Node* node = i->second->findNode(handle, i->second->Root());
+ if (node != NULL) {
+ local_mtpparentid = i->second->getMtpParentId(node);
+ MTPD("path: %s\n", i->second->getPath(node).c_str());
+ MTPD("mtpparentid: %d going to endloop\n", local_mtpparentid);
+ std::vector<Node::mtpProperty> mtpprop = node->getMtpProps();
+ count = mtpprop.size();
+ for (int i = 0; i < count; ++i) {
+ propertyCodes.push_back(mtpprop[i].property);
+ longValues.push_back(mtpprop[i].valueInt);
+ valueStrs.push_back(mtpprop[i].valueStr);
+ dataTypes.push_back(mtpprop[i].dataType);
+ }
+ local_mtpid = node->Mtpid();
+ goto endloop;
+ }
+ }
+ }
+ else if (property == MTP_PROPERTY_PROTECTION_STATUS) {
+ MTPD("MtpStorage::getObjectPropertyList MTP_PROPERTY_PROTECTION_STATUS\n");
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+ MTPD("root: %d\n", i->second->Root());
+ Node *node = i->second->findNode(handle, i->second->Root());
+ if (node != NULL) {
+ propertyCodes.push_back(MTP_PROPERTY_PROTECTION_STATUS);
+ longValues.push_back(0);
+ valueStrs.push_back("");
+ dataTypes.push_back(8);
+ count = 1;
+ local_mtpid = node->Mtpid();
+ goto endloop;
+ }
+ }
+ }
+ else if (property == MTP_PROPERTY_OBJECT_SIZE) {
+ MTPD("MtpStorage::getObjectPropertyList MTP_PROPERTY_OBJECT_SIZE\n");
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+ MTPD("root: %d\n", i->second->Root());
+ Node *node = i->second->findNode(handle, i->second->Root());
+ if (node != NULL) {
+ struct stat st;
+ uint64_t size;
+ lstat(node->getPath().c_str(), &st);
+ size = st.st_size;
+ propertyCodes.push_back(MTP_PROPERTY_OBJECT_SIZE);
+ longValues.push_back(size);
+ valueStrs.push_back("");
+ dataTypes.push_back(8);
+ count = 1;
+ local_mtpid = node->Mtpid();
+ goto endloop;
+ }
+ }
+ }
+ else {
+ MTPE("MtpStorage::getObjectPropertyList unsupported property %x\n", property);
+ return -1;
+ }
+
+endloop:
+ MTPD("mtpparentid: %d\n", local_mtpparentid);
+ MTPD("count: %d\n", count);
+ packet.putUInt32(count);
+
+ if (count > 0) {
+ std::string stringValuesArray;
+ for (int i = 0; i < count; ++i) {
+ packet.putUInt32(local_mtpid);
+ packet.putUInt16(propertyCodes[i]);
+ MTPD("dataTypes: %d\n", dataTypes[i]);
+ packet.putUInt16(dataTypes[i]);
+ MTPD("propertyCode: %s\n", MtpDebug::getObjectPropCodeName(propertyCodes[i]));
+ MTPD("longValues: %d\n", longValues[i]);
+ switch (dataTypes[i]) {
+ case MTP_TYPE_INT8:
+ MTPD("MTP_TYPE_INT8\n");
+ packet.putInt8(longValues[i]);
+ break;
+ case MTP_TYPE_UINT8:
+ MTPD("MTP_TYPE_UINT8\n");
+ packet.putUInt8(longValues[i]);
+ break;
+ case MTP_TYPE_INT16:
+ MTPD("MTP_TYPE_INT16\n");
+ packet.putInt16(longValues[i]);
+ break;
+ case MTP_TYPE_UINT16:
+ MTPD("MTP_TYPE_UINT16\n");
+ packet.putUInt16(longValues[i]);
+ break;
+ case MTP_TYPE_INT32:
+ MTPD("MTP_TYPE_INT32\n");
+ packet.putInt32(longValues[i]);
+ break;
+ case MTP_TYPE_UINT32:
+ MTPD("MTP_TYPE_UINT32\n");
+ packet.putUInt32(longValues[i]);
+ break;
+ case MTP_TYPE_INT64:
+ MTPD("MTP_TYPE_INT64\n");
+ packet.putInt64(longValues[i]);
+ break;
+ case MTP_TYPE_UINT64:
+ MTPD("MTP_TYPE_UINT64\n");
+ packet.putUInt64(longValues[i]);
+ break;
+ case MTP_TYPE_INT128:
+ MTPD("MTP_TYPE_INT128\n");
+ packet.putInt128(longValues[i]);
+ break;
+ case MTP_TYPE_UINT128:
+ MTPD("MTP_TYPE_UINT128\n");
+ packet.putUInt128(longValues[i]);
+ break;
+ case MTP_TYPE_STR:
+ MTPD("MTP_TYPE_STR: %s\n", valueStrs[i].c_str());
+ packet.putString((const char*) valueStrs[i].c_str());
+ break;
+ default:
+ MTPE("bad or unsupported data type: %i in MyMtpDatabase::getObjectPropertyList", dataTypes[i]);
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+int MtpStorage::renameObject(MtpObjectHandle handle, std::string newName) {
+ int index;
+ MTPD("MtpStorage::renameObject, handle: %d, 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++) {
+ MTPD("root: %d\n", i->second->Root());
+ Node* node = i->second->findNode(handle, i->second->Root());
+ if (node != NULL) {
+ std::string oldName = i->second->getPath(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->setPath(newFullName);
+ return 0;
+ } else {
+ MTPE("MtpStorage::renameObject failed, handle: %d, new name: '%s'\n", handle, newName.c_str());
+ return -1;
+ }
+ }
+ }
+ }
+ MTPE("MtpStorage::renameObject handle / node not found, error!\n");
+ return -1;
+}
+
+void MtpStorage::createEmptyDir(const char* path) {
+ Node *node;
+ ++mtpparentid;
+ MtpStorageID storage = getStorageID();
+ MTPD("MtpStorage::createEmptyDir path: '%s', storage: %i, mtpparentid: %d\n", path, storage, mtpparentid);
+ mtpmap[mtpparentid] = new Tree();
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) {
+ node = i->second->findNodePath(path, i->second->Root());
+ if (node != NULL) {
+ mtpmap[mtpparentid]->setMtpParentId(mtpparentid, node);
+ }
+ }
+}
+
+int MtpStorage::getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, uint64_t &longValue) {
+ Node *node;
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+ node = i->second->findNode(handle, i->second->Root());
+ if (node != NULL) {
+ longValue = node->getIntProperty(property);
+ return 0;
+ }
+ }
+ MTPE("MtpStorage::getObjectPropertyValue unable to locate handle: %i\n", handle);
+ return -1;
+}
+pthread_t MtpStorage::inotify(void) {
+ pthread_t thread;
+ ThreadPtr inotifyptr = &MtpStorage::inotify_t;
+ PThreadPtr p = *(PThreadPtr*)&inotifyptr;
+ pthread_create(&thread, NULL, p, this);
+ return thread;
+}
+
+int MtpStorage::addInotifyDirs(std::string path) {
+ struct dirent *de;
+ DIR *d;
+ struct stat st;
+ std::string inotifypath;
+
+ d = opendir(path.c_str());
+ if (d == NULL) {
+ MTPE("MtpStorage::addInotifyDirs unable to open '%s'\n", path.c_str());
+ closedir(d);
+ return -1;
+ }
+
+ while ((de = readdir(d)) != NULL) {
+ if (de->d_type != DT_DIR || strcmp(de->d_name, ".") == 0 || strcmp(de->d_name, "..") == 0)
+ continue;
+ inotifypath = path + "/" + de->d_name;
+ if (addInotifyDirs(inotifypath)) {
+ closedir(d);
+ return -1;
+ }
+ inotify_wd = inotify_add_watch(inotify_fd, inotifypath.c_str(), IN_CREATE | IN_DELETE);
+ inotifymap[inotify_wd] = inotifypath;
+ MTPD("added inotify dir: '%s'\n", inotifypath.c_str());
+ }
+ closedir(d);
+ return 0;
+}
+
+int MtpStorage::inotify_t(void) {
+ int len, i = 0;
+ int local_mtpparentid;
+ Node* node = NULL;
+ struct stat st;
+ #define EVENT_SIZE ( sizeof(struct inotify_event) )
+ #define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16) )
+ char buf[EVENT_BUF_LEN];
+ std::string item, parent = "";
+
+ MTPD("starting inotify thread\n");
+ inotify_fd = inotify_init();
+
+ if (inotify_fd < 0){
+ MTPE("Can't run inotify for mtp server\n");
+ }
+
+ inotify_wd = inotify_add_watch(inotify_fd, getPath(), WATCH_FLAGS);
+ inotifymap[inotify_wd] = getPath();
+ if (addInotifyDirs(getPath())) {
+ MTPE("MtpStorage::inotify_t failed to add watches to directories\n");
+ for (std::map<int, std::string>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
+ inotify_rm_watch(inotify_fd, i->first);
+ }
+ close(inotify_fd);
+ return -1;
+ }
+
+ while (true) {
+ i = 0;
+ len = read(inotify_fd, buf, EVENT_BUF_LEN);
+
+ if (len < 0) {
+ MTPE("inotify_t Can't read inotify events\n");
+ }
+
+ while (i < len) {
+ struct inotify_event *event = ( struct inotify_event * ) &buf[ i ];
+ if ( event->len ) {
+ if (inotifymap[event->wd].empty()) {
+ MTPE("Unable to locate inotify_wd: %i\n", event->wd);
+ goto end;
+ } else {
+ item = inotifymap[event->wd];
+ item = item + "/" + event->name;
+ MTPD("inotify_t item: '%s'\n", item.c_str());
+ if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) {
+ lockMutex(1);
+ if (event->mask & IN_ISDIR) {
+ MTPD("inotify_t create is dir\n");
+ } else {
+ MTPD("inotify_t create is file\n");
+ }
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) {
+ node = i->second->findNodePath(item, i->second->Root());
+ if (node != NULL)
+ break;
+ }
+ if (node == NULL) {
+ parent = item.substr(0, item.find_last_of('/'));
+ MTPD("parent: %s\n", parent.c_str());
+ if (parent == getPath()) {
+ local_mtpparentid = 1;
+ } else {
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) {
+ node = i->second->findNodePath(parent, i->second->Root());
+ MTPD("searching for node: %d\n", (int)node);
+ if (node != NULL) {
+ local_mtpparentid = i->second->getMtpParentId(node);
+ break;
+ }
+ }
+ if (node == NULL) {
+ MTPE("inotify_t unable to locate mtparentid\n");
+ goto end;
+ }
+ }
+ ++mtpid;
+ MTPD("mtpid: %d\n", mtpid);
+ MTPD("mtpparentid1: %d\n", local_mtpparentid);
+ node = mtpmap[local_mtpparentid]->addNode(mtpid, item);
+ mtpmap[local_mtpparentid]->setMtpParentId(local_mtpparentid, node);
+ node->addProperties(getStorageID(), getParentObject(parent));
+ if (event->mask & IN_ISDIR) {
+ createEmptyDir(item.c_str());
+ }
+ mServer->sendObjectAdded(mtpid);
+ } else {
+ MTPD("inotify_t item already exists.\n");
+ }
+ if (event->mask & IN_ISDIR) {
+ inotify_wd = inotify_add_watch(inotify_fd, item.c_str(), WATCH_FLAGS);
+ inotifymap[inotify_wd] = item;
+ MTPD("added inotify dir: '%s'\n", item.c_str());
+ MTPD("inotify_t scanning new dir\n");
+ readParentDirs(item);
+ std::string mtpParent;
+ while (!mtpParentList.empty()) {
+ mtpParent = mtpParentList.front();
+ mtpParentList.pop_front();
+ readParentDirs(mtpParent);
+ inotify_wd = inotify_add_watch(inotify_fd, mtpParent.c_str(), WATCH_FLAGS);
+ inotifymap[inotify_wd] = mtpParent;
+ MTPD("added inotify dir: '%s'\n", mtpParent.c_str());
+ }
+ }
+ } else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) {
+ lockMutex(1);
+ if (event->mask & IN_ISDIR) {
+ MTPD("inotify_t Directory %s deleted\n", event->name);
+ } else {
+ MTPD("inotify_t File %s deleted\n", event->name);
+ }
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) {
+ node = i->second->findNodePath(item, i->second->Root());
+ if (node != NULL)
+ break;
+ }
+ if (node != NULL && node->Mtpid() > 0) {
+ int local_id = node->Mtpid();
+ node = NULL;
+ deleteFile(local_id);
+ mServer->sendObjectRemoved(local_id);
+ } else {
+ MTPD("inotify_t already removed.\n");
+ }
+ if (event->mask & IN_ISDIR) {
+ std::string orig_item = item + "/";
+ size_t item_size = orig_item.size();
+ std::string path_check;
+ for (std::map<int, std::string>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
+ if ((i->second.size() > item_size && i->second.substr(0, item_size) == orig_item) || i->second == item) {
+ inotify_rm_watch(inotify_fd, i->first);
+ MTPD("inotify_t removing watch on '%s'\n", i->second.c_str());
+ inotifymap.erase(i->first);
+ }
+ }
+ }
+ } else if (event->mask & IN_MODIFY) {
+ MTPD("inotify_t item %s modified.\n", event->name);
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) {
+ node = i->second->findNodePath(item, i->second->Root());
+ if (node != NULL)
+ break;
+ }
+ if (node != NULL) {
+ uint64_t orig_size = node->getIntProperty(MTP_PROPERTY_OBJECT_SIZE);
+ struct stat st;
+ lstat(item.c_str(), &st);
+ uint64_t new_size = (uint64_t)st.st_size;
+ if (orig_size != new_size) {
+ MTPD("size changed from %llu to %llu on mtpid: %i\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");
+ }
+ }
+ }
+ }
+end:
+ unlockMutex(1);
+ i += EVENT_SIZE + event->len;
+ }
+ }
+
+ for (std::map<int, std::string>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
+ inotify_rm_watch(inotify_fd, i->first);
+ }
+ close(inotify_fd);
+ return 0;
+}
+
+int MtpStorage::getParentObject(std::string parent_path) {
+ Node* node;
+ if (parent_path == getPath()) {
+ MTPD("MtpStorage::getParentObject for: '%s' returning: 0 for root\n", parent_path.c_str());
+ return 0;
+ }
+ for (iter i = mtpmap.begin(); i != mtpmap.end(); ++i) {
+ node = i->second->findNodePath(parent_path, i->second->Root());
+ if (node != NULL) {
+ MTPD("MtpStorage::getParentObject for: '%s' returning: %i\n", parent_path.c_str(), node->Mtpid());
+ return node->Mtpid();
+ }
+ }
+ MTPE("MtpStorage::getParentObject for: '%s' unable to locate node\n", parent_path.c_str());
+ return -1;
+}
+
+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 100755
index 000000000..ad828d163
--- /dev/null
+++ b/mtp/MtpStorage.h
@@ -0,0 +1,103 @@
+/*
+ * 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"
+
+class MtpDatabase;
+
+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;
+ std::deque<std::string> mtpParentList;
+ int mtpparentid;
+ Tree *mtpdbtree;
+ typedef std::map<int, Tree*> maptree;
+ typedef maptree::iterator iter;
+ maptree mtpmap;
+ std::string mtpstorageparent;
+ pthread_t inotify_thread;
+ int inotify_fd;
+ int inotify_wd;
+ 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; }
+ int readParentDirs(std::string path);
+ int createDB();
+ MtpObjectHandleList* getObjectList(MtpStorageID storageID, MtpObjectHandle parent);
+ int getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info);
+ MtpObjectHandle beginSendObject(const char* path, MtpObjectFormat format, MtpObjectHandle parent, MtpStorageID storage, uint64_t size, time_t modified);
+ 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, uint64_t &longValue);
+ void lockMutex(int thread_type);
+ void unlockMutex(int thread_type);
+
+private:
+ void createEmptyDir(const char* path);
+ pthread_t inotify();
+ int inotify_t();
+ typedef int (MtpStorage::*ThreadPtr)(void);
+ typedef void* (*PThreadPtr)(void *);
+ std::map<int, std::string> inotifymap;
+ int addInotifyDirs(std::string path);
+ void deleteTrees(int parent);
+ bool sendEvents;
+ int getParentObject(std::string parent_path);
+ bool use_mutex;
+ pthread_mutex_t inMutex; // inotify mutex
+ pthread_mutex_t mtpMutex; // main mtp mutex
+};
+
+#endif // _MTP_STORAGE_H
diff --git a/mtp/MtpStorageInfo.cpp b/mtp/MtpStorageInfo.cpp
new file mode 100755
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 100755
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 100755
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 100755
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 100755
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 100755
index 000000000..5fa32f271
--- /dev/null
+++ b/mtp/MtpUtils.cpp
@@ -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++
+ */
+
+#include <stdio.h>
+#include <time.h>
+
+#include <cutils/tztime.h>
+#include "MtpUtils.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);
+ 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 100755
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 100755
index 000000000..7976ac325
--- /dev/null
+++ b/mtp/btree.cpp
@@ -0,0 +1,308 @@
+/*
+ * 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 <iostream>
+#include <utils/threads.h>
+#include "btree.hpp"
+#include "MtpDebug.h"
+
+// Constructor
+Tree::Tree() {
+ root = NULL;
+ count = 0;
+}
+
+// Destructor
+Tree::~Tree() {
+ freeNode(root);
+}
+
+// Free the node
+void Tree::freeNode(Node* leaf)
+{
+ if ( leaf != NULL )
+ {
+ freeNode(leaf->Left());
+ freeNode(leaf->Right());
+ delete leaf;
+ }
+}
+
+int Tree::getCount(void) {
+ MTPD("node count: %d\n", count);
+ return count;
+}
+
+Node* Tree::addNode(int mtpid, std::string path)
+{
+ MTPD("root: %d\n", root);
+ // No elements. Add the root
+ if ( root == NULL ) {
+ Node* n = new Node();
+ count++;
+ MTPD("node count: %d\n", count);
+ MTPD("adding node address: %d\n", n);
+ MTPD("adding mtpid: %d\n", mtpid);
+ n->setMtpid(mtpid);
+ n->setPath(path);
+ root = n;
+ MTPD("set root to %d\n", root);
+ return n;
+ }
+ else {
+ count++;
+ MTPD("node count: %d\n", count);
+ MTPD("adding new child node\n");
+ return addNode(mtpid, root, path);
+ }
+}
+
+// Add a node (private)
+Node* Tree::addNode(int mtpid, Node* leaf, std::string path) {
+ Node* n;
+ if ( mtpid <= leaf->Mtpid() )
+ {
+ if ( leaf->Left() != NULL )
+ return addNode(mtpid, leaf->Left(), path);
+ else {
+ n = new Node();
+ MTPD("adding mtpid: %d node: %d\n", mtpid, n);
+ n->setMtpid(mtpid);
+ n->setPath(path);
+ n->setParent(leaf);
+ leaf->setLeft(n);
+ }
+ }
+ else
+ {
+ if ( leaf->Right() != NULL )
+ return addNode(mtpid, leaf->Right(), path);
+ else {
+ n = new Node();
+ MTPD("adding mtpid: %d node: %d\n", mtpid, n);
+ n->setMtpid(mtpid);
+ n->setPath(path);
+ n->setParent(leaf);
+ leaf->setRight(n);
+ }
+ }
+ return n;
+}
+
+void Tree::setMtpParentId(int mtpparentid, Node* node) {
+ node->setMtpParentId(mtpparentid);
+}
+
+std::string Tree::getPath(Node* node) {
+ return node->getPath();
+}
+
+int Tree::getMtpParentId(Node* node) {
+ return node->getMtpParentId();
+}
+
+Node* Tree::findNodePath(std::string path, Node* node) {
+ Node* n;
+ if ( node == NULL ) {
+ return NULL;
+ }
+ if ( node->getPath().compare(path) == 0 && node->Mtpid() > 0) {
+ return node;
+ }
+ else {
+ n = findNodePath(path, node->Left());
+ if (n)
+ return n;
+ n = findNodePath(path, node->Right());
+ if (n)
+ return n;
+ }
+ return NULL;
+}
+
+Node* Tree::getNext(Node *node) {
+ if (node == NULL)
+ return NULL;
+ else {
+ if (node->Left() != NULL)
+ return node->Left();
+ if (node->Right() != NULL)
+ return node->Right();
+ }
+ return NULL;
+}
+
+Node* Tree::findNode(int key, Node* node) {
+ //MTPD("key: %d\n", key);
+ //MTPD("node: %d\n", node);
+ if ( node == NULL ) {
+ return NULL;
+ }
+ else if ( node->Mtpid() == key ) {
+ return node;
+ }
+ else if ( key <= node->Mtpid() ) {
+ return findNode(key, node->Left());
+ }
+ else if ( key > node->Mtpid() ) {
+ return findNode(key, node->Right());
+ }
+ else {
+ return NULL;
+ }
+ return NULL;
+}
+
+void Tree::getmtpids(Node* node, std::vector<int>* mtpids)
+{
+ if ( node )
+ {
+ MTPD("node: %d\n", node->Mtpid());
+ mtpids->push_back(node->Mtpid());
+ if (node->Left())
+ getmtpids(node->Left(), mtpids);
+ if (node->Right())
+ getmtpids(node->Right(), mtpids);
+ } else {
+ mtpids->push_back(0);
+ }
+ return;
+}
+
+// Find the node with min key
+// Traverse the left sub-tree recursively
+// till left sub-tree is empty to get min
+Node* Tree::min(Node* node)
+{
+ if ( node == NULL )
+ return NULL;
+
+ if ( node->Left() )
+ min(node->Left());
+ else
+ return node;
+ return NULL;
+}
+
+// Find the node with max key
+// Traverse the right sub-tree recursively
+// till right sub-tree is empty to get max
+Node* Tree::max(Node* node)
+{
+ if ( node == NULL )
+ return NULL;
+
+ if ( node->Right() )
+ max(node->Right());
+ else
+ return node;
+ return NULL;
+}
+
+// Find successor to a node
+// Find the node, get the node with max value
+// for the right sub-tree to get the successor
+Node* Tree::successor(int key, Node *node)
+{
+ Node* thisKey = findNode(key, node);
+ if ( thisKey )
+ return max(thisKey->Right());
+ return NULL;
+}
+
+// Find predecessor to a node
+// Find the node, get the node with max value
+// for the left sub-tree to get the predecessor
+Node* Tree::predecessor(int key, Node *node)
+{
+ Node* thisKey = findNode(key, node);
+ if ( thisKey )
+ return max(thisKey->Left());
+ return NULL;
+}
+
+void Tree::deleteNode(int key)
+{
+ // Find the node.
+ Node* thisKey = findNode(key, root);
+ MTPD("Tree::deleteNode found node: %d\n", thisKey);
+ MTPD("handle: %d\n", thisKey->Mtpid());
+
+ if (thisKey == root) {
+ if (thisKey->Right()) {
+ root = thisKey->Right();
+ root->setParent(NULL);
+ return;
+ }
+ if (thisKey->Left()) {
+ root = thisKey->Left();
+ root->setParent(NULL);
+ return;
+ }
+ root = NULL;
+ delete thisKey;
+ return;
+ }
+
+ if ( thisKey->Left() == NULL && thisKey->Right() == NULL )
+ {
+ if ( thisKey->Mtpid() > thisKey->Parent()->Mtpid() ) {
+ thisKey->Parent()->setRight(NULL);
+ }
+ else {
+ thisKey->Parent()->setLeft(NULL);
+ }
+ delete thisKey;
+ return;
+ }
+
+ if ( thisKey->Left() == NULL && thisKey->Right() != NULL )
+ {
+ if ( thisKey->Mtpid() > thisKey->Parent()->Mtpid() )
+ thisKey->Parent()->setRight(thisKey->Right());
+ else
+ thisKey->Parent()->setLeft(thisKey->Right());
+ thisKey->Right()->setParent(thisKey->Parent());
+ delete thisKey;
+ return;
+ }
+ if ( thisKey->Left() != NULL && thisKey->Right() == NULL )
+ {
+ if ( thisKey->Mtpid() > thisKey->Parent()->Mtpid() )
+ thisKey->Parent()->setRight(thisKey->Left());
+ else
+ thisKey->Parent()->setLeft(thisKey->Left());
+ thisKey->Left()->setParent(thisKey->Parent());
+ delete thisKey;
+ return;
+ }
+
+ if ( thisKey->Left() != NULL && thisKey->Right() != NULL )
+ {
+ Node* sub = predecessor(thisKey->Mtpid(), thisKey);
+ if ( sub == NULL )
+ sub = successor(thisKey->Mtpid(), thisKey);
+
+ if ( sub->Parent()->Mtpid() <= sub->Mtpid() )
+ sub->Parent()->setRight(sub->Right());
+ else
+ sub->Parent()->setLeft(sub->Left());
+
+ thisKey->setMtpid(sub->Mtpid());
+ delete sub;
+ return;
+ }
+}
diff --git a/mtp/btree.hpp b/mtp/btree.hpp
new file mode 100755
index 000000000..d6bd0c380
--- /dev/null
+++ b/mtp/btree.hpp
@@ -0,0 +1,94 @@
+/*
+ * 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 <iostream>
+#include <vector>
+#include <utils/threads.h>
+#include "MtpDebug.h"
+
+// A generic tree node class
+class Node {
+ int mtpid;
+ int mtpparentid;
+ std::string path;
+ int parentID;
+ Node* left;
+ Node* right;
+ Node* parent;
+
+public:
+ Node();
+ void setMtpid(int aMtpid);
+ void setPath(std::string aPath);
+ void setLeft(Node* aLeft);
+ void setRight(Node* aRight);
+ void setParent(Node* aParent);
+ void setMtpParentId(int id);
+ int Mtpid();
+ int getMtpParentId();
+ std::string getPath();
+ Node* Left();
+ Node* Right();
+ Node* Parent();
+ void addProperty(uint64_t property, uint64_t valueInt, std::string valueStr, int dataType);
+ void updateProperty(uint64_t property, uint64_t valueInt, std::string valueStr, int dataType);
+ void addProperties(int storageID, int parent_object);
+ uint64_t getIntProperty(uint64_t property);
+ struct mtpProperty {
+ uint64_t property;
+ uint64_t valueInt;
+ std::string valueStr;
+ int dataType;
+ };
+ std::vector<mtpProperty>& getMtpProps();
+ std::vector<mtpProperty> mtpProp;
+};
+
+// Binary Search Tree class
+class Tree {
+ Node* root;
+public:
+ Tree();
+ ~Tree();
+ Node* Root() {
+ MTPD("root: %d\n", root);
+ return root;
+ };
+ Node* addNode(int mtpid, std::string path);
+ void setMtpParentId(int mtpparentid, Node* node);
+ Node* findNode(int key, Node* parent);
+ void getmtpids(Node* node, std::vector<int>* mtpids);
+ void deleteNode(int key);
+ Node* min(Node* node);
+ Node* max(Node* node);
+ Node* successor(int key, Node* parent);
+ Node* predecessor(int key, Node* parent);
+ std::string getPath(Node* node);
+ int getMtpParentId(Node* node);
+ Node* findNodePath(std::string path, Node* node);
+ Node* getNext(Node* node);
+ int getCount();
+
+private:
+ Node* addNode(int mtpid, Node* leaf, std::string path);
+ void freeNode(Node* leaf);
+ int count;
+};
+
+#endif
diff --git a/mtp/mtp.h b/mtp/mtp.h
new file mode 100755
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 100755
index 000000000..60a871ce3
--- /dev/null
+++ b/mtp/mtp_MtpDatabase.cpp
@@ -0,0 +1,884 @@
+/*
+ * 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::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,
+ 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) {
+ MTPD("MyMtpDatabase::beginSendObject() which passes to MtpStorage.cpp\n");
+ return storagemap[storage]->beginSendObject(path, format, parent, storage, 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);
+ }
+}
+
+void MyMtpDatabase::createDB(MtpStorage* storage, MtpStorageID storageID) {
+ storagemap[storageID] = storage;
+ storage->createDB();
+}
+
+MtpObjectHandleList* MyMtpDatabase::getObjectList(MtpStorageID storageID,
+ MtpObjectFormat format,
+ MtpObjectHandle parent) {
+ MTPD("storageID: %d\n", storageID);
+ MtpObjectHandleList* list = new MtpObjectHandleList();
+ list = storagemap[storageID]->getObjectList(storageID, parent);
+ MTPD("list: %d\n", list->size());
+ return list;
+}
+
+int MyMtpDatabase::getNumObjects(MtpStorageID storageID,
+ MtpObjectFormat format,
+ MtpObjectHandle parent) {
+ MTPE("MyMtpDatabase::getNumObjects not implemented, returning 0\n");
+ int result = 0;
+ //get number of objects on filesystem storage
+ return result;
+}
+
+MtpObjectFormatList* MyMtpDatabase::getSupportedPlaybackFormats() {
+ // This function tells the host PC which file formats the device supports
+ int* formats;
+ MtpObjectFormatList* list = new MtpObjectFormatList();
+ formats = SUPPORTED_PLAYBACK_FORMATS;
+ int length = sizeof(formats)/ sizeof(int);
+ MTPD("MyMtpDatabase::getSupportedPlaybackFormats length: %i\n", length);
+ for (int i = 0; i < length; i++) {
+ MTPD("supported playback format: %d\n", formats[i]);
+ list->push(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();
+ properties = FILE_PROPERTIES;
+ int length = sizeof(FILE_PROPERTIES);
+ MTPD("MyMtpDatabase::getSupportedObjectProperties length is: %i, format: %x, sizeof: %i, forcing length to 10\n", length, format, sizeof(properties));
+ for (int i = 0; i < length; i++) {
+ MTPD("supported object property: %x\n", properties[i]);
+ list->push(properties[i]);
+ }
+ return list;
+}
+
+MtpDevicePropertyList* MyMtpDatabase::getSupportedDeviceProperties() {
+ int properties[] = {
+ MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,
+ MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,
+ MTP_DEVICE_PROPERTY_IMAGE_SIZE,
+ };
+ MtpDevicePropertyList* list = new MtpDevicePropertyList();
+ int length = sizeof(properties) / sizeof(int);
+ MTPD("MyMtpDatabase::getSupportedDeviceProperties length was: %i, forcing to 3\n", length);
+ length = 3;
+ for (int i = 0; i < length; i++)
+ list->push(properties[i]);
+ return list;
+}
+
+int* MyMtpDatabase::getSupportedObjectProperties(int format) {
+ switch (format) {
+ case MTP_FORMAT_MP3:
+ case MTP_FORMAT_WAV:
+ case MTP_FORMAT_WMA:
+ case MTP_FORMAT_OGG:
+ case MTP_FORMAT_AAC:
+ return AUDIO_PROPERTIES;
+ case MTP_FORMAT_MPEG:
+ case MTP_FORMAT_3GP_CONTAINER:
+ case MTP_FORMAT_WMV:
+ return VIDEO_PROPERTIES;
+ case MTP_FORMAT_EXIF_JPEG:
+ case MTP_FORMAT_GIF:
+ case MTP_FORMAT_PNG:
+ case MTP_FORMAT_BMP:
+ return IMAGE_PROPERTIES;
+ case 0:
+ return ALL_PROPERTIES;
+ default:
+ return FILE_PROPERTIES;
+ }
+}
+
+MtpResponseCode MyMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle,
+ MtpObjectProperty property,
+ MtpDataPacket& packet) {
+ MTPD("MyMtpDatabase::getObjectPropertyValue mtpid: %i, property: %x\n", handle, property);
+ int type;
+ MtpResponseCode result;
+ uint64_t longValue;
+ if (!getObjectPropertyInfo(property, type)) {
+ MTPE("MyMtpDatabase::setObjectPropertyValue 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, longValue) == 0) {
+ result = MTP_RESPONSE_OK;
+ break;
+ }
+ }
+
+ // 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.putInt128(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();
+ }*/
+ 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 getObhectFilePath\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
+ MTPD("MyMtpDatabase::getObjectPropertyDesc format\n");
+ result = new MtpProperty(property, MTP_TYPE_UINT16, false, format);
+ break;
+ case MTP_PROPERTY_PROTECTION_STATUS:
+ case MTP_PROPERTY_TRACK:
+ MTPD("MyMtpDatabase::getObjectPropertyDesc track\n");
+ result = new MtpProperty(property, MTP_TYPE_UINT16);
+ break;
+ case MTP_PROPERTY_STORAGE_ID:
+ case MTP_PROPERTY_PARENT_OBJECT:
+ case MTP_PROPERTY_DURATION:
+ MTPD("MyMtpDatabase::getObjectPropertyDesc duration\n");
+ result = new MtpProperty(property, MTP_TYPE_UINT32);
+ break;
+ case MTP_PROPERTY_OBJECT_SIZE:
+ MTPD("MyMtpDatabase::getObjectPropertyDesc size\n");
+ result = new MtpProperty(property, MTP_TYPE_UINT64);
+ break;
+ case MTP_PROPERTY_PERSISTENT_UID:
+ MTPD("MyMtpDatabase::getObjectPropertyDesc persistent uid\n");
+ 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:
+ MTPD("MyMtpDatabase::getObjectPropertyDesc description\n");
+ result = new MtpProperty(property, MTP_TYPE_STR);
+ break;
+ case MTP_PROPERTY_DATE_MODIFIED:
+ case MTP_PROPERTY_DATE_ADDED:
+ case MTP_PROPERTY_ORIGINAL_RELEASE_DATE:
+ MTPD("MyMtpDatabase::getObjectPropertyDesc date\n");
+ result = new MtpProperty(property, MTP_TYPE_STR);
+ result->setFormDateTime();
+ break;
+ case MTP_PROPERTY_OBJECT_FILE_NAME:
+ MTPD("MyMtpDatabase::getObjectPropertyDesc file name\n");
+ // 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 = true;
+ switch (property) {
+ case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
+ case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
+ ret = MTP_RESPONSE_OK;
+ // fall through
+ case MTP_DEVICE_PROPERTY_IMAGE_SIZE:
+ result = new MtpProperty(property, MTP_TYPE_STR, writable);
+ ret = MTP_RESPONSE_OK;
+
+ // get current value
+ if (ret == MTP_RESPONSE_OK) {
+ MTPD("here\n");
+ result->setCurrentValue('\0');
+ result->setDefaultValue('\0');
+ } else {
+ MTPE("unable to read device property, response: %04X", ret);
+ }
+ break;
+ default:
+ ret = MTP_RESPONSE_DEVICE_PROP_NOT_SUPPORTED;
+ 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;
+}
+
+// ----------------------------------------------------------------------------
+
+static void
+android_mtp_MtpDatabase_setup()
+{
+ MyMtpDatabase* database = new MyMtpDatabase();
+}
+
+static void
+android_mtp_MtpDatabase_finalize()
+{
+ return;
+}
+
+static std::string
+android_mtp_MtpPropertyGroup_format_date_time(long seconds)
+{
+ char date[20];
+ formatDateTime(seconds, date, sizeof(date));
+ return date;
+}
+
+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 100755
index 000000000..2f5e931ba
--- /dev/null
+++ b/mtp/mtp_MtpDatabase.hpp
@@ -0,0 +1,144 @@
+/*
+ * 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 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);
+ 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 100755
index 000000000..8565d5401
--- /dev/null
+++ b/mtp/mtp_MtpServer.cpp
@@ -0,0 +1,143 @@
+/*
+ * 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 "mtp_MtpServer.hpp"
+#include "MtpServer.h"
+#include "MtpStorage.h"
+#include "MtpDebug.h"
+
+#include <string>
+
+void twmtp_MtpServer::start()
+{
+ setup();
+ add_storage();
+ server->run();
+}
+
+void twmtp_MtpServer::set_storages(storages* mtpstorages) {
+ stores = mtpstorages;
+}
+
+void twmtp_MtpServer::setup()
+{
+ #define USB_MTP_DEVICE "/dev/mtp_usb"
+ usePtp = false;
+ MyMtpDatabase* mtpdb = new MyMtpDatabase();
+#ifdef USB_MTP_DEVICE
+ int fd = open(USB_MTP_DEVICE, O_RDWR);
+#else
+ int fd = open("/dev/mtp_usb", O_RDWR);
+#endif
+ if (fd >= 0) {
+ MTPD("fd: %d\n", fd);
+ server = new MtpServer(fd, mtpdb, usePtp, 0, 0664, 0775);
+ refserver = server;
+ MTPI("created new mtpserver object\n");
+ } else {
+ MTPE("could not open MTP driver, errno: %d", errno);
+ }
+}
+
+void twmtp_MtpServer::run()
+{
+ MTPD("running in twmtp\n");
+ server->run();
+}
+
+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);
+
+ MTPI("adding internal storage\n");
+ 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;
+ long maxFileSize = 1000000000L;
+ 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) {
+ server->removeStorage(storage);
+ delete storage;
+ }
+ } else
+ MTPD("server is null in remove_storage");
+}
diff --git a/mtp/mtp_MtpServer.hpp b/mtp/mtp_MtpServer.hpp
new file mode 100755
index 000000000..360d1c376
--- /dev/null
+++ b/mtp/mtp_MtpServer.hpp
@@ -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++
+ */
+
+#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;
+} storage;
+
+typedef std::vector<storage*> storages;
+
+class twmtp_MtpServer {
+ public:
+ void start();
+ void setup();
+ void run();
+ 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);
+ storages *stores;
+ private:
+ bool usePtp;
+ MtpServer* server;
+ MtpServer* refserver;
+
+};
+#endif
diff --git a/mtp/node.cpp b/mtp/node.cpp
new file mode 100755
index 000000000..a9c1f9bae
--- /dev/null
+++ b/mtp/node.cpp
@@ -0,0 +1,139 @@
+/*
+ * 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 <iostream>
+#include <vector>
+#include <sstream>
+#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() {
+ mtpid= -1;
+ path = "";
+ left = NULL;
+ right = NULL;
+ parent = NULL;
+ mtpparentid = 0;
+}
+
+void Node::setMtpid(int aMtpid) { mtpid = aMtpid; }
+void Node::setPath(std::string aPath) { path = aPath; }
+void Node::setLeft(Node* aLeft) { left = aLeft; }
+void Node::setRight(Node* aRight) { right = aRight; }
+void Node::setParent(Node* aParent) { parent = aParent; }
+void Node::setMtpParentId(int id) {
+ mtpparentid = id;
+ MTPD("setting mtpparentid: %i on mtpid: %i\n", mtpparentid, mtpid);
+}
+int Node::Mtpid() { return mtpid; }
+int Node::getMtpParentId() { return mtpparentid; }
+std::string Node::getPath() { return path; }
+Node* Node::Left() { return left; }
+Node* Node::Right() { return right; }
+Node* Node::Parent() { return parent; }
+
+uint64_t Node::getIntProperty(uint64_t 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;
+}
+
+void Node::addProperty(uint64_t property, uint64_t valueInt, std::string valueStr, int dataType) {
+ MTPD("adding str 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(uint64_t property, uint64_t valueInt, std::string valueStr, int 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(int storageID, int parent_object) {
+ struct stat st;
+ int mFormat = 0;
+ uint64_t puid;
+
+ std::string mtpidStr = static_cast<std::ostringstream*>( &(std::ostringstream() << mtpid) )->str();
+ std::string storageIDStr = static_cast<std::ostringstream*>( &(std::ostringstream() << storageID) )->str();
+ std::string puidStr = storageIDStr + mtpidStr;
+ if ( ! (std::istringstream(puidStr) >> puid) ) puid = 0;
+ stat(getPath().c_str(), &st);
+ std::string mtimeStr = static_cast<std::ostringstream*>( &(std::ostringstream() << st.st_mtime) )->str();
+ std::string atimeStr = static_cast<std::ostringstream*>( &(std::ostringstream() << st.st_atime) )->str();
+ if (S_ISDIR(st.st_mode)) {
+ mFormat = MTP_FORMAT_ASSOCIATION; // folder
+ }
+ else {
+ mFormat = MTP_FORMAT_UNDEFINED; // file
+ }
+ 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, st.st_size, "", MTP_TYPE_UINT64);
+ addProperty(MTP_PROPERTY_OBJECT_FILE_NAME, 0, basename(getPath().c_str()), MTP_TYPE_STR);
+ MTPD("mtpid: %i, filename: '%s', parent object: %i\n", mtpid, basename(getPath().c_str()), parent_object);
+ addProperty(MTP_PROPERTY_DATE_MODIFIED, 0, mtimeStr, MTP_TYPE_STR);
+ addProperty(MTP_PROPERTY_PARENT_OBJECT, parent_object, "", MTP_TYPE_UINT32);
+ addProperty(MTP_PROPERTY_PERSISTENT_UID, puid, "", MTP_TYPE_UINT128);
+ addProperty(MTP_PROPERTY_NAME, 0, basename(getPath().c_str()), MTP_TYPE_STR);
+ addProperty(MTP_PROPERTY_DISPLAY_NAME, 0, basename(getPath().c_str()), MTP_TYPE_STR);
+ addProperty(MTP_PROPERTY_DATE_ADDED, 0, atimeStr, MTP_TYPE_STR);
+ 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, 0, "00101T000000", MTP_TYPE_STR);
+ 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 100755
index 000000000..ff58f79a7
--- /dev/null
+++ b/mtp/twrpMtp.cpp
@@ -0,0 +1,98 @@
+/*
+ * 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() {
+ mtpstorages = new storages;
+}
+
+int twrpMtp::start(void) {
+ MTPI("Starting MTP\n");
+ twmtp_MtpServer *mtp = new twmtp_MtpServer();
+ mtp->set_storages(mtpstorages);
+ mtp->start();
+ return 0;
+}
+
+pthread_t twrpMtp::runserver(void) {
+ pthread_t thread;
+ ThreadPtr mtpptr = &twrpMtp::start;
+ PThreadPtr p = *(PThreadPtr*)&mtpptr;
+ pthread_create(&thread, NULL, p, this);
+ return thread;
+}
+
+void twrpMtp::addStorage(std::string display, std::string path, int mtpid) {
+ s = new storage;
+ s->display = display;
+ s->mount = path;
+ s->mtpid = mtpid;
+ MTPD("twrpMtp mtpid: %d\n", s->mtpid);
+ mtpstorages->push_back(s);
+}
diff --git a/mtp/twrpMtp.hpp b/mtp/twrpMtp.hpp
new file mode 100755
index 000000000..2dd56a226
--- /dev/null
+++ b/mtp/twrpMtp.hpp
@@ -0,0 +1,46 @@
+/*
+ * 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();
+ pthread_t runserver(void);
+ void addStorage(std::string display, std::string path, int mtpid);
+ private:
+ int start(void);
+ typedef int (twrpMtp::*ThreadPtr)(void);
+ typedef void* (*PThreadPtr)(void *);
+ storages *mtpstorages;
+ storage *s;
+};
+#endif
diff --git a/partition.cpp b/partition.cpp
index 1a62bbae8..f1d7fd2a9 100644
--- a/partition.cpp
+++ b/partition.cpp
@@ -107,7 +107,8 @@ static struct flag_list mount_flags[] = {
{ 0, 0 },
};
-TWPartition::TWPartition(void) {
+TWPartition::TWPartition(int *id) {
+ initmtpid = id;
Can_Be_Mounted = false;
Can_Be_Wiped = false;
Can_Be_Backed_Up = false;
@@ -430,6 +431,12 @@ bool TWPartition::Process_Fstab_Line(string Line, bool Display_Error) {
}
}
+ // Generate MTP ID
+ if (Is_Storage) {
+ (*initmtpid)++;
+ mtpid = *initmtpid;
+ }
+
// Process any custom flags
if (Flags.size() > 0)
Process_Flags(Flags, Display_Error);
@@ -1051,6 +1058,9 @@ bool TWPartition::UnMount(bool Display_Error) {
if (never_unmount_system == 1 && Mount_Point == "/system")
return true; // Never unmount system if you're not supposed to unmount it
+ if (Is_Storage)
+ TWFunc::Toggle_MTP(false);
+
#ifdef TW_INCLUDE_CRYPTO_SAMSUNG
if (EcryptFS_Password.size() > 0) {
if (unmount_ecryptfs_drive(Mount_Point.c_str()) != 0) {
@@ -1074,15 +1084,16 @@ bool TWPartition::UnMount(bool Display_Error) {
else
LOGINFO("Unable to unmount '%s'\n", Mount_Point.c_str());
return false;
- } else
+ } else {
return true;
+ }
} else {
return true;
}
}
bool TWPartition::Wipe(string New_File_System) {
- bool wiped = false, update_crypt = false, recreate_media = true;
+ bool wiped = false, update_crypt = false, recreate_media = true, mtp_toggle = true;
int check;
string Layout_Filename = Mount_Point + "/.layout_version";
@@ -1109,8 +1120,8 @@ bool TWPartition::Wipe(string New_File_System) {
if (Has_Data_Media && Current_File_System == New_File_System) {
wiped = Wipe_Data_Without_Wiping_Media();
recreate_media = false;
+ mtp_toggle = false;
} else {
-
DataManager::GetValue(TW_RM_RF_VAR, check);
if (check || Use_Rm_Rf)
@@ -1128,6 +1139,9 @@ bool TWPartition::Wipe(string New_File_System) {
else if (New_File_System == "f2fs")
wiped = Wipe_F2FS();
else {
+ if (Is_Storage) {
+ TWFunc::Toggle_MTP(true);
+ }
LOGERR("Unable to wipe '%s' -- unknown file system '%s'\n", Mount_Point.c_str(), New_File_System.c_str());
unlink("/.layout_version");
return false;
@@ -1169,6 +1183,9 @@ bool TWPartition::Wipe(string New_File_System) {
Recreate_Media_Folder();
}
}
+ if (Is_Storage && mtp_toggle) {
+ TWFunc::Toggle_MTP(true);
+ }
return wiped;
}
@@ -1417,6 +1434,8 @@ bool TWPartition::Wipe_Encryption() {
Decrypted_Block_Device = "";
Is_Decrypted = false;
Is_Encrypted = false;
+ Find_Actual_Block_Device();
+ bool mtp_was_enabled = TWFunc::Toggle_MTP(false);
if (Wipe(Fstab_File_System)) {
Has_Data_Media = Save_Data_Media;
if (Has_Data_Media && !Symlink_Mount_Point.empty()) {
@@ -1425,6 +1444,7 @@ bool TWPartition::Wipe_Encryption() {
#ifndef TW_OEM_BUILD
gui_print("You may need to reboot recovery to be able to use /data again.\n");
#endif
+ TWFunc::Toggle_MTP(mtp_was_enabled);
return true;
} else {
Has_Data_Media = Save_Data_Media;
@@ -1632,6 +1652,8 @@ bool TWPartition::Wipe_MTD() {
}
bool TWPartition::Wipe_RMRF() {
+ if (Is_Storage)
+ TWFunc::Toggle_MTP(false);
if (!Mount(true))
return false;
@@ -2018,9 +2040,9 @@ bool TWPartition::Update_Size(bool Display_Error) {
}
void TWPartition::Find_Actual_Block_Device(void) {
- if (Is_Decrypted) {
+ if (Is_Decrypted && !Decrypted_Block_Device.empty()) {
Actual_Block_Device = Decrypted_Block_Device;
- if (TWFunc::Path_Exists(Primary_Block_Device))
+ if (TWFunc::Path_Exists(Decrypted_Block_Device))
Is_Present = true;
} else if (TWFunc::Path_Exists(Primary_Block_Device)) {
Is_Present = true;
diff --git a/partitionmanager.cpp b/partitionmanager.cpp
index de727f4a2..59451e3e7 100644
--- a/partitionmanager.cpp
+++ b/partitionmanager.cpp
@@ -38,6 +38,11 @@
#include "twrpDigest.hpp"
#include "twrpDU.hpp"
+#ifdef TW_HAS_MTP
+#include "mtp/mtp_MtpServer.hpp"
+#include "mtp/twrpMtp.hpp"
+#endif
+
extern "C" {
#include "cutils/properties.h"
}
@@ -51,6 +56,8 @@ extern "C" {
#endif
TWPartitionManager::TWPartitionManager(void) {
+ mtpid = 100;
+ mtp_was_enabled = false;
}
int TWPartitionManager::Process_Fstab(string Fstab_Filename, bool Display_Error) {
@@ -71,8 +78,7 @@ int TWPartitionManager::Process_Fstab(string Fstab_Filename, bool Display_Error)
if (fstab_line[strlen(fstab_line) - 1] != '\n')
fstab_line[strlen(fstab_line)] = '\n';
-
- TWPartition* partition = new TWPartition();
+ TWPartition* partition = new TWPartition(&mtpid);
string line = fstab_line;
memset(fstab_line, 0, sizeof(fstab_line));
@@ -1297,15 +1303,25 @@ int TWPartitionManager::Wipe_Media_From_Data(void) {
return false;
gui_print("Wiping internal storage -- /data/media...\n");
+ mtp_was_enabled = TWFunc::Toggle_MTP(false);
TWFunc::removeDir("/data/media", false);
- if (mkdir("/data/media", S_IRWXU | S_IRWXG | S_IWGRP | S_IXGRP) != 0)
- return -1;
+ if (mkdir("/data/media", S_IRWXU | S_IRWXG | S_IWGRP | S_IXGRP) != 0) {
+ if (mtp_was_enabled) {
+ if (!Enable_MTP())
+ Disable_MTP();
+ }
+ return false;
+ }
if (dat->Has_Data_Media) {
dat->Recreate_Media_Folder();
// Unmount and remount - slightly hackish way to ensure that the "/sdcard" folder is still mounted properly after wiping
dat->UnMount(false);
dat->Mount(false);
}
+ if (mtp_was_enabled) {
+ if (!Enable_MTP())
+ Disable_MTP();
+ }
return true;
} else {
LOGERR("Unable to locate /data.\n");
@@ -1735,20 +1751,22 @@ int TWPartitionManager::usb_storage_enable(void) {
if (TWFunc::Path_Exists(lun_file))
has_multiple_lun = true;
}
+ mtp_was_enabled = TWFunc::Toggle_MTP(false);
if (!has_multiple_lun) {
LOGINFO("Device doesn't have multiple lun files, mount current storage\n");
sprintf(lun_file, CUSTOM_LUN_FILE, 0);
if (TWFunc::Get_Root_Path(DataManager::GetCurrentStoragePath()) == "/data") {
TWPartition* Mount = Find_Next_Storage("", "/data");
if (Mount) {
- if (!Open_Lun_File(Mount->Mount_Point, lun_file))
- return false;
+ if (!Open_Lun_File(Mount->Mount_Point, lun_file)) {
+ goto error_handle;
+ }
} else {
LOGERR("Unable to find storage partition to mount to USB\n");
- return false;
+ goto error_handle;
}
} else if (!Open_Lun_File(DataManager::GetCurrentStoragePath(), lun_file)) {
- return false;
+ goto error_handle;
}
} else {
LOGINFO("Device has multiple lun files\n");
@@ -1757,8 +1775,9 @@ int TWPartitionManager::usb_storage_enable(void) {
sprintf(lun_file, CUSTOM_LUN_FILE, 0);
Mount1 = Find_Next_Storage("", "/data");
if (Mount1) {
- if (!Open_Lun_File(Mount1->Mount_Point, lun_file))
- return false;
+ if (!Open_Lun_File(Mount1->Mount_Point, lun_file)) {
+ goto error_handle;
+ }
sprintf(lun_file, CUSTOM_LUN_FILE, 1);
Mount2 = Find_Next_Storage(Mount1->Mount_Point, "/data");
if (Mount2) {
@@ -1766,11 +1785,16 @@ int TWPartitionManager::usb_storage_enable(void) {
}
} else {
LOGERR("Unable to find storage partition to mount to USB\n");
- return false;
+ goto error_handle;
}
}
property_set("sys.storage.ums_enabled", "1");
return true;
+error_handle:
+ if (mtp_was_enabled)
+ if (!Enable_MTP())
+ Disable_MTP();
+ return false;
}
int TWPartitionManager::usb_storage_disable(void) {
@@ -1789,6 +1813,9 @@ int TWPartitionManager::usb_storage_disable(void) {
Update_System_Details();
UnMount_Main_Partitions();
property_set("sys.storage.ums_enabled", "0");
+ if (mtp_was_enabled)
+ if (!Enable_MTP())
+ Disable_MTP();
if (ret < 0 && index == 0) {
LOGERR("Unable to write to ums lunfile '%s'.", lun_file);
return false;
@@ -2123,3 +2150,69 @@ TWPartition *TWPartitionManager::Get_Default_Storage_Partition()
}
return res;
}
+
+bool TWPartitionManager::Enable_MTP(void) {
+#ifdef TW_HAS_MTP
+ if (mtpthread) {
+ LOGERR("MTP already enabled\n");
+ return true;
+ }
+ //Launch MTP Responder
+ LOGINFO("Starting MTP\n");
+ char vendor[PROPERTY_VALUE_MAX];
+ char product[PROPERTY_VALUE_MAX];
+ int count = 0;
+ property_set("sys.usb.config", "none");
+ property_get("usb.vendor", vendor, "18D1");
+ property_get("usb.product.mtpadb", product, "4EE2");
+ string vendorstr = vendor;
+ string productstr = product;
+ TWFunc::write_file("/sys/class/android_usb/android0/idVendor", vendorstr);
+ TWFunc::write_file("/sys/class/android_usb/android0/idProduct", productstr);
+ property_set("sys.usb.config", "mtp,adb");
+ std::vector<TWPartition*>::iterator iter;
+ twrpMtp *mtp = new twrpMtp();
+ for (iter = Partitions.begin(); iter != Partitions.end(); iter++) {
+ if ((*iter)->Is_Storage && (*iter)->Is_Present && (*iter)->Mount(false)) {
+ printf("twrp mtpid: %d\n", (*iter)->mtpid);
+ mtp->addStorage((*iter)->Storage_Name, (*iter)->Storage_Path, (*iter)->mtpid);
+ count++;
+ }
+ }
+ if (count) {
+ mtpthread = mtp->runserver();
+ DataManager::SetValue("tw_mtp_enabled", 1);
+ return true;
+ }
+ LOGERR("No valid storage partitions found for MTP.\n");
+#else
+ LOGERR("MTP support not included\n");
+#endif
+ DataManager::SetValue("tw_mtp_enabled", 0);
+ return false;
+}
+
+bool TWPartitionManager::Disable_MTP(void) {
+#ifdef TW_HAS_MTP
+ char vendor[PROPERTY_VALUE_MAX];
+ char product[PROPERTY_VALUE_MAX];
+ property_set("sys.usb.config", "none");
+ property_get("usb.vendor", vendor, "18D1");
+ property_get("usb.product.adb", product, "D002");
+ string vendorstr = vendor;
+ string productstr = product;
+ TWFunc::write_file("/sys/class/android_usb/android0/idVendor", vendorstr);
+ TWFunc::write_file("/sys/class/android_usb/android0/idProduct", productstr);
+ if (mtpthread) {
+ pthread_kill(mtpthread, 0);
+ mtpthread = NULL;
+ }
+ property_set("sys.usb.config", "adb");
+ DataManager::SetValue("tw_mtp_enabled", 0);
+ return true;
+#else
+ LOGERR("MTP support not included\n");
+ DataManager::SetValue("tw_mtp_enabled", 0);
+ return false;
+#endif
+}
diff --git a/partitions.hpp b/partitions.hpp
index 0d74315b4..00f435a79 100644
--- a/partitions.hpp
+++ b/partitions.hpp
@@ -45,7 +45,7 @@ public:
};
public:
- TWPartition();
+ TWPartition(int *id);
virtual ~TWPartition();
public:
@@ -72,6 +72,7 @@ public:
string Current_File_System; // Current file system
string Actual_Block_Device; // Actual block device (one of primary, alternate, or decrypted)
string MTD_Name; // Name of the partition for MTD devices
+ bool Is_Present; // Indicates if the partition is currently present as a block device
private:
bool Process_Fstab_Line(string Line, bool Display_Error); // Processes a fstab line
@@ -127,8 +128,9 @@ private:
string Alternate_Block_Device; // Alternate block device (e.g. /dev/block/mmcblk1)
string Decrypted_Block_Device; // Decrypted block device available after decryption
bool Removable; // Indicates if this partition is removable -- affects how often we check overall size, if present, etc.
- bool Is_Present; // Indicates if the partition is currently present as a block device
int Length; // Used by make_ext4fs to leave free space at the end of the partition block for things like a crypto footer
+ int *initmtpid; // Initial MTP ID
+ int mtpid; // Store MTP ID
unsigned long long Size; // Overall size of the partition
unsigned long long Used; // Overall used space
unsigned long long Free; // Overall free space
@@ -224,6 +226,8 @@ public:
void Get_Partition_List(string ListType, std::vector<PartitionList> *Partition_List);
int Fstab_Processed(); // Indicates if the fstab has been processed or not
void Output_Storage_Fstab(); // Creates a /cache/recovery/storage.fstab file with a list of all potential storage locations for app use
+ bool Enable_MTP(); // Enables MTP
+ bool Disable_MTP(); // Disables MTP
private:
void Setup_Settings_Storage_Partition(TWPartition* Part); // Sets up settings storage
@@ -234,6 +238,9 @@ private:
void Output_Partition(TWPartition* Part);
TWPartition* Find_Next_Storage(string Path, string Exclude);
int Open_Lun_File(string Partition_Path, string Lun_File);
+ int mtpid;
+ pthread_t mtpthread;
+ bool mtp_was_enabled;
private:
std::vector<TWPartition*> Partitions; // Vector list of all partitions
diff --git a/prebuilt/Android.mk b/prebuilt/Android.mk
index 8a29e14e8..9431c945e 100644
--- a/prebuilt/Android.mk
+++ b/prebuilt/Android.mk
@@ -26,8 +26,13 @@ RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/e2fsck
RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/mke2fs
RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/tune2fs
RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/linker
+#RELINK_SOURCE_FILES += $(TARGET_OUT_EXECUTABLES)/twrpmtp
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libc.so
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcutils.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libcorkscrew.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libusbhost.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libgccdemangle.so
+RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libutils.so
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libdl.so
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libext2_blkid.so
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libext2_com_err.so
@@ -53,6 +58,11 @@ RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libstlport.so
RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/toolbox
ifneq ($(TW_OEM_BUILD),true)
RELINK_SOURCE_FILES += $(TARGET_RECOVERY_ROOT_OUT)/sbin/twrp
+else
+ TW_EXCLUDE_MTP := true
+endif
+ifneq ($(TW_EXCLUDE_MTP), true)
+ RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libtwrpmtp.so
endif
ifeq ($(TARGET_USERIMAGES_USE_EXT4), true)
RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libext4_utils.so
diff --git a/twrp-functions.cpp b/twrp-functions.cpp
index 885d352db..1798c49b7 100644
--- a/twrp-functions.cpp
+++ b/twrp-functions.cpp
@@ -1260,4 +1260,22 @@ int TWFunc::Set_Brightness(std::string brightness_value)
return -1;
}
+bool TWFunc::Toggle_MTP(bool enable) {
+#ifdef TW_HAS_MTP
+ static int was_enabled = false;
+
+ if (enable && was_enabled) {
+ if (!PartitionManager.Enable_MTP())
+ PartitionManager.Disable_MTP();
+ } else {
+ was_enabled = DataManager::GetIntValue("tw_mtp_enabled");
+ PartitionManager.Disable_MTP();
+ usleep(500);
+ }
+ return was_enabled;
+#else
+ return false;
+#endif
+}
+
#endif // ndef BUILD_TWRPTAR_MAIN
diff --git a/twrp-functions.hpp b/twrp-functions.hpp
index 5f46d4ff4..c46738e37 100644
--- a/twrp-functions.hpp
+++ b/twrp-functions.hpp
@@ -85,6 +85,7 @@ public:
static std::vector<std::string> Split_String(const std::string& str, const std::string& delimiter, bool removeEmpty = true); // Splits string by delimiter
static bool Create_Dir_Recursive(const std::string& path, mode_t mode = 0755, uid_t uid = -1, gid_t gid = -1); // Create directory and it's parents, if they don't exist. mode, uid and gid are set to all _newly_ created folders. If whole path exists, do nothing.
static int Set_Brightness(std::string brightness_value); // Well, you can read, it does what it says, passing return int from TWFunc::Write_File ;)
+ static bool Toggle_MTP(bool enable); // Disables MTP if enable is false and re-enables MTP if enable is true and it was enabled the last time it was toggled off
private:
static void Copy_Log(string Source, string Destination);
diff --git a/twrp.cpp b/twrp.cpp
index 8b60ee1db..5f58e41aa 100644
--- a/twrp.cpp
+++ b/twrp.cpp
@@ -82,6 +82,8 @@ int main(int argc, char **argv) {
crash_counter = atoi(crash_prop_val) + 1;
snprintf(crash_prop_val, sizeof(crash_prop_val), "%d", crash_counter);
property_set("twrp.crash_counter", crash_prop_val);
+ property_set("ro.twrp.boot", "1");
+ property_set("ro.twrp.version", TW_VERSION_STR);
time_t StartupTime = time(NULL);
printf("Starting TWRP %s on %s", TW_VERSION_STR, ctime(&StartupTime));
@@ -279,6 +281,23 @@ int main(int argc, char **argv) {
OpenRecoveryScript::Run_OpenRecoveryScript();
}
+ // Enable MTP?
+ if (DataManager::GetIntValue(TW_IS_ENCRYPTED) != 0) {
+ if (DataManager::GetIntValue(TW_IS_DECRYPTED) != 0 && DataManager::GetIntValue("tw_mtp_enabled") == 1) {
+ LOGINFO("Enabling MTP during startup\n");
+ if (!PartitionManager.Enable_MTP())
+ PartitionManager.Disable_MTP();
+ else
+ gui_print("MTP Enabled\n");
+ }
+ } else if (DataManager::GetIntValue("tw_mtp_enabled") == 1) {
+ LOGINFO("Enabling MTP during startup\n");
+ if (!PartitionManager.Enable_MTP())
+ PartitionManager.Disable_MTP();
+ else
+ gui_print("MTP Enabled\n");
+ }
+
// Launch the main GUI
gui_start();
diff --git a/variables.h b/variables.h
index 4e5de4426..1c1957f4e 100644
--- a/variables.h
+++ b/variables.h
@@ -17,7 +17,7 @@
#ifndef _VARIABLES_HEADER_
#define _VARIABLES_HEADER_
-#define TW_VERSION_STR "2.7.1.0"
+#define TW_VERSION_STR "2.8.0.0"
#define TW_USE_COMPRESSION_VAR "tw_use_compression"
#define TW_FILENAME "tw_filename"