Merge "Update FDE decrypt to pie from CAF" into android-9.0
diff --git a/Android.mk b/Android.mk
index 0bd225b..920b390 100755
--- a/Android.mk
+++ b/Android.mk
@@ -210,9 +210,19 @@
 endif
 LOCAL_CFLAGS += -DTW_GIT_REVISION='"$(tw_git_revision)"'
 
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
+ifeq ($(TW_EXCLUDE_MTP),)
+    LOCAL_SHARED_LIBRARIES += libtwrpmtp-ffs
+endif
+else
+ifeq ($(TW_EXCLUDE_MTP),)
+    LOCAL_CFLAGS += -DTW_HAS_LEGACY_MTP
+    LOCAL_SHARED_LIBRARIES += libtwrpmtp-legacy
+endif
+endif
+
 #TWRP Build Flags
 ifeq ($(TW_EXCLUDE_MTP),)
-    LOCAL_SHARED_LIBRARIES += libtwrpmtp
     LOCAL_CFLAGS += -DTW_HAS_MTP
 endif
 ifneq ($(TW_NO_SCREEN_TIMEOUT),)
@@ -281,6 +291,9 @@
 ifeq ($(TW_HAS_DOWNLOAD_MODE), true)
     LOCAL_CFLAGS += -DTW_HAS_DOWNLOAD_MODE
 endif
+ifeq ($(TW_HAS_EDL_MODE), true)
+    LOCAL_CFLAGS += -DTW_HAS_EDL_MODE
+endif
 ifeq ($(TW_NO_SCREEN_BLANK), true)
     LOCAL_CFLAGS += -DTW_NO_SCREEN_BLANK
 endif
@@ -780,6 +793,12 @@
 include $(commands_TWRP_local_path)/bootloader_message/Android.mk
 endif
 
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
+    include $(commands_TWRP_local_path)/mtp/ffs/Android.mk
+else
+    include $(commands_TWRP_local_path)/mtp/legacy/Android.mk
+endif
+
 ifeq ($(wildcard system/core/uncrypt/Android.mk),)
     #include $(commands_TWRP_local_path)/uncrypt/Android.mk
 endif
@@ -815,7 +834,6 @@
     $(commands_TWRP_local_path)/openaes/Android.mk \
     $(commands_TWRP_local_path)/toolbox/Android.mk \
     $(commands_TWRP_local_path)/twrpTarMain/Android.mk \
-    $(commands_TWRP_local_path)/mtp/Android.mk \
     $(commands_TWRP_local_path)/minzip/Android.mk \
     $(commands_TWRP_local_path)/dosfstools/Android.mk \
     $(commands_TWRP_local_path)/etc/Android.mk \
diff --git a/data.cpp b/data.cpp
index 0ece4e7..cf9dd24 100755
--- a/data.cpp
+++ b/data.cpp
@@ -722,6 +722,10 @@
 	printf("TW_HAS_DOWNLOAD_MODE := true\n");
 	mConst.SetValue(TW_DOWNLOAD_MODE, "1");
 #endif
+#ifdef TW_HAS_EDL_MODE
+	printf("TW_HAS_EDL_MODE := true\n");
+	mConst.SetValue(TW_EDL_MODE, "1");
+#endif
 #ifdef TW_INCLUDE_CRYPTO
 	mConst.SetValue(TW_HAS_CRYPTO, "1");
 	printf("TW_INCLUDE_CRYPTO := true\n");
diff --git a/gui/theme/common/landscape.xml b/gui/theme/common/landscape.xml
index d1bce16..6df8862 100755
--- a/gui/theme/common/landscape.xml
+++ b/gui/theme/common/landscape.xml
@@ -2705,6 +2705,25 @@
 				</actions>
 			</button>
 
+			<button style="main_button">
+				<condition var1="tw_edl_mode" var2="1"/>
+				<placement x="%center_x%" y="%row11_y%"/>
+				<text>{@rb_edl_btn=Edl}</text>
+				<actions>
+					<action function="set">tw_back=reboot</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=edl</action>
+					<action function="set">tw_reboot_param=edl</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1={@no_os1=No OS Installed! Are you}</action>
+					<action function="set">tw_text2={@no_osrb=sure you wish to reboot?}</action>
+					<action function="set">tw_action_text1={@rebooting=Rebooting...}</action>
+					<action function="set">tw_complete_text1={@rebooting=Rebooting...}</action>
+					<action function="set">tw_slider_text={@swipe_reboot=Swipe to Reboot}</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</button>
+
 			<action>
 				<touch key="home"/>
 				<action function="page">main</action>
diff --git a/gui/theme/common/languages/cz.xml b/gui/theme/common/languages/cz.xml
index 4ba5c2c..7f7fc3c 100644
--- a/gui/theme/common/languages/cz.xml
+++ b/gui/theme/common/languages/cz.xml
@@ -262,6 +262,7 @@
 		<string name="rb_recovery_btn">Recovery</string>
 		<string name="rb_bootloader_btn">Bootloader</string>
 		<string name="rb_download_btn">Stáhnout</string>
+		<string name="rb_edl_btn">Edl</string>
 		<string name="turning_off">Vypínání...</string>
 		<string name="swipe_power_off">Potáhnout pro vypnutí</string>
 		<string name="swipe_power_off_s">Vypnout</string>
diff --git a/gui/theme/common/languages/de.xml b/gui/theme/common/languages/de.xml
index 6ff8014..56f7ebd 100644
--- a/gui/theme/common/languages/de.xml
+++ b/gui/theme/common/languages/de.xml
@@ -283,6 +283,7 @@
 		<string name="rb_recovery_btn">Recovery</string>
 		<string name="rb_bootloader_btn">Bootloader</string>
 		<string name="rb_download_btn">Download</string>
+		<string name="rb_edl_btn">Edl</string>
 		<string name="turning_off">Ausschalten...</string>
 		<string name="swipe_power_off">Gerät ausschalten</string>
 		<string name="swipe_power_off_s">Ausschalten</string>
diff --git a/gui/theme/common/languages/el.xml b/gui/theme/common/languages/el.xml
index ffb960f..8ac681d 100644
--- a/gui/theme/common/languages/el.xml
+++ b/gui/theme/common/languages/el.xml
@@ -273,6 +273,7 @@
         <string name="rb_recovery_btn">Recovery</string>
         <string name="rb_bootloader_btn">Bootloader</string>
         <string name="rb_download_btn">Λήψη</string>
+		<string name="rb_edl_btn">Edl</string>
         <string name="turning_off">Απενεργοποίηση...</string>
         <string name="swipe_power_off">Σύρετε για Απενεργοποίηση</string>
         <string name="swipe_power_off_s">Απενεργοποίηση</string>
diff --git a/gui/theme/common/languages/en.xml b/gui/theme/common/languages/en.xml
index f8de662..e61f6c0 100755
--- a/gui/theme/common/languages/en.xml
+++ b/gui/theme/common/languages/en.xml
@@ -288,6 +288,7 @@
 		<string name="rb_recovery_btn">Recovery</string>
 		<string name="rb_bootloader_btn">Bootloader</string>
 		<string name="rb_download_btn">Download</string>
+		<string name="rb_edl_btn">Edl</string>
 		<string name="turning_off">Turning Off...</string>
 		<string name="swipe_power_off">Swipe to Power Off</string>
 		<string name="swipe_power_off_s">Power Off</string>
diff --git a/gui/theme/common/languages/es.xml b/gui/theme/common/languages/es.xml
index 1f20061..e096e66 100644
--- a/gui/theme/common/languages/es.xml
+++ b/gui/theme/common/languages/es.xml
@@ -263,6 +263,7 @@
 		<string name="rb_recovery_btn">Recovery</string>
 		<string name="rb_bootloader_btn">Bootloader</string>
 		<string name="rb_download_btn">Descarga</string>
+		<string name="rb_edl_btn">Edl</string>
 		<string name="turning_off">Apagando...</string>
 		<string name="swipe_power_off">Deslice para Apagar</string>
 		<string name="swipe_power_off_s">Apagar</string>
diff --git a/gui/theme/common/languages/fr.xml b/gui/theme/common/languages/fr.xml
index 61516a8..6ae3298 100644
--- a/gui/theme/common/languages/fr.xml
+++ b/gui/theme/common/languages/fr.xml
@@ -260,6 +260,7 @@
         <string name="rb_recovery_btn">Récupération</string>
         <string name="rb_bootloader_btn">Amorçage</string>
         <string name="rb_download_btn">Télécharger</string>
+		<string name="rb_edl_btn">Edl</string>
         <string name="turning_off">Arrêt en cours...</string>
         <string name="swipe_power_off">Glisser pour éteindre</string>
         <string name="swipe_power_off_s">Éteindre</string>
diff --git a/gui/theme/common/languages/hu.xml b/gui/theme/common/languages/hu.xml
index 6141cde..96557ac 100644
--- a/gui/theme/common/languages/hu.xml
+++ b/gui/theme/common/languages/hu.xml
@@ -283,6 +283,7 @@
 		<string name="rb_recovery_btn">Recovery</string>
 		<string name="rb_bootloader_btn">Bootloader</string>
 		<string name="rb_download_btn">Download mód</string>
+		<string name="rb_edl_btn">Edl</string>
 		<string name="turning_off">Kikapcsolás...</string>
 		<string name="swipe_power_off">Csúsztasson a kikapcsoláshoz</string>
 		<string name="swipe_power_off_s">Kikapcsolás</string>
diff --git a/gui/theme/common/languages/it.xml b/gui/theme/common/languages/it.xml
index 6d56e80..ec74d59 100644
--- a/gui/theme/common/languages/it.xml
+++ b/gui/theme/common/languages/it.xml
@@ -283,6 +283,7 @@
 		<string name="rb_recovery_btn">Recovery</string>
 		<string name="rb_bootloader_btn">Bootloader</string>
 		<string name="rb_download_btn">Download</string>
+		<string name="rb_edl_btn">Edl</string>
 		<string name="turning_off">Spegnimento in corso...</string>
 		<string name="swipe_power_off">Scorri per spegnere</string>
 		<string name="swipe_power_off_s">Spegni</string>
diff --git a/gui/theme/common/languages/nl.xml b/gui/theme/common/languages/nl.xml
index f5ec9bb..8dadd40 100644
--- a/gui/theme/common/languages/nl.xml
+++ b/gui/theme/common/languages/nl.xml
@@ -270,6 +270,7 @@
 		<string name="rb_recovery_btn">Recovery</string>
 		<string name="rb_bootloader_btn">Bootloader</string>
 		<string name="rb_download_btn">Download</string>
+		<string name="rb_edl_btn">Edl</string>
 		<string name="turning_off">Uitschakelen...</string>
 		<string name="swipe_power_off">Veeg om uit te schakelen</string>
 		<string name="swipe_power_off_s">Uitschakelen</string>
diff --git a/gui/theme/common/languages/pl.xml b/gui/theme/common/languages/pl.xml
index a67de06..e337455 100644
--- a/gui/theme/common/languages/pl.xml
+++ b/gui/theme/common/languages/pl.xml
@@ -270,6 +270,7 @@
 		<string name="rb_recovery_btn">Recovery</string>
 		<string name="rb_bootloader_btn">Bootloader</string>
 		<string name="rb_download_btn">Download</string>
+		<string name="rb_edl_btn">Edl</string>
 		<string name="turning_off">Wyłączanie...</string>
 		<string name="swipe_power_off">Przesuń, aby wyłączyć</string>
 		<string name="swipe_power_off_s">Wyłącz</string>
diff --git a/gui/theme/common/languages/pt_BR.xml b/gui/theme/common/languages/pt_BR.xml
index 88deb0c..f540e4c 100644
--- a/gui/theme/common/languages/pt_BR.xml
+++ b/gui/theme/common/languages/pt_BR.xml
@@ -262,6 +262,7 @@
 		<string name="rb_recovery_btn">Recuperação</string>
 		<string name="rb_bootloader_btn">Bootloader</string>
 		<string name="rb_download_btn">Baixar</string>
+		<string name="rb_edl_btn">Edl</string>
 		<string name="turning_off">Desligar...</string>
 		<string name="swipe_power_off">Deslize para desligar</string>
 		<string name="swipe_power_off_s">Desligar</string>
diff --git a/gui/theme/common/languages/pt_PT.xml b/gui/theme/common/languages/pt_PT.xml
index 6b5bc6c..40ef41f 100644
--- a/gui/theme/common/languages/pt_PT.xml
+++ b/gui/theme/common/languages/pt_PT.xml
@@ -284,6 +284,7 @@
 		<string name="rb_recovery_btn">Recovery</string>
 		<string name="rb_bootloader_btn">Bootloader</string>
 		<string name="rb_download_btn">Descarregar</string>
+		<string name="rb_edl_btn">Edl</string>
 		<string name="turning_off">A desligar...</string>
 		<string name="swipe_power_off">      Deslize para desligar</string>
 		<string name="swipe_power_off_s">Desligar</string>
diff --git a/gui/theme/common/languages/ru.xml b/gui/theme/common/languages/ru.xml
index ba4d930..95fd371 100644
--- a/gui/theme/common/languages/ru.xml
+++ b/gui/theme/common/languages/ru.xml
@@ -280,6 +280,7 @@
 		<string name="rb_recovery_btn">Рекавери</string>
 		<string name="rb_bootloader_btn">Загрузчик</string>
 		<string name="rb_download_btn">Режим загрузки</string>
+		<string name="rb_edl_btn">Edl</string>
 		<string name="turning_off">Выключение...</string>
 		<string name="swipe_power_off">Свайп для выключения</string>
 		<string name="swipe_power_off_s">Выключение</string>
diff --git a/gui/theme/common/languages/sk.xml b/gui/theme/common/languages/sk.xml
index b8e2fc5..f4e56ef 100644
--- a/gui/theme/common/languages/sk.xml
+++ b/gui/theme/common/languages/sk.xml
@@ -262,6 +262,7 @@
 		<string name="rb_recovery_btn">Recovery</string>
 		<string name="rb_bootloader_btn">Bootloader</string>
 		<string name="rb_download_btn">Stiahnuť</string>
+		<string name="rb_edl_btn">Edl</string>
 		<string name="turning_off">Vypínanie...</string>
 		<string name="swipe_power_off">Potiahnite pre vypnutie</string>
 		<string name="swipe_power_off_s">Vypnúť</string>
diff --git a/gui/theme/common/languages/sl.xml b/gui/theme/common/languages/sl.xml
index 5b0853e..4259e62 100644
--- a/gui/theme/common/languages/sl.xml
+++ b/gui/theme/common/languages/sl.xml
@@ -262,6 +262,7 @@
 		<string name="rb_recovery_btn">Obnovitev</string>
 		<string name="rb_bootloader_btn">Zagonski nalagalnik</string>
 		<string name="rb_download_btn">Prejmi</string>
+		<string name="rb_edl_btn">Edl</string>
 		<string name="turning_off">Izklapljanje …</string>
 		<string name="swipe_power_off">Povlecite za izklop</string>
 		<string name="swipe_power_off_s">Izklopi</string>
diff --git a/gui/theme/common/languages/sv.xml b/gui/theme/common/languages/sv.xml
index eadc18d..c898957 100644
--- a/gui/theme/common/languages/sv.xml
+++ b/gui/theme/common/languages/sv.xml
@@ -101,6 +101,7 @@
 		<string name="rb_system_btn">System</string>
 		<string name="rb_bootloader_btn">Bootloader</string>
 		<string name="rb_download_btn">Ladda ner</string>
+		<string name="rb_edl_btn">Edl</string>
 		<string name="turning_off">Stänger av...</string>
 		<string name="settings_hdr">Inställningar</string>
 		<string name="settings_gen_s_hdr">Allmänt</string>
diff --git a/gui/theme/common/languages/tr.xml b/gui/theme/common/languages/tr.xml
index 4c10fdf..8e924cb 100644
--- a/gui/theme/common/languages/tr.xml
+++ b/gui/theme/common/languages/tr.xml
@@ -284,6 +284,7 @@
 		<string name="rb_recovery_btn">Recovery</string>
 		<string name="rb_bootloader_btn">Bootloader</string>
 		<string name="rb_download_btn">Download</string>
+		<string name="rb_edl_btn">Edl</string>
 		<string name="turning_off">Kapatılıyor...</string>
 		<string name="swipe_power_off">Kapatmak için Kaydır</string>
 		<string name="swipe_power_off_s">Kapat</string>
diff --git a/gui/theme/common/languages/uk.xml b/gui/theme/common/languages/uk.xml
index a3ea7dd..9b267d2 100644
--- a/gui/theme/common/languages/uk.xml
+++ b/gui/theme/common/languages/uk.xml
@@ -266,6 +266,7 @@
 		<string name="rb_recovery_btn">Рекавері</string>
 		<string name="rb_bootloader_btn">Завантажувач</string>
 		<string name="rb_download_btn">Режим завантаження</string>
+		<string name="rb_edl_btn">Edl</string>
 		<string name="turning_off">Вимкнення...</string>
 		<string name="swipe_power_off">Вимкнути</string>
 		<string name="swipe_power_off_s">Вимкнення</string>
diff --git a/gui/theme/common/portrait.xml b/gui/theme/common/portrait.xml
index 1cf4d89..c2d32dd 100755
--- a/gui/theme/common/portrait.xml
+++ b/gui/theme/common/portrait.xml
@@ -2862,6 +2862,25 @@
 				</actions>
 			</button>
 
+			<button style="main_button">
+				<condition var1="tw_edl_mode" var2="1"/>
+				<placement x="%center_x%" y="%row13a_y%"/>
+				<text>{@rb_edl_btn=Edl}</text>
+				<actions>
+					<action function="set">tw_back=reboot</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=edl</action>
+					<action function="set">tw_reboot_param=edl</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1={@no_os1=No OS Installed! Are you}</action>
+					<action function="set">tw_text2={@no_osrb=sure you wish to reboot?}</action>
+					<action function="set">tw_action_text1={@rebooting=Rebooting...}</action>
+					<action function="set">tw_complete_text1={@rebooting=Rebooting...}</action>
+					<action function="set">tw_slider_text={@swipe_reboot=Swipe to Reboot}</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</button>
+
 			<text style="text_m">
 				<condition var1="tw_has_boot_slots" var2="1"/>
 				<placement x="%center_x%" y="%row17_y%" placement="5"/>
diff --git a/gui/theme/common/watch.xml b/gui/theme/common/watch.xml
index 56bdc55..0c30a34 100755
--- a/gui/theme/common/watch.xml
+++ b/gui/theme/common/watch.xml
@@ -3359,6 +3359,24 @@
 					<action function="page">rebootcheck</action>
 				</actions>
 			</button>
+			<button style="main_button">
+				<condition var1="tw_edl_mode" var2="1"/>
+				<placement x="%col1_x_right%" y="%row11_y%"/>
+				<text>{@rb_edl_btn=Edl}</text>
+				<actions>
+					<action function="set">tw_back=reboot</action>
+					<action function="set">tw_action=reboot</action>
+					<action function="set">tw_action_param=edl</action>
+					<action function="set">tw_reboot_param=edl</action>
+					<action function="set">tw_has_action2=0</action>
+					<action function="set">tw_text1={@no_os1=No OS Installed! Are you}</action>
+					<action function="set">tw_text2={@no_osrb=sure you wish to reboot?}</action>
+					<action function="set">tw_action_text1={@rebooting=Rebooting...}</action>
+					<action function="set">tw_complete_text1={@rebooting=Rebooting...}</action>
+					<action function="set">tw_slider_text={@swipe_reboot_s=   Reboot}</action>
+					<action function="page">rebootcheck</action>
+				</actions>
+			</button>
 
 			<action>
 				<touch key="home"/>
diff --git a/gui/theme/extra-languages/languages/ja.xml b/gui/theme/extra-languages/languages/ja.xml
index 37d3e7c..977b170 100644
--- a/gui/theme/extra-languages/languages/ja.xml
+++ b/gui/theme/extra-languages/languages/ja.xml
@@ -265,6 +265,7 @@
 		<string name="rb_recovery_btn">リカバリ</string>
 		<string name="rb_bootloader_btn">ブートローダー</string>
 		<string name="rb_download_btn">ダウンロード</string>
+		<string name="rb_edl_btn">Edl</string>
 		<string name="turning_off">電源を切っています...</string>
 		<string name="swipe_power_off">スワイプで電源を切る</string>
 		<string name="swipe_power_off_s">電源を切る</string>
diff --git a/gui/theme/extra-languages/languages/zh_CN.xml b/gui/theme/extra-languages/languages/zh_CN.xml
index 5c214a5..944be48 100644
--- a/gui/theme/extra-languages/languages/zh_CN.xml
+++ b/gui/theme/extra-languages/languages/zh_CN.xml
@@ -283,6 +283,7 @@
 		<string name="rb_recovery_btn">Recovery</string>
 		<string name="rb_bootloader_btn">Bootloader</string>
 		<string name="rb_download_btn">Download</string>
+		<string name="rb_edl_btn">Edl</string>
 		<string name="turning_off">正在关机…</string>
 		<string name="swipe_power_off">滑动按钮确认关机</string>
 		<string name="swipe_power_off_s">关机</string>
diff --git a/gui/theme/extra-languages/languages/zh_TW.xml b/gui/theme/extra-languages/languages/zh_TW.xml
index 75c717a..fc80862 100644
--- a/gui/theme/extra-languages/languages/zh_TW.xml
+++ b/gui/theme/extra-languages/languages/zh_TW.xml
@@ -283,6 +283,7 @@
 		<string name="rb_recovery_btn">Recovery</string>
 		<string name="rb_bootloader_btn">Bootloader</string>
 		<string name="rb_download_btn">Download</string>
+		<string name="rb_edl_btn">Edl</string>
 		<string name="turning_off">正在關機…</string>
 		<string name="swipe_power_off">滑動按鈕確認關機</string>
 		<string name="swipe_power_off_s">關機</string>
diff --git a/mtp/MtpDataPacket.h b/mtp/MtpDataPacket.h
deleted file mode 100644
index 0e7a873..0000000
--- a/mtp/MtpDataPacket.h
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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
deleted file mode 100644
index a0ff8da..0000000
--- a/mtp/MtpDatabase.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to c++
- */
-
-#ifndef _MTP_DATABASE_H
-#define _MTP_DATABASE_H
-
-#include "MtpTypes.h"
-
-class MtpDataPacket;
-class MtpProperty;
-class MtpObjectInfo;
-
-class MtpDatabase {
-public:
-    virtual ~MtpDatabase() {}
-
-    // called from SendObjectInfo to reserve a database entry for the incoming file
-    virtual MtpObjectHandle         beginSendObject(const char* path,
-                                            MtpObjectFormat format,
-                                            MtpObjectHandle parent,
-                                            MtpStorageID storage,
-                                            uint64_t size,
-                                            time_t modified) = 0;
-
-    // called to report success or failure of the SendObject file transfer
-    // success should signal a notification of the new object's creation,
-    // failure should remove the database entry created in beginSendObject
-    virtual void                    endSendObject(const char* path,
-                                            MtpObjectHandle handle,
-                                            MtpObjectFormat format,
-                                            bool succeeded) = 0;
-
-    virtual MtpObjectHandleList*    getObjectList(MtpStorageID storageID,
-                                            MtpObjectFormat format,
-                                            MtpObjectHandle parent) = 0;
-
-    virtual int                     getNumObjects(MtpStorageID storageID,
-                                            MtpObjectFormat format,
-                                            MtpObjectHandle parent) = 0;
-
-    // callee should delete[] the results from these
-    // results can be NULL
-    virtual MtpObjectFormatList*    getSupportedPlaybackFormats() = 0;
-    virtual MtpObjectFormatList*    getSupportedCaptureFormats() = 0;
-    virtual MtpObjectPropertyList*  getSupportedObjectProperties(MtpObjectFormat format) = 0;
-    virtual MtpDevicePropertyList*  getSupportedDeviceProperties() = 0;
-
-	virtual void 					createDB(MtpStorage* storage, MtpStorageID storageID) = 0;
-	virtual void 					destroyDB(MtpStorageID storageID) = 0;
-
-    virtual MtpResponseCode         getObjectPropertyValue(MtpObjectHandle handle,
-                                            MtpObjectProperty property,
-                                            MtpDataPacket& packet) = 0;
-
-    virtual MtpResponseCode         setObjectPropertyValue(MtpObjectHandle handle,
-                                            MtpObjectProperty property,
-                                            MtpDataPacket& packet) = 0;
-
-    virtual MtpResponseCode         getDevicePropertyValue(MtpDeviceProperty property,
-                                            MtpDataPacket& packet) = 0;
-
-    virtual MtpResponseCode         setDevicePropertyValue(MtpDeviceProperty property,
-                                            MtpDataPacket& packet) = 0;
-
-    virtual MtpResponseCode         resetDeviceProperty(MtpDeviceProperty property) = 0;
-
-    virtual MtpResponseCode         getObjectPropertyList(MtpObjectHandle handle,
-                                            uint32_t format, uint32_t property,
-                                            int groupCode, int depth,
-                                            MtpDataPacket& packet) = 0;
-
-    virtual MtpResponseCode         getObjectInfo(MtpObjectHandle handle,
-                                            MtpObjectInfo& info) = 0;
-
-    virtual void*                   getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) = 0;
-
-    virtual MtpResponseCode         getObjectFilePath(MtpObjectHandle handle,
-                                            MtpString& outFilePath,
-                                            int64_t& outFileLength,
-                                            MtpObjectFormat& outFormat) = 0;
-
-    virtual MtpResponseCode         deleteFile(MtpObjectHandle handle) = 0;
-
-    virtual MtpObjectHandleList*    getObjectReferences(MtpObjectHandle handle) = 0;
-
-    virtual MtpResponseCode         setObjectReferences(MtpObjectHandle handle,
-                                            MtpObjectHandleList* references) = 0;
-
-    virtual MtpProperty*            getObjectPropertyDesc(MtpObjectProperty property,
-                                            MtpObjectFormat format) = 0;
-
-    virtual MtpProperty*            getDevicePropertyDesc(MtpDeviceProperty property) = 0;
-
-    virtual void                    sessionStarted() = 0;
-
-    virtual void                    sessionEnded() = 0;
-    virtual void                    lockMutex() = 0;
-    virtual void                    unlockMutex() = 0;
-};
-
-#endif // _MTP_DATABASE_H
diff --git a/mtp/MtpDevice.h b/mtp/MtpDevice.h
deleted file mode 100644
index d90b0c0..0000000
--- a/mtp/MtpDevice.h
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * 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.h b/mtp/MtpDeviceInfo.h
deleted file mode 100644
index b316371..0000000
--- a/mtp/MtpDeviceInfo.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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/MtpObjectInfo.h b/mtp/MtpObjectInfo.h
deleted file mode 100644
index 406c3f4..0000000
--- a/mtp/MtpObjectInfo.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * 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.h b/mtp/MtpPacket.h
deleted file mode 100644
index ec763c8..0000000
--- a/mtp/MtpPacket.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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.h b/mtp/MtpProperty.h
deleted file mode 100644
index 017d875..0000000
--- a/mtp/MtpProperty.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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/MtpServer.h b/mtp/MtpServer.h
deleted file mode 100644
index 9443311..0000000
--- a/mtp/MtpServer.h
+++ /dev/null
@@ -1,155 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
- */
-
-#ifndef _MTP_SERVER_H
-#define _MTP_SERVER_H
-
-#include <utils/threads.h>
-#include <utils/Vector.h>
-#include "MtpRequestPacket.h"
-#include "MtpDatabase.h"
-#include "MtpDataPacket.h"
-#include "MtpResponsePacket.h"
-#include "MtpEventPacket.h"
-#include "mtp.h"
-#include "MtpUtils.h"
-
-
-class MtpDatabase;
-class MtpStorage;
-
-class MtpServer {
-
-private:
-    // file descriptor for MTP kernel driver
-    int                 mFD;
-	android::Mutex                   mMutex;
-    MtpDatabase*        mDatabase;
-
-    // appear as a PTP device
-    bool                mPtp;
-
-    // group to own new files and folders
-    int                 mFileGroup;
-    // permissions for new files and directories
-    int                 mFilePermission;
-    int                 mDirectoryPermission;
-
-    // current session ID
-    MtpSessionID        mSessionID;
-    // true if we have an open session and mSessionID is valid
-    bool                mSessionOpen;
-
-    MtpRequestPacket    mRequest;
-    MtpDataPacket       mData;
-    MtpResponsePacket   mResponse;
-    MtpEventPacket      mEvent;
-
-    MtpStorageList      mStorages;
-
-    // handle for new object, set by SendObjectInfo and used by SendObject
-    MtpObjectHandle     mSendObjectHandle;
-    MtpObjectFormat     mSendObjectFormat;
-    MtpString           mSendObjectFilePath;
-    size_t              mSendObjectFileSize;
-
-	pthread_mutex_t mtpMutex;
-
-    // represents an MTP object that is being edited using the android extensions
-    // for direct editing (BeginEditObject, SendPartialObject, TruncateObject and EndEditObject)
-    class ObjectEdit {
-        public:
-        MtpObjectHandle     mHandle;
-        MtpString           mPath;
-        uint64_t            mSize;
-        MtpObjectFormat     mFormat;
-        int                 mFD;
-
-        ObjectEdit(MtpObjectHandle handle, const char* path, uint64_t size,
-            MtpObjectFormat format, int fd)
-                : mHandle(handle), mPath(path), mSize(size), mFormat(format), mFD(fd) {
-            }
-
-        virtual ~ObjectEdit() {
-            close(mFD);
-        }
-    };
-    android::Vector<ObjectEdit*>  mObjectEditList;
-
-public:
-                        MtpServer(MtpDatabase* database, bool ptp,
-                                    int fileGroup, int filePerm, int directoryPerm);
-    virtual             ~MtpServer();
-
-    MtpStorage*         getStorage(MtpStorageID id);
-    inline bool         hasStorage() { return mStorages.size() > 0; }
-    bool                hasStorage(MtpStorageID id);
-    void                addStorage(MtpStorage* storage);
-    void                removeStorage(MtpStorage* storage);
-
-    void                run(int fd);
-
-    void                sendObjectAdded(MtpObjectHandle handle);
-    void                sendObjectRemoved(MtpObjectHandle handle);
-    void                sendObjectUpdated(MtpObjectHandle handle);
-
-private:
-    void                sendStoreAdded(MtpStorageID id);
-    void                sendStoreRemoved(MtpStorageID id);
-    void                sendEvent(MtpEventCode code, uint32_t param1);
-
-    void                addEditObject(MtpObjectHandle handle, MtpString& path,
-                                uint64_t size, MtpObjectFormat format, int fd);
-    ObjectEdit*         getEditObject(MtpObjectHandle handle);
-    void                removeEditObject(MtpObjectHandle handle);
-    void                commitEdit(ObjectEdit* edit);
-
-    bool                handleRequest();
-
-    MtpResponseCode     doGetDeviceInfo();
-    MtpResponseCode     doOpenSession();
-    MtpResponseCode     doCloseSession();
-    MtpResponseCode     doGetStorageIDs();
-    MtpResponseCode     doGetStorageInfo();
-    MtpResponseCode     doGetObjectPropsSupported();
-    MtpResponseCode     doGetObjectHandles();
-    MtpResponseCode     doGetNumObjects();
-    MtpResponseCode     doGetObjectReferences();
-    MtpResponseCode     doSetObjectReferences();
-    MtpResponseCode     doGetObjectPropValue();
-    MtpResponseCode     doSetObjectPropValue();
-    MtpResponseCode     doGetDevicePropValue();
-    MtpResponseCode     doSetDevicePropValue();
-    MtpResponseCode     doResetDevicePropValue();
-    MtpResponseCode     doGetObjectPropList();
-    MtpResponseCode     doGetObjectInfo();
-    MtpResponseCode     doGetObject();
-    MtpResponseCode     doGetThumb();
-    MtpResponseCode     doGetPartialObject(MtpOperationCode operation);
-    MtpResponseCode     doSendObjectInfo();
-    MtpResponseCode     doSendObject();
-    MtpResponseCode     doDeleteObject();
-    MtpResponseCode     doGetObjectPropDesc();
-    MtpResponseCode     doGetDevicePropDesc();
-    MtpResponseCode     doSendPartialObject();
-    MtpResponseCode     doTruncateObject();
-    MtpResponseCode     doBeginEditObject();
-    MtpResponseCode     doEndEditObject();
-};
-
-#endif // _MTP_SERVER_H
diff --git a/mtp/MtpStorageInfo.h b/mtp/MtpStorageInfo.h
deleted file mode 100644
index 8858328..0000000
--- a/mtp/MtpStorageInfo.h
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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.h b/mtp/MtpStringBuffer.h
deleted file mode 100644
index 9d61ecf..0000000
--- a/mtp/MtpStringBuffer.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * 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/ffs/Android.mk b/mtp/ffs/Android.mk
new file mode 100644
index 0000000..9e75e0d
--- /dev/null
+++ b/mtp/ffs/Android.mk
@@ -0,0 +1,70 @@
+LOCAL_PATH := $(call my-dir)
+
+# Build libtwrpmtp library
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := libtwrpmtp-ffs
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64 -DMTP_DEVICE -DMTP_HOST -fno-strict-aliasing -Wno-unused-variable -Wno-format -Wno-unused-parameter -Wno-unused-private-field
+LOCAL_C_INCLUDES += $(LOCAL_PATH) bionic frameworks/base/include system/core/include bionic/libc/private/ bootable/recovery/twrplibusbhost/include
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -lt 23; echo $$?),0)
+    LOCAL_C_INCLUDES += external/stlport/stlport
+    LOCAL_SHARED_LIBRARIES += libstlport
+else
+    LOCAL_SHARED_LIBRARIES += libc++
+endif
+
+LOCAL_SRC_FILES = \
+    MtpDataPacket.cpp \
+    MtpDebug.cpp \
+    MtpDevice.cpp \
+    MtpDevHandle.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 \
+    btree.cpp \
+    twrpMtp.cpp \
+    mtp_MtpDatabase.cpp \
+    node.cpp
+
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 25; echo $$?),0)
+    LOCAL_CFLAGS += -D_FFS_DEVICE
+    LOCAL_SHARED_LIBRARIES += libasyncio
+    LOCAL_SRC_FILES += \
+        MtpDescriptors.cpp \
+        MtpFfsHandle.cpp \
+        MtpFfsCompatHandle.cpp \
+        PosixAsyncIO.cpp
+endif
+
+LOCAL_SHARED_LIBRARIES += libz \
+                          libc \
+                          libusbhost \
+                          libstdc++ \
+                          libdl \
+                          libcutils \
+                          libutils \
+                          libaosprecovery \
+                          libselinux \
+                          libbase
+
+LOCAL_C_INCLUDES += bootable/recovery/twrplibusbhost/include
+
+ifneq ($(TW_MTP_DEVICE),)
+	LOCAL_CFLAGS += -DUSB_MTP_DEVICE=$(TW_MTP_DEVICE)
+endif
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -gt 25; echo $$?),0)
+    LOCAL_CFLAGS += -DHAS_USBHOST_TIMEOUT
+endif
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/mtp/ffs/AsyncIO.cpp b/mtp/ffs/AsyncIO.cpp
new file mode 100644
index 0000000..eb97a98
--- /dev/null
+++ b/mtp/ffs/AsyncIO.cpp
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <queue>
+
+#include "AsyncIO.h"
+
+void read_func(struct aiocb *aiocbp) {
+	aiocbp->ret = TEMP_FAILURE_RETRY(pread(aiocbp->aio_fildes,
+				aiocbp->aio_buf, aiocbp->aio_nbytes, aiocbp->aio_offset));
+	if (aiocbp->ret == -1) aiocbp->error = errno;
+}
+
+void write_func(struct aiocb *aiocbp) {
+	aiocbp->ret = TEMP_FAILURE_RETRY(pwrite(aiocbp->aio_fildes,
+				aiocbp->aio_buf, aiocbp->aio_nbytes, aiocbp->aio_offset));
+	if (aiocbp->ret == -1) aiocbp->error = errno;
+}
+
+void splice_read_func(struct aiocb *aiocbp) {
+	loff_t long_offset = aiocbp->aio_offset;
+	aiocbp->ret = TEMP_FAILURE_RETRY(splice(aiocbp->aio_fildes,
+				&long_offset, aiocbp->aio_sink,
+				NULL, aiocbp->aio_nbytes, 0));
+	if (aiocbp->ret == -1) aiocbp->error = errno;
+}
+
+void splice_write_func(struct aiocb *aiocbp) {
+	loff_t long_offset = aiocbp->aio_offset;
+	aiocbp->ret = TEMP_FAILURE_RETRY(splice(aiocbp->aio_fildes, NULL,
+				aiocbp->aio_sink, &long_offset,
+				aiocbp->aio_nbytes, 0));
+	if (aiocbp->ret == -1) aiocbp->error = errno;
+}
+
+std::queue<std::unique_ptr<struct aiocb>> queue;
+std::mutex queue_lock;
+std::condition_variable queue_cond;
+std::condition_variable write_cond;
+int done = 1;
+void splice_write_pool_func(int) {
+	while(1) {
+		std::unique_lock<std::mutex> lk(queue_lock);
+		queue_cond.wait(lk, []{return !queue.empty() || done;});
+		if (queue.empty() && done) {
+			return;
+		}
+		std::unique_ptr<struct aiocb> aiocbp = std::move(queue.front());
+		queue.pop();
+		lk.unlock();
+		write_cond.notify_one();
+		splice_write_func(aiocbp.get());
+		close(aiocbp->aio_fildes);
+	}
+}
+
+void write_pool_func(int) {
+	while(1) {
+		std::unique_lock<std::mutex> lk(queue_lock);
+		queue_cond.wait(lk, []{return !queue.empty() || done;});
+		if (queue.empty() && done) {
+			return;
+		}
+		std::unique_ptr<struct aiocb> aiocbp = std::move(queue.front());
+		queue.pop();
+		lk.unlock();
+		write_cond.notify_one();
+		aiocbp->ret = TEMP_FAILURE_RETRY(pwrite(aiocbp->aio_fildes,
+					aiocbp->aio_pool_buf.get(), aiocbp->aio_nbytes, aiocbp->aio_offset));
+		if (aiocbp->ret == -1) aiocbp->error = errno;
+	}
+}
+
+constexpr int NUM_THREADS = 1;
+constexpr int MAX_QUEUE_SIZE = 10;
+std::thread pool[NUM_THREADS];
+
+aiocb::~aiocb() {
+	CHECK(!thread.joinable());
+}
+
+void aio_pool_init(void(f)(int)) {
+	CHECK(done == 1);
+	done = 0;
+	for (int i = 0; i < NUM_THREADS; i++) {
+		pool[i] = std::thread(f, i);
+	}
+}
+
+void aio_pool_splice_init() {
+	aio_pool_init(splice_write_pool_func);
+}
+
+void aio_pool_write_init() {
+	aio_pool_init(write_pool_func);
+}
+
+void aio_pool_end() {
+	done = 1;
+	for (int i = 0; i < NUM_THREADS; i++) {
+		std::unique_lock<std::mutex> lk(queue_lock);
+		lk.unlock();
+		queue_cond.notify_one();
+	}
+
+	for (int i = 0; i < NUM_THREADS; i++) {
+		pool[i].join();
+	}
+}
+
+// used for both writes and splices depending on which init was used before.
+int aio_pool_write(struct aiocb *aiocbp) {
+	std::unique_lock<std::mutex> lk(queue_lock);
+	write_cond.wait(lk, []{return queue.size() < MAX_QUEUE_SIZE;});
+	queue.push(std::unique_ptr<struct aiocb>(aiocbp));
+	lk.unlock();
+	queue_cond.notify_one();
+	return 0;
+}
+
+int aio_read(struct aiocb *aiocbp) {
+	aiocbp->thread = std::thread(read_func, aiocbp);
+	return 0;
+}
+
+int aio_write(struct aiocb *aiocbp) {
+	aiocbp->thread = std::thread(write_func, aiocbp);
+	return 0;
+}
+
+int aio_splice_read(struct aiocb *aiocbp) {
+	aiocbp->thread = std::thread(splice_read_func, aiocbp);
+	return 0;
+}
+
+int aio_splice_write(struct aiocb *aiocbp) {
+	aiocbp->thread = std::thread(splice_write_func, aiocbp);
+	return 0;
+}
+
+int aio_error(const struct aiocb *aiocbp) {
+	return aiocbp->error;
+}
+
+ssize_t aio_return(struct aiocb *aiocbp) {
+	return aiocbp->ret;
+}
+
+int aio_suspend(struct aiocb *aiocbp[], int n,
+		const struct timespec *) {
+	for (int i = 0; i < n; i++) {
+		aiocbp[i]->thread.join();
+	}
+	return 0;
+}
+
+int aio_cancel(int, struct aiocb *) {
+	// Not implemented
+	return -1;
+}
+
diff --git a/mtp/ffs/AsyncIO.h b/mtp/ffs/AsyncIO.h
new file mode 100644
index 0000000..19e7617
--- /dev/null
+++ b/mtp/ffs/AsyncIO.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ASYNCIO_H
+#define _ASYNCIO_H
+
+#include <fcntl.h>
+#include <linux/aio_abi.h>
+#include <memory>
+#include <signal.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <time.h>
+#include <thread>
+#include <unistd.h>
+
+/**
+ * Provides a subset of POSIX aio operations, as well
+ * as similar operations with splice and threadpools.
+ */
+
+struct aiocb {
+	int aio_fildes;		// Assumed to be the source for splices
+	void *aio_buf;		// Unused for splices
+
+	// Used for threadpool operations only, freed automatically
+	std::unique_ptr<char[]> aio_pool_buf;
+
+	off_t aio_offset;
+	size_t aio_nbytes;
+
+	int aio_sink;		// Unused for non splice r/w
+
+	// Used internally
+	std::thread thread;
+	ssize_t ret;
+	int error;
+
+	~aiocb();
+};
+
+// Submit a request for IO to be completed
+int aio_read(struct aiocb *);
+int aio_write(struct aiocb *);
+int aio_splice_read(struct aiocb *);
+int aio_splice_write(struct aiocb *);
+
+// Suspend current thread until given IO is complete, at which point
+// its return value and any errors can be accessed
+// All submitted requests must have a corresponding suspend.
+// aiocb->aio_buf must refer to valid memory until after the suspend call
+int aio_suspend(struct aiocb *[], int, const struct timespec *);
+int aio_error(const struct aiocb *);
+ssize_t aio_return(struct aiocb *);
+
+// (Currently unimplemented)
+int aio_cancel(int, struct aiocb *);
+
+// Initialize a threadpool to perform IO. Only one pool can be
+// running at a time.
+void aio_pool_write_init();
+void aio_pool_splice_init();
+// Suspend current thread until all queued work is complete, then ends the threadpool
+void aio_pool_end();
+// Submit IO work for the threadpool to complete. Memory associated with the work is
+// freed automatically when the work is complete.
+int aio_pool_write(struct aiocb *);
+
+#endif // ASYNCIO_H
+
diff --git a/mtp/ffs/IMtpHandle.h b/mtp/ffs/IMtpHandle.h
new file mode 100644
index 0000000..caf4450
--- /dev/null
+++ b/mtp/ffs/IMtpHandle.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef _IMTP_HANDLE_H
+#define _IMTP_HANDLE_H
+
+#include <linux/usb/f_mtp.h>
+
+class IMtpHandle {
+public:
+	// Return number of bytes read/written, or -1 and errno is set
+	virtual int read(void *data, size_t len) = 0;
+	virtual int write(const void *data, size_t len) = 0;
+
+	// Return 0 if send/receive is successful, or -1 and errno is set
+	virtual int receiveFile(mtp_file_range mfr, bool zero_packet) = 0;
+	virtual int sendFile(mtp_file_range mfr) = 0;
+	virtual int sendEvent(mtp_event me) = 0;
+
+	// Return 0 if operation is successful, or -1 else
+	virtual int start(bool ptp) = 0;
+
+	virtual bool writeDescriptors(bool ptp) = 0;
+
+	virtual void close() = 0;
+
+	virtual ~IMtpHandle() {}
+};
+
+#endif // _IMTP_HANDLE_H
+
diff --git a/mtp/ffs/MtpDataPacket.cpp b/mtp/ffs/MtpDataPacket.cpp
new file mode 100644
index 0000000..08f57c5
--- /dev/null
+++ b/mtp/ffs/MtpDataPacket.cpp
@@ -0,0 +1,650 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "MtpDataPacket"
+
+#include "MtpDataPacket.h"
+
+#include <algorithm>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <usbhost/usbhost.h>
+#include "MtpStringBuffer.h"
+#include "IMtpHandle.h"
+#include "MtpDebug.h"
+
+namespace {
+// Reads the exact |count| bytes from |fd| to |buf|.
+// Returns |count| if it succeed to read the bytes. Otherwise returns -1. If it reaches EOF, the
+// function regards it as an error.
+ssize_t readExactBytes(int fd, void* buf, size_t count) {
+	if (count > SSIZE_MAX) {
+		return -1;
+	}
+	size_t read_count = 0;
+	while (read_count < count) {
+		int result = read(fd, static_cast<int8_t*>(buf) + read_count, count - read_count);
+		// Assume that EOF is error.
+		if (result <= 0) {
+			return -1;
+		}
+		read_count += result;
+	}
+	return read_count == count ? count : -1;
+}
+}  // namespace
+
+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);
+}
+
+bool MtpDataPacket::getUInt8(uint8_t& value) {
+	if (mPacketSize - mOffset < sizeof(value))
+		return false;
+	value = mBuffer[mOffset++];
+	return true;
+}
+
+bool MtpDataPacket::getUInt16(uint16_t& value) {
+	if (mPacketSize - mOffset < sizeof(value))
+		return false;
+	int offset = mOffset;
+	value = (uint16_t)mBuffer[offset] | ((uint16_t)mBuffer[offset + 1] << 8);
+	mOffset += sizeof(value);
+	return true;
+}
+
+bool MtpDataPacket::getUInt32(uint32_t& value) {
+	if (mPacketSize - mOffset < sizeof(value))
+		return false;
+	int offset = mOffset;
+	value = (uint32_t)mBuffer[offset] | ((uint32_t)mBuffer[offset + 1] << 8) |
+		   ((uint32_t)mBuffer[offset + 2] << 16)  | ((uint32_t)mBuffer[offset + 3] << 24);
+	mOffset += sizeof(value);
+	return true;
+}
+
+bool MtpDataPacket::getUInt64(uint64_t& value) {
+	if (mPacketSize - mOffset < sizeof(value))
+		return false;
+	int offset = mOffset;
+	value = (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 += sizeof(value);
+	return true;
+}
+
+bool MtpDataPacket::getUInt128(uint128_t& value) {
+	return getUInt32(value[0]) && getUInt32(value[1]) && getUInt32(value[2]) && getUInt32(value[3]);
+}
+
+bool MtpDataPacket::getString(MtpStringBuffer& string)
+{
+	return string.readFromPacket(this);
+}
+
+Int8List* MtpDataPacket::getAInt8() {
+	uint32_t count;
+	if (!getUInt32(count))
+		return NULL;
+	Int8List* result = new Int8List;
+	for (uint32_t i = 0; i < count; i++) {
+		int8_t value;
+		if (!getInt8(value)) {
+			delete result;
+			return NULL;
+		}
+		result->push_back(value);
+	}
+	return result;
+}
+
+UInt8List* MtpDataPacket::getAUInt8() {
+	uint32_t count;
+	if (!getUInt32(count))
+		return NULL;
+	UInt8List* result = new UInt8List;
+	for (uint32_t i = 0; i < count; i++) {
+		uint8_t value;
+		if (!getUInt8(value)) {
+			delete result;
+			return NULL;
+		}
+		result->push_back(value);
+	}
+	return result;
+}
+
+Int16List* MtpDataPacket::getAInt16() {
+	uint32_t count;
+	if (!getUInt32(count))
+		return NULL;
+	Int16List* result = new Int16List;
+	for (uint32_t i = 0; i < count; i++) {
+		int16_t value;
+		if (!getInt16(value)) {
+			delete result;
+			return NULL;
+		}
+		result->push_back(value);
+	}
+	return result;
+}
+
+UInt16List* MtpDataPacket::getAUInt16() {
+	uint32_t count;
+	if (!getUInt32(count))
+		return NULL;
+	UInt16List* result = new UInt16List;
+	for (uint32_t i = 0; i < count; i++) {
+		uint16_t value;
+		if (!getUInt16(value)) {
+			delete result;
+			return NULL;
+		}
+		result->push_back(value);
+	}
+	return result;
+}
+
+Int32List* MtpDataPacket::getAInt32() {
+	uint32_t count;
+	if (!getUInt32(count))
+		return NULL;
+	Int32List* result = new Int32List;
+	for (uint32_t i = 0; i < count; i++) {
+		int32_t value;
+		if (!getInt32(value)) {
+			delete result;
+			return NULL;
+		}
+		result->push_back(value);
+	}
+	return result;
+}
+
+UInt32List* MtpDataPacket::getAUInt32() {
+	uint32_t count;
+	if (!getUInt32(count))
+		return NULL;
+	UInt32List* result = new UInt32List;
+	for (uint32_t i = 0; i < count; i++) {
+		uint32_t value;
+		if (!getUInt32(value)) {
+			delete result;
+			return NULL;
+		}
+		result->push_back(value);
+	}
+	return result;
+}
+
+Int64List* MtpDataPacket::getAInt64() {
+	uint32_t count;
+	if (!getUInt32(count))
+		return NULL;
+	Int64List* result = new Int64List;
+	for (uint32_t i = 0; i < count; i++) {
+		int64_t value;
+		if (!getInt64(value)) {
+			delete result;
+			return NULL;
+		}
+		result->push_back(value);
+	}
+	return result;
+}
+
+UInt64List* MtpDataPacket::getAUInt64() {
+	uint32_t count;
+	if (!getUInt32(count))
+		return NULL;
+	UInt64List* result = new UInt64List;
+	for (uint32_t i = 0; i < count; i++) {
+		uint64_t value;
+		if (!getUInt64(value)) {
+			delete result;
+			return NULL;
+		}
+		result->push_back(value);
+	}
+	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 <= MTP_STRING_MAX_CHARACTER_NUMBER; 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(IMtpHandle *h) {
+	int ret = h->read(mBuffer, MTP_BUFFER_SIZE);
+	if (ret < MTP_CONTAINER_HEADER_SIZE)
+		return -1;
+	mPacketSize = ret;
+	mOffset = MTP_CONTAINER_HEADER_SIZE;
+	return ret;
+}
+
+int MtpDataPacket::write(IMtpHandle *h) {
+	MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+	MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
+	int ret = h->write(mBuffer, mPacketSize);
+	return (ret < 0 ? ret : 0);
+}
+
+int MtpDataPacket::writeData(IMtpHandle *h, void* data, uint32_t length) {
+	allocate(length + MTP_CONTAINER_HEADER_SIZE);
+	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 = h->write(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;
+	int 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 > static_cast<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, -1);
+	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::write(struct usb_request *request, UrbPacketDivisionMode divisionMode) {
+	if (mPacketSize < MTP_CONTAINER_HEADER_SIZE || mPacketSize > MTP_BUFFER_SIZE) {
+		MTPE("Illegal packet size.");
+		return -1;
+	}
+
+	MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+	MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
+
+	size_t processedBytes = 0;
+	while (processedBytes < mPacketSize) {
+		const size_t write_size =
+				processedBytes == 0 && divisionMode == FIRST_PACKET_ONLY_HEADER ?
+						MTP_CONTAINER_HEADER_SIZE : mPacketSize - processedBytes;
+		request->buffer = mBuffer + processedBytes;
+		request->buffer_length = write_size;
+		const int result = transfer(request);
+		if (result < 0) {
+			MTPE("Failed to write bytes to the device.");
+			return -1;
+		}
+		processedBytes += result;
+	}
+
+	return processedBytes == mPacketSize ? processedBytes : -1;
+}
+
+int MtpDataPacket::write(struct usb_request *request,
+						 UrbPacketDivisionMode divisionMode,
+						 int fd,
+						 size_t payloadSize) {
+	// Obtain the greatest multiple of minimum packet size that is not greater than
+	// MTP_BUFFER_SIZE.
+	if (request->max_packet_size <= 0) {
+		MTPE("Cannot determine bulk transfer size due to illegal max packet size %d.",
+			  request->max_packet_size);
+		return -1;
+	}
+	const size_t maxBulkTransferSize =
+			MTP_BUFFER_SIZE - (MTP_BUFFER_SIZE % request->max_packet_size);
+	const size_t containerLength = payloadSize + MTP_CONTAINER_HEADER_SIZE;
+	size_t processedBytes = 0;
+	bool readError = false;
+
+	// Bind the packet with given request.
+	request->buffer = mBuffer;
+	allocate(maxBulkTransferSize);
+
+	while (processedBytes < containerLength) {
+		size_t bulkTransferSize = 0;
+
+		// prepare header.
+		const bool headerSent = processedBytes != 0;
+		if (!headerSent) {
+			MtpPacket::putUInt32(MTP_CONTAINER_LENGTH_OFFSET, containerLength);
+			MtpPacket::putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_DATA);
+			bulkTransferSize += MTP_CONTAINER_HEADER_SIZE;
+		}
+
+		// Prepare payload.
+		if (headerSent || divisionMode == FIRST_PACKET_HAS_PAYLOAD) {
+			const size_t processedPayloadBytes =
+					headerSent ? processedBytes - MTP_CONTAINER_HEADER_SIZE : 0;
+			const size_t maxRead = payloadSize - processedPayloadBytes;
+			const size_t maxWrite = maxBulkTransferSize - bulkTransferSize;
+			const size_t bulkTransferPayloadSize = std::min(maxRead, maxWrite);
+			// prepare payload.
+			if (!readError) {
+				const ssize_t result = readExactBytes(
+						fd,
+						mBuffer + bulkTransferSize,
+						bulkTransferPayloadSize);
+				if (result < 0) {
+					MTPE("Found an error while reading data from FD. Send 0 data instead.");
+					readError = true;
+				}
+			}
+			if (readError) {
+				memset(mBuffer + bulkTransferSize, 0, bulkTransferPayloadSize);
+			}
+			bulkTransferSize += bulkTransferPayloadSize;
+		}
+
+		// Bulk transfer.
+		mPacketSize = bulkTransferSize;
+		request->buffer_length = bulkTransferSize;
+		const int result = transfer(request);
+		if (result != static_cast<ssize_t>(bulkTransferSize)) {
+			// Cannot recover writing error.
+			MTPE("Found an error while write data to MtpDevice.");
+			return -1;
+		}
+
+		// Update variables.
+		processedBytes += bulkTransferSize;
+	}
+
+	return readError ? -1 : processedBytes;
+}
+
+#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/ffs/MtpDataPacket.h b/mtp/ffs/MtpDataPacket.h
new file mode 100644
index 0000000..2240a3d
--- /dev/null
+++ b/mtp/ffs/MtpDataPacket.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_DATA_PACKET_H
+#define _MTP_DATA_PACKET_H
+
+#include "MtpPacket.h"
+#include "mtp.h"
+
+struct usb_device;
+struct usb_request;
+
+class IMtpHandle;
+class MtpStringBuffer;
+
+class MtpDataPacket : public MtpPacket {
+private:
+	// current offset for get/put methods
+	size_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; }
+
+	bool				getUInt8(uint8_t& value);
+	inline bool			getInt8(int8_t& value) { return getUInt8((uint8_t&)value); }
+	bool				getUInt16(uint16_t& value);
+	inline bool			getInt16(int16_t& value) { return getUInt16((uint16_t&)value); }
+	bool				getUInt32(uint32_t& value);
+	inline bool			getInt32(int32_t& value) { return getUInt32((uint32_t&)value); }
+	bool				getUInt64(uint64_t& value);
+	inline bool			getInt64(int64_t& value) { return getUInt64((uint64_t&)value); }
+	bool				getUInt128(uint128_t& value);
+	inline bool			getInt128(int128_t& value) { return getUInt128((uint128_t&)value); }
+	bool				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 usb handle
+	int					read(IMtpHandle *h);
+
+	// write our data to the given usb handle
+	int					write(IMtpHandle *h);
+	int					writeData(IMtpHandle *h, 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);
+
+	// Write a whole data packet with payload to the end point given by a request. |divisionMode|
+	// specifies whether to divide header and payload. See |UrbPacketDivisionMode| for meanings of
+	// each value. Return the number of bytes (including header size) sent to the device on success.
+	// Otherwise -1.
+	int					write(struct usb_request *request, UrbPacketDivisionMode divisionMode);
+	// Similar to previous write method but it reads the payload from |fd|. If |size| is larger than
+	// MTP_BUFFER_SIZE, the data will be sent by multiple bulk transfer requests.
+	int					write(struct usb_request *request, UrbPacketDivisionMode divisionMode,
+							  int fd, size_t size);
+#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/ffs/MtpDatabase.h b/mtp/ffs/MtpDatabase.h
new file mode 100755
index 0000000..18aabb8
--- /dev/null
+++ b/mtp/ffs/MtpDatabase.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_DATABASE_H
+#define _MTP_DATABASE_H
+
+#include "MtpTypes.h"
+#include "MtpStringBuffer.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,
+											MtpStringBuffer& 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;
+};
+
+#endif // _MTP_DATABASE_H
diff --git a/mtp/ffs/MtpDebug.cpp b/mtp/ffs/MtpDebug.cpp
new file mode 100644
index 0000000..c5f5d43
--- /dev/null
+++ b/mtp/ffs/MtpDebug.cpp
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "MtpDebug.h"
+
+#define MTP_DEBUG_BUFFER_SIZE 2048
+
+static int debug_enabled = 0;
+
+extern "C" void mtpdebug(const char *fmt, ...)
+{
+		if (debug_enabled) {
+				char buf[MTP_DEBUG_BUFFER_SIZE];				// We're going to limit a single request to 512 bytes
+
+				va_list ap;
+				va_start(ap, fmt);
+				vsnprintf(buf, MTP_DEBUG_BUFFER_SIZE, fmt, ap);
+				va_end(ap);
+
+				fputs(buf, stdout);
+		}
+}
+
+struct CodeEntry {
+	const char* name;
+	uint16_t code;
+};
+
+static const CodeEntry sOperationCodes[] = {
+	{ "MTP_OPERATION_GET_DEVICE_INFO",				0x1001 },
+	{ "MTP_OPERATION_OPEN_SESSION",					0x1002 },
+	{ "MTP_OPERATION_CLOSE_SESSION",				0x1003 },
+	{ "MTP_OPERATION_GET_STORAGE_IDS",				0x1004 },
+	{ "MTP_OPERATION_GET_STORAGE_INFO",				0x1005 },
+	{ "MTP_OPERATION_GET_NUM_OBJECTS",				0x1006 },
+	{ "MTP_OPERATION_GET_OBJECT_HANDLES",			0x1007 },
+	{ "MTP_OPERATION_GET_OBJECT_INFO",				0x1008 },
+	{ "MTP_OPERATION_GET_OBJECT",					0x1009 },
+	{ "MTP_OPERATION_GET_THUMB",					0x100A },
+	{ "MTP_OPERATION_DELETE_OBJECT",				0x100B },
+	{ "MTP_OPERATION_SEND_OBJECT_INFO",				0x100C },
+	{ "MTP_OPERATION_SEND_OBJECT",					0x100D },
+	{ "MTP_OPERATION_INITIATE_CAPTURE",				0x100E },
+	{ "MTP_OPERATION_FORMAT_STORE",					0x100F },
+	{ "MTP_OPERATION_RESET_DEVICE",					0x1010 },
+	{ "MTP_OPERATION_SELF_TEST",					0x1011 },
+	{ "MTP_OPERATION_SET_OBJECT_PROTECTION",		0x1012 },
+	{ "MTP_OPERATION_POWER_DOWN",					0x1013 },
+	{ "MTP_OPERATION_GET_DEVICE_PROP_DESC",			0x1014 },
+	{ "MTP_OPERATION_GET_DEVICE_PROP_VALUE",		0x1015 },
+	{ "MTP_OPERATION_SET_DEVICE_PROP_VALUE",		0x1016 },
+	{ "MTP_OPERATION_RESET_DEVICE_PROP_VALUE",		0x1017 },
+	{ "MTP_OPERATION_TERMINATE_OPEN_CAPTURE",		0x1018 },
+	{ "MTP_OPERATION_MOVE_OBJECT",					0x1019 },
+	{ "MTP_OPERATION_COPY_OBJECT",					0x101A },
+	{ "MTP_OPERATION_GET_PARTIAL_OBJECT",			0x101B },
+	{ "MTP_OPERATION_INITIATE_OPEN_CAPTURE",		0x101C },
+	{ "MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED",	0x9801 },
+	{ "MTP_OPERATION_GET_OBJECT_PROP_DESC",			0x9802 },
+	{ "MTP_OPERATION_GET_OBJECT_PROP_VALUE",		0x9803 },
+	{ "MTP_OPERATION_SET_OBJECT_PROP_VALUE",		0x9804 },
+	{ "MTP_OPERATION_GET_OBJECT_PROP_LIST",			0x9805 },
+	{ "MTP_OPERATION_SET_OBJECT_PROP_LIST",			0x9806 },
+	{ "MTP_OPERATION_GET_INTERDEPENDENT_PROP_DESC", 0x9807 },
+	{ "MTP_OPERATION_SEND_OBJECT_PROP_LIST",		0x9808 },
+	{ "MTP_OPERATION_GET_OBJECT_REFERENCES",		0x9810 },
+	{ "MTP_OPERATION_SET_OBJECT_REFERENCES",		0x9811 },
+	{ "MTP_OPERATION_SKIP",							0x9820 },
+	// android extensions
+	{ "MTP_OPERATION_GET_PARTIAL_OBJECT_64",		0x95C1 },
+	{ "MTP_OPERATION_SEND_PARTIAL_OBJECT",			0x95C2 },
+	{ "MTP_OPERATION_TRUNCATE_OBJECT",				0x95C3 },
+	{ "MTP_OPERATION_BEGIN_EDIT_OBJECT",			0x95C4 },
+	{ "MTP_OPERATION_END_EDIT_OBJECT",				0x95C5 },
+	{ 0,											0	   },
+};
+
+static const CodeEntry sFormatCodes[] = {
+	{ "MTP_FORMAT_UNDEFINED",						0x3000 },
+	{ "MTP_FORMAT_ASSOCIATION",						0x3001 },
+	{ "MTP_FORMAT_SCRIPT",							0x3002 },
+	{ "MTP_FORMAT_EXECUTABLE",						0x3003 },
+	{ "MTP_FORMAT_TEXT",							0x3004 },
+	{ "MTP_FORMAT_HTML",							0x3005 },
+	{ "MTP_FORMAT_DPOF",							0x3006 },
+	{ "MTP_FORMAT_AIFF",							0x3007 },
+	{ "MTP_FORMAT_WAV",								0x3008 },
+	{ "MTP_FORMAT_MP3",								0x3009 },
+	{ "MTP_FORMAT_AVI",								0x300A },
+	{ "MTP_FORMAT_MPEG",							0x300B },
+	{ "MTP_FORMAT_ASF",								0x300C },
+	{ "MTP_FORMAT_DEFINED",							0x3800 },
+	{ "MTP_FORMAT_EXIF_JPEG",						0x3801 },
+	{ "MTP_FORMAT_TIFF_EP",							0x3802 },
+	{ "MTP_FORMAT_FLASHPIX",						0x3803 },
+	{ "MTP_FORMAT_BMP",								0x3804 },
+	{ "MTP_FORMAT_CIFF",							0x3805 },
+	{ "MTP_FORMAT_GIF",								0x3807 },
+	{ "MTP_FORMAT_JFIF",							0x3808 },
+	{ "MTP_FORMAT_CD",								0x3809 },
+	{ "MTP_FORMAT_PICT",							0x380A },
+	{ "MTP_FORMAT_PNG",								0x380B },
+	{ "MTP_FORMAT_TIFF",							0x380D },
+	{ "MTP_FORMAT_TIFF_IT",							0x380E },
+	{ "MTP_FORMAT_JP2",								0x380F },
+	{ "MTP_FORMAT_JPX",								0x3810 },
+	{ "MTP_FORMAT_DNG",								0x3811 },
+	{ "MTP_FORMAT_HEIF",							0x3812 },
+	{ "MTP_FORMAT_UNDEFINED_FIRMWARE",				0xB802 },
+	{ "MTP_FORMAT_WINDOWS_IMAGE_FORMAT",			0xB881 },
+	{ "MTP_FORMAT_UNDEFINED_AUDIO",					0xB900 },
+	{ "MTP_FORMAT_WMA",								0xB901 },
+	{ "MTP_FORMAT_OGG",								0xB902 },
+	{ "MTP_FORMAT_AAC",								0xB903 },
+	{ "MTP_FORMAT_AUDIBLE",							0xB904 },
+	{ "MTP_FORMAT_FLAC",							0xB906 },
+	{ "MTP_FORMAT_UNDEFINED_VIDEO",					0xB980 },
+	{ "MTP_FORMAT_WMV",								0xB981 },
+	{ "MTP_FORMAT_MP4_CONTAINER",					0xB982 },
+	{ "MTP_FORMAT_MP2",								0xB983 },
+	{ "MTP_FORMAT_3GP_CONTAINER",					0xB984 },
+	{ "MTP_FORMAT_UNDEFINED_COLLECTION",			0xBA00 },
+	{ "MTP_FORMAT_ABSTRACT_MULTIMEDIA_ALBUM",		0xBA01 },
+	{ "MTP_FORMAT_ABSTRACT_IMAGE_ALBUM",			0xBA02 },
+	{ "MTP_FORMAT_ABSTRACT_AUDIO_ALBUM",			0xBA03 },
+	{ "MTP_FORMAT_ABSTRACT_VIDEO_ALBUM",			0xBA04 },
+	{ "MTP_FORMAT_ABSTRACT_AV_PLAYLIST",			0xBA05 },
+	{ "MTP_FORMAT_ABSTRACT_CONTACT_GROUP",			0xBA06 },
+	{ "MTP_FORMAT_ABSTRACT_MESSAGE_FOLDER",			0xBA07 },
+	{ "MTP_FORMAT_ABSTRACT_CHAPTERED_PRODUCTION",	0xBA08 },
+	{ "MTP_FORMAT_ABSTRACT_AUDIO_PLAYLIST",			0xBA09 },
+	{ "MTP_FORMAT_ABSTRACT_VIDEO_PLAYLIST",			0xBA0A },
+	{ "MTP_FORMAT_ABSTRACT_MEDIACAST",				0xBA0B },
+	{ "MTP_FORMAT_WPL_PLAYLIST",					0xBA10 },
+	{ "MTP_FORMAT_M3U_PLAYLIST",					0xBA11 },
+	{ "MTP_FORMAT_MPL_PLAYLIST",					0xBA12 },
+	{ "MTP_FORMAT_ASX_PLAYLIST",					0xBA13 },
+	{ "MTP_FORMAT_PLS_PLAYLIST",					0xBA14 },
+	{ "MTP_FORMAT_UNDEFINED_DOCUMENT",				0xBA80 },
+	{ "MTP_FORMAT_ABSTRACT_DOCUMENT",				0xBA81 },
+	{ "MTP_FORMAT_XML_DOCUMENT",					0xBA82 },
+	{ "MTP_FORMAT_MS_WORD_DOCUMENT",				0xBA83 },
+	{ "MTP_FORMAT_MHT_COMPILED_HTML_DOCUMENT",		0xBA84 },
+	{ "MTP_FORMAT_MS_EXCEL_SPREADSHEET",			0xBA85 },
+	{ "MTP_FORMAT_MS_POWERPOINT_PRESENTATION",		0xBA86 },
+	{ "MTP_FORMAT_UNDEFINED_MESSAGE",				0xBB00 },
+	{ "MTP_FORMAT_ABSTRACT_MESSSAGE",				0xBB01 },
+	{ "MTP_FORMAT_UNDEFINED_CONTACT",				0xBB80 },
+	{ "MTP_FORMAT_ABSTRACT_CONTACT",				0xBB81 },
+	{ "MTP_FORMAT_VCARD_2",							0xBB82 },
+	{ 0,											0	   },
+};
+
+static const CodeEntry sObjectPropCodes[] = {
+	{ "MTP_PROPERTY_STORAGE_ID",							 0xDC01 },
+	{ "MTP_PROPERTY_OBJECT_FORMAT",							 0xDC02 },
+	{ "MTP_PROPERTY_PROTECTION_STATUS",						 0xDC03 },
+	{ "MTP_PROPERTY_OBJECT_SIZE",							 0xDC04 },
+	{ "MTP_PROPERTY_ASSOCIATION_TYPE",						 0xDC05 },
+	{ "MTP_PROPERTY_ASSOCIATION_DESC",						 0xDC06 },
+	{ "MTP_PROPERTY_OBJECT_FILE_NAME",						 0xDC07 },
+	{ "MTP_PROPERTY_DATE_CREATED",							 0xDC08 },
+	{ "MTP_PROPERTY_DATE_MODIFIED",							 0xDC09 },
+	{ "MTP_PROPERTY_KEYWORDS",								 0xDC0A },
+	{ "MTP_PROPERTY_PARENT_OBJECT",							 0xDC0B },
+	{ "MTP_PROPERTY_ALLOWED_FOLDER_CONTENTS",				 0xDC0C },
+	{ "MTP_PROPERTY_HIDDEN",								 0xDC0D },
+	{ "MTP_PROPERTY_SYSTEM_OBJECT",							 0xDC0E },
+	{ "MTP_PROPERTY_PERSISTENT_UID",						 0xDC41 },
+	{ "MTP_PROPERTY_SYNC_ID",								 0xDC42 },
+	{ "MTP_PROPERTY_PROPERTY_BAG",							 0xDC43 },
+	{ "MTP_PROPERTY_NAME",									 0xDC44 },
+	{ "MTP_PROPERTY_CREATED_BY",							 0xDC45 },
+	{ "MTP_PROPERTY_ARTIST",								 0xDC46 },
+	{ "MTP_PROPERTY_DATE_AUTHORED",							 0xDC47 },
+	{ "MTP_PROPERTY_DESCRIPTION",							 0xDC48 },
+	{ "MTP_PROPERTY_URL_REFERENCE",							 0xDC49 },
+	{ "MTP_PROPERTY_LANGUAGE_LOCALE",						 0xDC4A },
+	{ "MTP_PROPERTY_COPYRIGHT_INFORMATION",					 0xDC4B },
+	{ "MTP_PROPERTY_SOURCE",								 0xDC4C },
+	{ "MTP_PROPERTY_ORIGIN_LOCATION",						 0xDC4D },
+	{ "MTP_PROPERTY_DATE_ADDED",							 0xDC4E },
+	{ "MTP_PROPERTY_NON_CONSUMABLE",						 0xDC4F },
+	{ "MTP_PROPERTY_CORRUPT_UNPLAYABLE",					 0xDC50 },
+	{ "MTP_PROPERTY_PRODUCER_SERIAL_NUMBER",				 0xDC51 },
+	{ "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_FORMAT",			 0xDC81 },
+	{ "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_SIZE",			 0xDC82 },
+	{ "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_HEIGHT",			 0xDC83 },
+	{ "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_WIDTH",			 0xDC84 },
+	{ "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DURATION",		 0xDC85 },
+	{ "MTP_PROPERTY_REPRESENTATIVE_SAMPLE_DATA",			 0xDC86 },
+	{ "MTP_PROPERTY_WIDTH",									 0xDC87 },
+	{ "MTP_PROPERTY_HEIGHT",								 0xDC88 },
+	{ "MTP_PROPERTY_DURATION",								 0xDC89 },
+	{ "MTP_PROPERTY_RATING",								 0xDC8A },
+	{ "MTP_PROPERTY_TRACK",									 0xDC8B },
+	{ "MTP_PROPERTY_GENRE",									 0xDC8C },
+	{ "MTP_PROPERTY_CREDITS",								 0xDC8D },
+	{ "MTP_PROPERTY_LYRICS",								 0xDC8E },
+	{ "MTP_PROPERTY_SUBSCRIPTION_CONTENT_ID",				 0xDC8F },
+	{ "MTP_PROPERTY_PRODUCED_BY",							 0xDC90 },
+	{ "MTP_PROPERTY_USE_COUNT",								 0xDC91 },
+	{ "MTP_PROPERTY_SKIP_COUNT",							 0xDC92 },
+	{ "MTP_PROPERTY_LAST_ACCESSED",							 0xDC93 },
+	{ "MTP_PROPERTY_PARENTAL_RATING",						 0xDC94 },
+	{ "MTP_PROPERTY_META_GENRE",							 0xDC95 },
+	{ "MTP_PROPERTY_COMPOSER",								 0xDC96 },
+	{ "MTP_PROPERTY_EFFECTIVE_RATING",						 0xDC97 },
+	{ "MTP_PROPERTY_SUBTITLE",								 0xDC98 },
+	{ "MTP_PROPERTY_ORIGINAL_RELEASE_DATE",					 0xDC99 },
+	{ "MTP_PROPERTY_ALBUM_NAME",							 0xDC9A },
+	{ "MTP_PROPERTY_ALBUM_ARTIST",							 0xDC9B },
+	{ "MTP_PROPERTY_MOOD",									 0xDC9C },
+	{ "MTP_PROPERTY_DRM_STATUS",							 0xDC9D },
+	{ "MTP_PROPERTY_SUB_DESCRIPTION",						 0xDC9E },
+	{ "MTP_PROPERTY_IS_CROPPED",							 0xDCD1 },
+	{ "MTP_PROPERTY_IS_COLOUR_CORRECTED",					 0xDCD2 },
+	{ "MTP_PROPERTY_IMAGE_BIT_DEPTH",						 0xDCD3 },
+	{ "MTP_PROPERTY_F_NUMBER",								 0xDCD4 },
+	{ "MTP_PROPERTY_EXPOSURE_TIME",							 0xDCD5 },
+	{ "MTP_PROPERTY_EXPOSURE_INDEX",						 0xDCD6 },
+	{ "MTP_PROPERTY_TOTAL_BITRATE",							 0xDE91 },
+	{ "MTP_PROPERTY_BITRATE_TYPE",							 0xDE92 },
+	{ "MTP_PROPERTY_SAMPLE_RATE",							 0xDE93 },
+	{ "MTP_PROPERTY_NUMBER_OF_CHANNELS",					 0xDE94 },
+	{ "MTP_PROPERTY_AUDIO_BIT_DEPTH",						 0xDE95 },
+	{ "MTP_PROPERTY_SCAN_TYPE",								 0xDE97 },
+	{ "MTP_PROPERTY_AUDIO_WAVE_CODEC",						 0xDE99 },
+	{ "MTP_PROPERTY_AUDIO_BITRATE",							 0xDE9A },
+	{ "MTP_PROPERTY_VIDEO_FOURCC_CODEC",					 0xDE9B },
+	{ "MTP_PROPERTY_VIDEO_BITRATE",							 0xDE9C },
+	{ "MTP_PROPERTY_FRAMES_PER_THOUSAND_SECONDS",			 0xDE9D },
+	{ "MTP_PROPERTY_KEYFRAME_DISTANCE",						 0xDE9E },
+	{ "MTP_PROPERTY_BUFFER_SIZE",							 0xDE9F },
+	{ "MTP_PROPERTY_ENCODING_QUALITY",						 0xDEA0 },
+	{ "MTP_PROPERTY_ENCODING_PROFILE",						 0xDEA1 },
+	{ "MTP_PROPERTY_DISPLAY_NAME",							 0xDCE0 },
+	{ "MTP_PROPERTY_BODY_TEXT",								 0xDCE1 },
+	{ "MTP_PROPERTY_SUBJECT",								 0xDCE2 },
+	{ "MTP_PROPERTY_PRIORITY",								 0xDCE3 },
+	{ "MTP_PROPERTY_GIVEN_NAME",							 0xDD00 },
+	{ "MTP_PROPERTY_MIDDLE_NAMES",							 0xDD01 },
+	{ "MTP_PROPERTY_FAMILY_NAME",							 0xDD02 },
+	{ "MTP_PROPERTY_PREFIX",								 0xDD03 },
+	{ "MTP_PROPERTY_SUFFIX",								 0xDD04 },
+	{ "MTP_PROPERTY_PHONETIC_GIVEN_NAME",					 0xDD05 },
+	{ "MTP_PROPERTY_PHONETIC_FAMILY_NAME",					 0xDD06 },
+	{ "MTP_PROPERTY_EMAIL_PRIMARY",							 0xDD07 },
+	{ "MTP_PROPERTY_EMAIL_PERSONAL_1",						 0xDD08 },
+	{ "MTP_PROPERTY_EMAIL_PERSONAL_2",						 0xDD09 },
+	{ "MTP_PROPERTY_EMAIL_BUSINESS_1",						 0xDD0A },
+	{ "MTP_PROPERTY_EMAIL_BUSINESS_2",						 0xDD0B },
+	{ "MTP_PROPERTY_EMAIL_OTHERS",							 0xDD0C },
+	{ "MTP_PROPERTY_PHONE_NUMBER_PRIMARY",					 0xDD0D },
+	{ "MTP_PROPERTY_PHONE_NUMBER_PERSONAL",					 0xDD0E },
+	{ "MTP_PROPERTY_PHONE_NUMBER_PERSONAL_2",				 0xDD0F },
+	{ "MTP_PROPERTY_PHONE_NUMBER_BUSINESS",					 0xDD10 },
+	{ "MTP_PROPERTY_PHONE_NUMBER_BUSINESS_2",				 0xDD11 },
+	{ "MTP_PROPERTY_PHONE_NUMBER_MOBILE",					 0xDD12 },
+	{ "MTP_PROPERTY_PHONE_NUMBER_MOBILE_2",					 0xDD13 },
+	{ "MTP_PROPERTY_FAX_NUMBER_PRIMARY",					 0xDD14 },
+	{ "MTP_PROPERTY_FAX_NUMBER_PERSONAL",					 0xDD15 },
+	{ "MTP_PROPERTY_FAX_NUMBER_BUSINESS",					 0xDD16 },
+	{ "MTP_PROPERTY_PAGER_NUMBER",							 0xDD17 },
+	{ "MTP_PROPERTY_PHONE_NUMBER_OTHERS",					 0xDD18 },
+	{ "MTP_PROPERTY_PRIMARY_WEB_ADDRESS",					 0xDD19 },
+	{ "MTP_PROPERTY_PERSONAL_WEB_ADDRESS",					 0xDD1A },
+	{ "MTP_PROPERTY_BUSINESS_WEB_ADDRESS",					 0xDD1B },
+	{ "MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS",				 0xDD1C },
+	{ "MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_2",			 0xDD1D },
+	{ "MTP_PROPERTY_INSTANT_MESSANGER_ADDRESS_3",			 0xDD1E },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_FULL",			 0xDD1F },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_1",		 0xDD20 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_LINE_2",		 0xDD21 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_CITY",			 0xDD22 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_REGION",		 0xDD23 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_POSTAL_CODE",	 0xDD24 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_PERSONAL_COUNTRY",		 0xDD25 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_FULL",			 0xDD26 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_1",		 0xDD27 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_LINE_2",		 0xDD28 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_CITY",			 0xDD29 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_REGION",		 0xDD2A },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_POSTAL_CODE",	 0xDD2B },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_BUSINESS_COUNTRY",		 0xDD2C },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_FULL",				 0xDD2D },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_1",			 0xDD2E },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_LINE_2",			 0xDD2F },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_CITY",				 0xDD30 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_REGION",			 0xDD31 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_POSTAL_CODE",		 0xDD32 },
+	{ "MTP_PROPERTY_POSTAL_ADDRESS_OTHER_COUNTRY",			 0xDD33 },
+	{ "MTP_PROPERTY_ORGANIZATION_NAME",						 0xDD34 },
+	{ "MTP_PROPERTY_PHONETIC_ORGANIZATION_NAME",			 0xDD35 },
+	{ "MTP_PROPERTY_ROLE",									 0xDD36 },
+	{ "MTP_PROPERTY_BIRTHDATE",								 0xDD37 },
+	{ "MTP_PROPERTY_MESSAGE_TO",							 0xDD40 },
+	{ "MTP_PROPERTY_MESSAGE_CC",							 0xDD41 },
+	{ "MTP_PROPERTY_MESSAGE_BCC",							 0xDD42 },
+	{ "MTP_PROPERTY_MESSAGE_READ",							 0xDD43 },
+	{ "MTP_PROPERTY_MESSAGE_RECEIVED_TIME",					 0xDD44 },
+	{ "MTP_PROPERTY_MESSAGE_SENDER",						 0xDD45 },
+	{ "MTP_PROPERTY_ACTIVITY_BEGIN_TIME",					 0xDD50 },
+	{ "MTP_PROPERTY_ACTIVITY_END_TIME",						 0xDD51 },
+	{ "MTP_PROPERTY_ACTIVITY_LOCATION",						 0xDD52 },
+	{ "MTP_PROPERTY_ACTIVITY_REQUIRED_ATTENDEES",			 0xDD54 },
+	{ "MTP_PROPERTY_ACTIVITY_OPTIONAL_ATTENDEES",			 0xDD55 },
+	{ "MTP_PROPERTY_ACTIVITY_RESOURCES",					 0xDD56 },
+	{ "MTP_PROPERTY_ACTIVITY_ACCEPTED",						 0xDD57 },
+	{ "MTP_PROPERTY_ACTIVITY_TENTATIVE",					 0xDD58 },
+	{ "MTP_PROPERTY_ACTIVITY_DECLINED",						 0xDD59 },
+	{ "MTP_PROPERTY_ACTIVITY_REMAINDER_TIME",				 0xDD5A },
+	{ "MTP_PROPERTY_ACTIVITY_OWNER",						 0xDD5B },
+	{ "MTP_PROPERTY_ACTIVITY_STATUS",						 0xDD5C },
+	{ "MTP_PROPERTY_OWNER",									 0xDD5D },
+	{ "MTP_PROPERTY_EDITOR",								 0xDD5E },
+	{ "MTP_PROPERTY_WEBMASTER",								 0xDD5F },
+	{ "MTP_PROPERTY_URL_SOURCE",							 0xDD60 },
+	{ "MTP_PROPERTY_URL_DESTINATION",						 0xDD61 },
+	{ "MTP_PROPERTY_TIME_BOOKMARK",							 0xDD62 },
+	{ "MTP_PROPERTY_OBJECT_BOOKMARK",						 0xDD63 },
+	{ "MTP_PROPERTY_BYTE_BOOKMARK",							 0xDD64 },
+	{ "MTP_PROPERTY_LAST_BUILD_DATE",						 0xDD70 },
+	{ "MTP_PROPERTY_TIME_TO_LIVE",							 0xDD71 },
+	{ "MTP_PROPERTY_MEDIA_GUID",							 0xDD72 },
+	{ 0,													 0		},
+};
+
+static const CodeEntry sDevicePropCodes[] = {
+	{ "MTP_DEVICE_PROPERTY_UNDEFINED",						 0x5000 },
+	{ "MTP_DEVICE_PROPERTY_BATTERY_LEVEL",					 0x5001 },
+	{ "MTP_DEVICE_PROPERTY_FUNCTIONAL_MODE",				 0x5002 },
+	{ "MTP_DEVICE_PROPERTY_IMAGE_SIZE",						 0x5003 },
+	{ "MTP_DEVICE_PROPERTY_COMPRESSION_SETTING",			 0x5004 },
+	{ "MTP_DEVICE_PROPERTY_WHITE_BALANCE",					 0x5005 },
+	{ "MTP_DEVICE_PROPERTY_RGB_GAIN",						 0x5006 },
+	{ "MTP_DEVICE_PROPERTY_F_NUMBER",						 0x5007 },
+	{ "MTP_DEVICE_PROPERTY_FOCAL_LENGTH",					 0x5008 },
+	{ "MTP_DEVICE_PROPERTY_FOCUS_DISTANCE",					 0x5009 },
+	{ "MTP_DEVICE_PROPERTY_FOCUS_MODE",						 0x500A },
+	{ "MTP_DEVICE_PROPERTY_EXPOSURE_METERING_MODE",			 0x500B },
+	{ "MTP_DEVICE_PROPERTY_FLASH_MODE",						 0x500C },
+	{ "MTP_DEVICE_PROPERTY_EXPOSURE_TIME",					 0x500D },
+	{ "MTP_DEVICE_PROPERTY_EXPOSURE_PROGRAM_MODE",			 0x500E },
+	{ "MTP_DEVICE_PROPERTY_EXPOSURE_INDEX",					 0x500F },
+	{ "MTP_DEVICE_PROPERTY_EXPOSURE_BIAS_COMPENSATION",		 0x5010 },
+	{ "MTP_DEVICE_PROPERTY_DATETIME",						 0x5011 },
+	{ "MTP_DEVICE_PROPERTY_CAPTURE_DELAY",					 0x5012 },
+	{ "MTP_DEVICE_PROPERTY_STILL_CAPTURE_MODE",				 0x5013 },
+	{ "MTP_DEVICE_PROPERTY_CONTRAST",						 0x5014 },
+	{ "MTP_DEVICE_PROPERTY_SHARPNESS",						 0x5015 },
+	{ "MTP_DEVICE_PROPERTY_DIGITAL_ZOOM",					 0x5016 },
+	{ "MTP_DEVICE_PROPERTY_EFFECT_MODE",					 0x5017 },
+	{ "MTP_DEVICE_PROPERTY_BURST_NUMBER",					 0x5018 },
+	{ "MTP_DEVICE_PROPERTY_BURST_INTERVAL",					 0x5019 },
+	{ "MTP_DEVICE_PROPERTY_TIMELAPSE_NUMBER",				 0x501A },
+	{ "MTP_DEVICE_PROPERTY_TIMELAPSE_INTERVAL",				 0x501B },
+	{ "MTP_DEVICE_PROPERTY_FOCUS_METERING_MODE",			 0x501C },
+	{ "MTP_DEVICE_PROPERTY_UPLOAD_URL",						 0x501D },
+	{ "MTP_DEVICE_PROPERTY_ARTIST",							 0x501E },
+	{ "MTP_DEVICE_PROPERTY_COPYRIGHT_INFO",					 0x501F },
+	{ "MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER",		 0xD401 },
+	{ "MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME",			 0xD402 },
+	{ "MTP_DEVICE_PROPERTY_VOLUME",							 0xD403 },
+	{ "MTP_DEVICE_PROPERTY_SUPPORTED_FORMATS_ORDERED",		 0xD404 },
+	{ "MTP_DEVICE_PROPERTY_DEVICE_ICON",					 0xD405 },
+	{ "MTP_DEVICE_PROPERTY_PLAYBACK_RATE",					 0xD410 },
+	{ "MTP_DEVICE_PROPERTY_PLAYBACK_OBJECT",				 0xD411 },
+	{ "MTP_DEVICE_PROPERTY_PLAYBACK_CONTAINER_INDEX",		 0xD412 },
+	{ "MTP_DEVICE_PROPERTY_SESSION_INITIATOR_VERSION_INFO",  0xD406 },
+	{ "MTP_DEVICE_PROPERTY_PERCEIVED_DEVICE_TYPE",			 0xD407 },
+	{ 0,													 0		},
+};
+
+static const char* getCodeName(uint16_t code, const CodeEntry* table) {
+	const CodeEntry* entry = table;
+	while (entry->name) {
+		if (entry->code == code)
+			return entry->name;
+		entry++;
+	}
+	return "UNKNOWN";
+}
+
+const char* MtpDebug::getOperationCodeName(MtpOperationCode code) {
+	return getCodeName(code, sOperationCodes);
+}
+
+const char* MtpDebug::getFormatCodeName(MtpObjectFormat code) {
+	if (code == 0)
+		return "NONE";
+	return getCodeName(code, sFormatCodes);
+}
+
+const char* MtpDebug::getObjectPropCodeName(MtpPropertyCode code) {
+	if (code == 0)
+		return "NONE";
+	return getCodeName(code, sObjectPropCodes);
+}
+
+const char* MtpDebug::getDevicePropCodeName(MtpPropertyCode code) {
+	if (code == 0)
+		return "NONE";
+	return getCodeName(code, sDevicePropCodes);
+}
+
+void MtpDebug::enableDebug(void) {
+	debug_enabled = 1;
+	MTPD("MTP debug logging enabled\n");
+}
+
diff --git a/mtp/ffs/MtpDebug.h b/mtp/ffs/MtpDebug.h
new file mode 100644
index 0000000..61ae4b7
--- /dev/null
+++ b/mtp/ffs/MtpDebug.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_DEBUG_H
+#define _MTP_DEBUG_H
+
+// #define LOG_NDEBUG 0
+#include "MtpTypes.h"
+
+#include <log/log.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void mtpdebug(const char *fmt, ...);
+
+#define MTPI(...) fprintf(stdout, "I:[MTP] " __VA_ARGS__)
+#define MTPD(...) mtpdebug("D:[MTP] " __VA_ARGS__)
+#define MTPE(...) fprintf(stdout, "E:[MTP] " __VA_ARGS__)
+
+#ifdef __cplusplus
+}
+#endif
+
+class MtpDebug {
+public:
+	static const char* getOperationCodeName(MtpOperationCode code);
+	static const char* getFormatCodeName(MtpObjectFormat code);
+	static const char* getObjectPropCodeName(MtpPropertyCode code);
+	static const char* getDevicePropCodeName(MtpPropertyCode code);
+	static void enableDebug();
+};
+
+#endif // _MTP_DEBUG_H
diff --git a/mtp/ffs/MtpDescriptors.cpp b/mtp/ffs/MtpDescriptors.cpp
new file mode 100644
index 0000000..54306a9
--- /dev/null
+++ b/mtp/ffs/MtpDescriptors.cpp
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <sys/types.h>
+#include <cutils/properties.h>
+
+#include "MtpDescriptors.h"
+#include "MtpDebug.h"
+
+const struct usb_interface_descriptor mtp_interface_desc = {
+	.bLength = USB_DT_INTERFACE_SIZE,
+	.bDescriptorType = USB_DT_INTERFACE,
+	.bInterfaceNumber = 0,
+	.bNumEndpoints = 3,
+	.bInterfaceClass = USB_CLASS_STILL_IMAGE,
+	.bInterfaceSubClass = 1,
+	.bInterfaceProtocol = 1,
+	.iInterface = 1,
+};
+
+const struct usb_interface_descriptor ptp_interface_desc = {
+	.bLength = USB_DT_INTERFACE_SIZE,
+	.bDescriptorType = USB_DT_INTERFACE,
+	.bInterfaceNumber = 0,
+	.bNumEndpoints = 3,
+	.bInterfaceClass = USB_CLASS_STILL_IMAGE,
+	.bInterfaceSubClass = 1,
+	.bInterfaceProtocol = 1,
+};
+
+const struct usb_endpoint_descriptor_no_audio fs_sink = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bEndpointAddress = 1 | USB_DIR_IN,
+	.bmAttributes = USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize = MAX_PACKET_SIZE_FS,
+};
+
+const struct usb_endpoint_descriptor_no_audio fs_source = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bEndpointAddress = 2 | USB_DIR_OUT,
+	.bmAttributes = USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize = MAX_PACKET_SIZE_FS,
+};
+
+const struct usb_endpoint_descriptor_no_audio intr = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bEndpointAddress = 3 | USB_DIR_IN,
+	.bmAttributes = USB_ENDPOINT_XFER_INT,
+	.wMaxPacketSize = MAX_PACKET_SIZE_EV,
+	.bInterval = 6,
+};
+
+const struct usb_endpoint_descriptor_no_audio hs_sink = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bEndpointAddress = 1 | USB_DIR_IN,
+	.bmAttributes = USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize = MAX_PACKET_SIZE_HS,
+};
+
+const struct usb_endpoint_descriptor_no_audio hs_source = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bEndpointAddress = 2 | USB_DIR_OUT,
+	.bmAttributes = USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize = MAX_PACKET_SIZE_HS,
+};
+
+const struct usb_endpoint_descriptor_no_audio ss_sink = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bEndpointAddress = 1 | USB_DIR_IN,
+	.bmAttributes = USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize = MAX_PACKET_SIZE_SS,
+};
+
+const struct usb_endpoint_descriptor_no_audio ss_source = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bEndpointAddress = 2 | USB_DIR_OUT,
+	.bmAttributes = USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize = MAX_PACKET_SIZE_SS,
+};
+
+const struct usb_ss_ep_comp_descriptor ss_sink_comp = {
+	.bLength = sizeof(ss_sink_comp),
+	.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+	.bMaxBurst = 6,
+};
+
+const struct usb_ss_ep_comp_descriptor ss_source_comp = {
+	.bLength = sizeof(ss_source_comp),
+	.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+	.bMaxBurst = 6,
+};
+
+const struct usb_ss_ep_comp_descriptor ss_intr_comp = {
+	.bLength = sizeof(ss_intr_comp),
+	.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+const struct func_desc mtp_fs_descriptors = {
+	.intf = mtp_interface_desc,
+	.sink = fs_sink,
+	.source = fs_source,
+	.intr = intr,
+};
+
+const struct func_desc mtp_hs_descriptors = {
+	.intf = mtp_interface_desc,
+	.sink = hs_sink,
+	.source = hs_source,
+	.intr = intr,
+};
+
+const struct ss_func_desc mtp_ss_descriptors = {
+	.intf = mtp_interface_desc,
+	.sink = ss_sink,
+	.sink_comp = ss_sink_comp,
+	.source = ss_source,
+	.source_comp = ss_source_comp,
+	.intr = intr,
+	.intr_comp = ss_intr_comp,
+};
+
+const struct func_desc ptp_fs_descriptors = {
+	.intf = ptp_interface_desc,
+	.sink = fs_sink,
+	.source = fs_source,
+	.intr = intr,
+};
+
+const struct func_desc ptp_hs_descriptors = {
+	.intf = ptp_interface_desc,
+	.sink = hs_sink,
+	.source = hs_source,
+	.intr = intr,
+};
+
+const struct ss_func_desc ptp_ss_descriptors = {
+	.intf = ptp_interface_desc,
+	.sink = ss_sink,
+	.sink_comp = ss_sink_comp,
+	.source = ss_source,
+	.source_comp = ss_source_comp,
+	.intr = intr,
+	.intr_comp = ss_intr_comp,
+};
+
+const struct functionfs_strings mtp_strings = {
+	.header = {
+		.magic = htole32(FUNCTIONFS_STRINGS_MAGIC),
+		.length = htole32(sizeof(mtp_strings)),
+		.str_count = htole32(1),
+		.lang_count = htole32(1),
+	},
+	.lang0 = {
+		.code = htole16(0x0409),
+		.str1 = STR_INTERFACE,
+	},
+};
+
+const struct usb_os_desc_header mtp_os_desc_header = {
+	.interface = htole32(1),
+	.dwLength = htole32(sizeof(usb_os_desc_header) + sizeof(usb_ext_compat_desc)),
+	.bcdVersion = htole16(1),
+	.wIndex = htole16(4),
+	.bCount = htole16(1),
+	.Reserved = htole16(0),
+};
+
+const struct usb_ext_compat_desc mtp_os_desc_compat = {
+	.bFirstInterfaceNumber = 0,
+	.Reserved1 = htole32(1),
+	.CompatibleID = { 'M', 'T', 'P' },
+	.SubCompatibleID = {0},
+	.Reserved2 = {0},
+};
+
+const struct usb_ext_compat_desc ptp_os_desc_compat = {
+	.bFirstInterfaceNumber = 0,
+	.Reserved1 = htole32(1),
+	.CompatibleID = { 'P', 'T', 'P' },
+	.SubCompatibleID = {0},
+	.Reserved2 = {0},
+};
+
+const struct desc_v2 mtp_desc_v2 = {
+	.header = {
+		.magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
+		.length = htole32(sizeof(struct desc_v2)),
+		.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
+				 FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC,
+	},
+	.fs_count = 4,
+	.hs_count = 4,
+	.ss_count = 7,
+	.os_count = 1,
+	.fs_descs = mtp_fs_descriptors,
+	.hs_descs = mtp_hs_descriptors,
+	.ss_descs = mtp_ss_descriptors,
+	.os_header = mtp_os_desc_header,
+	.os_desc = mtp_os_desc_compat,
+};
+
+const struct desc_v2 ptp_desc_v2 = {
+	.header = {
+		.magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
+		.length = htole32(sizeof(struct desc_v2)),
+		.flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC |
+				 FUNCTIONFS_HAS_SS_DESC | FUNCTIONFS_HAS_MS_OS_DESC,
+	},
+	.fs_count = 4,
+	.hs_count = 4,
+	.ss_count = 7,
+	.os_count = 1,
+	.fs_descs = ptp_fs_descriptors,
+	.hs_descs = ptp_hs_descriptors,
+	.ss_descs = ptp_ss_descriptors,
+	.os_header = mtp_os_desc_header,
+	.os_desc = ptp_os_desc_compat,
+};
+
+const struct desc_v1 mtp_desc_v1 = {
+	.header = {
+		.magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC),
+		.length = htole32(sizeof(struct desc_v1)),
+		.fs_count = 4,
+		.hs_count = 4,
+	},
+	.fs_descs = mtp_fs_descriptors,
+	.hs_descs = mtp_hs_descriptors,
+};
+
+const struct desc_v1 ptp_desc_v1 = {
+	.header = {
+		.magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC),
+		.length = htole32(sizeof(struct desc_v1)),
+		.fs_count = 4,
+		.hs_count = 4,
+	},
+	.fs_descs = ptp_fs_descriptors,
+	.hs_descs = ptp_hs_descriptors,
+};
+
+bool writeDescriptors(int fd, bool ptp) {
+	ssize_t ret = TEMP_FAILURE_RETRY(write(fd,
+				&(ptp ? ptp_desc_v2 : mtp_desc_v2), sizeof(desc_v2)));
+	if (ret < 0) {
+		MTPE("Switching to V1 descriptor format\n");
+		ret = TEMP_FAILURE_RETRY(write(fd,
+					&(ptp ? ptp_desc_v1 : mtp_desc_v1), sizeof(desc_v1)));
+		if (ret < 0) {
+			MTPE("Writing descriptors failed\n");
+			return false;
+		}
+	}
+	ret = TEMP_FAILURE_RETRY(write(fd, &mtp_strings, sizeof(mtp_strings)));
+	if (ret < 0) {
+		MTPE("Writing strings failed\n");
+		return false;
+	}
+	property_set("sys.usb.ffs.mtp.ready", "1");
+	return true;
+}
diff --git a/mtp/ffs/MtpDescriptors.h b/mtp/ffs/MtpDescriptors.h
new file mode 100644
index 0000000..f362d80
--- /dev/null
+++ b/mtp/ffs/MtpDescriptors.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MTP_DESCRIPTORS_H
+#define MTP_DESCRIPTORS_H
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+#include <sys/endian.h>
+
+constexpr char FFS_MTP_EP0[] = "/dev/usb-ffs/mtp/ep0";
+constexpr char FFS_MTP_EP_IN[] = "/dev/usb-ffs/mtp/ep1";
+constexpr char FFS_MTP_EP_OUT[] = "/dev/usb-ffs/mtp/ep2";
+constexpr char FFS_MTP_EP_INTR[] = "/dev/usb-ffs/mtp/ep3";
+
+constexpr char FFS_PTP_EP0[] = "/dev/usb-ffs/ptp/ep0";
+constexpr char FFS_PTP_EP_IN[] = "/dev/usb-ffs/ptp/ep1";
+constexpr char FFS_PTP_EP_OUT[] = "/dev/usb-ffs/ptp/ep2";
+constexpr char FFS_PTP_EP_INTR[] = "/dev/usb-ffs/ptp/ep3";
+
+constexpr int MAX_PACKET_SIZE_FS = 64;
+constexpr int MAX_PACKET_SIZE_HS = 512;
+constexpr int MAX_PACKET_SIZE_SS = 1024;
+constexpr int MAX_PACKET_SIZE_EV = 28;
+
+struct func_desc {
+	struct usb_interface_descriptor intf;
+	struct usb_endpoint_descriptor_no_audio sink;
+	struct usb_endpoint_descriptor_no_audio source;
+	struct usb_endpoint_descriptor_no_audio intr;
+} __attribute__((packed));
+
+struct ss_func_desc {
+	struct usb_interface_descriptor intf;
+	struct usb_endpoint_descriptor_no_audio sink;
+	struct usb_ss_ep_comp_descriptor sink_comp;
+	struct usb_endpoint_descriptor_no_audio source;
+	struct usb_ss_ep_comp_descriptor source_comp;
+	struct usb_endpoint_descriptor_no_audio intr;
+	struct usb_ss_ep_comp_descriptor intr_comp;
+} __attribute__((packed));
+
+struct desc_v1 {
+	struct usb_functionfs_descs_head_v1 {
+		__le32 magic;
+		__le32 length;
+		__le32 fs_count;
+		__le32 hs_count;
+	} __attribute__((packed)) header;
+	struct func_desc fs_descs, hs_descs;
+} __attribute__((packed));
+
+struct desc_v2 {
+	struct usb_functionfs_descs_head_v2 header;
+	// The rest of the structure depends on the flags in the header.
+	__le32 fs_count;
+	__le32 hs_count;
+	__le32 ss_count;
+	__le32 os_count;
+	struct func_desc fs_descs, hs_descs;
+	struct ss_func_desc ss_descs;
+	struct usb_os_desc_header os_header;
+	struct usb_ext_compat_desc os_desc;
+} __attribute__((packed));
+
+// OS descriptor contents should not be changed. See b/64790536.
+static_assert(sizeof(struct desc_v2) == sizeof(usb_functionfs_descs_head_v2) +
+		16 + 2 * sizeof(struct func_desc) + sizeof(struct ss_func_desc) +
+		sizeof(usb_os_desc_header) + sizeof(usb_ext_compat_desc),
+		"Size of mtp descriptor is incorrect!");
+
+#define STR_INTERFACE "MTP"
+struct functionfs_lang {
+	__le16 code;
+	char str1[sizeof(STR_INTERFACE)];
+} __attribute__((packed));
+
+struct functionfs_strings {
+	struct usb_functionfs_strings_head header;
+	struct functionfs_lang lang0;
+} __attribute__((packed));
+
+extern const struct desc_v2 mtp_desc_v2;
+extern const struct desc_v2 ptp_desc_v2;
+extern const struct desc_v1 mtp_desc_v1;
+extern const struct desc_v1 ptp_desc_v1;
+extern const struct functionfs_strings mtp_strings;
+
+bool writeDescriptors(int fd, bool ptp);
+
+#endif // MTP_DESCRIPTORS_H
diff --git a/mtp/ffs/MtpDevHandle.cpp b/mtp/ffs/MtpDevHandle.cpp
new file mode 100644
index 0000000..d6a8b82
--- /dev/null
+++ b/mtp/ffs/MtpDevHandle.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or		   implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <cutils/properties.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/usb/ch9.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <unistd.h>
+
+#include "MtpDevHandle.h"
+
+constexpr char mtp_dev_path[] = "/dev/mtp_usb";
+
+MtpDevHandle::MtpDevHandle()
+	: mFd(-1) {};
+
+MtpDevHandle::~MtpDevHandle() {}
+
+int MtpDevHandle::read(void *data, size_t len) {
+	return ::read(mFd, data, len);
+}
+
+int MtpDevHandle::write(const void *data, size_t len) {
+	return ::write(mFd, data, len);
+}
+
+int MtpDevHandle::receiveFile(mtp_file_range mfr, bool) {
+	return ioctl(mFd, MTP_RECEIVE_FILE, reinterpret_cast<unsigned long>(&mfr));
+}
+
+int MtpDevHandle::sendFile(mtp_file_range mfr) {
+	return ioctl(mFd, MTP_SEND_FILE_WITH_HEADER, reinterpret_cast<unsigned long>(&mfr));
+}
+
+int MtpDevHandle::sendEvent(mtp_event me) {
+	return ioctl(mFd, MTP_SEND_EVENT, reinterpret_cast<unsigned long>(&me));
+}
+
+int MtpDevHandle::start(bool /* ptp */) {
+	mFd.reset(TEMP_FAILURE_RETRY(open(mtp_dev_path, O_RDWR)));
+	if (mFd == -1) return -1;
+	return 0;
+}
+
+void MtpDevHandle::close() {
+	mFd.reset();
+}
+
+bool MtpDevHandle::writeDescriptors(bool usePtp) { return usePtp; }
diff --git a/mtp/ffs/MtpDevHandle.h b/mtp/ffs/MtpDevHandle.h
new file mode 100644
index 0000000..4b06928
--- /dev/null
+++ b/mtp/ffs/MtpDevHandle.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_DEV_HANDLE_H
+#define _MTP_DEV_HANDLE_H
+
+#include <android-base/unique_fd.h>
+#include "IMtpHandle.h"
+
+class MtpDevHandle : public IMtpHandle {
+private:
+	android::base::unique_fd mFd;
+
+public:
+	MtpDevHandle();
+	~MtpDevHandle();
+	int read(void *data, size_t len);
+	int write(const void *data, size_t len);
+
+	int receiveFile(mtp_file_range mfr, bool);
+	int sendFile(mtp_file_range mfr);
+	int sendEvent(mtp_event me);
+
+	int start(bool ptp);
+	void close();
+	bool writeDescriptors(bool usePtp);
+};
+
+#endif // _MTP_FFS_HANDLE_H
diff --git a/mtp/ffs/MtpDevice.cpp b/mtp/ffs/MtpDevice.cpp
new file mode 100644
index 0000000..8a50ef1
--- /dev/null
+++ b/mtp/ffs/MtpDevice.cpp
@@ -0,0 +1,946 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "MtpDevice"
+
+#include "MtpDebug.h"
+#include "MtpDevice.h"
+#include "MtpDeviceInfo.h"
+#include "MtpEventPacket.h"
+#include "MtpObjectInfo.h"
+#include "MtpProperty.h"
+#include "MtpStorageInfo.h"
+#include "MtpStringBuffer.h"
+#include "MtpUtils.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>
+
+namespace {
+
+static constexpr int USB_CONTROL_TRANSFER_TIMEOUT_MS = 200;
+
+}  // namespace
+
+#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
+
+namespace {
+
+bool writeToFd(void* data, uint32_t /* unused_offset */, uint32_t length, void* clientData) {
+	const int fd = *static_cast<int*>(clientData);
+	const ssize_t result = write(fd, data, length);
+	if (result < 0) {
+		return false;
+	}
+	return static_cast<uint32_t>(result) == length;
+}
+
+}  // namespace
+
+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,
+						USB_CONTROL_TRANSFER_TIMEOUT_MS);
+				char* productName = usb_device_get_product_name(device,
+						USB_CONTROL_TRANSFER_TIMEOUT_MS);
+				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,
+						USB_CONTROL_TRANSFER_TIMEOUT_MS);
+				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,
+						USB_CONTROL_TRANSFER_TIMEOUT_MS);
+				char* productName = usb_device_get_product_name(device,
+						USB_CONTROL_TRANSFER_TIMEOUT_MS);
+				MTPD("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);
+				printf("usb_device_control_transfer returned %d errno: %d\n", ret, errno);
+				if (ret > 0) {
+					printf("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);
+					printf("OS descriptor got %d\n", ret);
+				} else {
+					printf("no MTP string\n");
+				}
+			}
+#else
+			else {
+				continue;
+			}
+#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;
+			//USB3 add USB_DT_SS_ENDPOINT_COMP as companion descriptor;
+			struct usb_ss_ep_comp_descriptor *ep_ss_ep_comp_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_SS_ENDPOINT_COMP) {
+					MTPD("Descriptor type is USB_DT_SS_ENDPOINT_COMP for USB3 \n");
+					ep_ss_ep_comp_desc = (usb_ss_ep_comp_descriptor*)ep;
+					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;
+			}
+
+			int ret = usb_device_claim_interface(device, interface->bInterfaceNumber);
+			if (ret && errno == EBUSY) {
+				// disconnect kernel driver and try again
+				usb_device_connect_kernel_driver(device, interface->bInterfaceNumber, false);
+				ret = usb_device_claim_interface(device, interface->bInterfaceNumber);
+			}
+			if (ret) {
+				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),
+		mProcessingEvent(false),
+		mCurrentEventHandle(0),
+		mLastSendObjectInfoTransactionID(0),
+		mLastSendObjectInfoObjectHandle(0),
+		mPacketDivisionMode(FIRST_PACKET_HAS_PAYLOAD)
+{
+	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_back(property);
+			}
+		}
+	}
+}
+
+void MtpDevice::close() {
+	if (mDevice) {
+		usb_device_release_interface(mDevice, mInterface);
+		usb_device_close(mDevice);
+		mDevice = NULL;
+	}
+}
+
+void MtpDevice::print() {
+	if (!mDeviceInfo)
+		return;
+
+	mDeviceInfo->print();
+
+	if (mDeviceInfo->mDeviceProperties) {
+		MTPD("***** 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) {
+			MTPD("***** OBJECT PROPERTIES *****\n");
+		int count = mDeviceInfo->mPlaybackFormats->size();
+		for (int i = 0; i < count; i++) {
+			MtpObjectFormat format = (*mDeviceInfo->mPlaybackFormats)[i];
+			MTPD("*** 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 {
+						MTPE("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() {
+	std::lock_guard<std::mutex> lg(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() {
+	std::lock_guard<std::mutex> lg(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;
+		if (info->read(mData))
+			return info;
+		else
+			delete info;
+	}
+	return NULL;
+}
+
+MtpStorageIDList* MtpDevice::getStorageIDs() {
+	std::lock_guard<std::mutex> lg(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) {
+	std::lock_guard<std::mutex> lg(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);
+		if (info->read(mData))
+			return info;
+		else
+			delete info;
+	}
+	return NULL;
+}
+
+MtpObjectHandleList* MtpDevice::getObjectHandles(MtpStorageID storageID,
+			MtpObjectFormat format, MtpObjectHandle parent) {
+	std::lock_guard<std::mutex> lg(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) {
+	std::lock_guard<std::mutex> lg(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);
+		if (info->read(mData))
+			return info;
+		else
+			delete info;
+	}
+	return NULL;
+}
+
+void* MtpDevice::getThumbnail(MtpObjectHandle handle, int& outLength) {
+	std::lock_guard<std::mutex> lg(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) {
+	std::lock_guard<std::mutex> lg(mMutex);
+
+	mRequest.reset();
+	MtpObjectHandle parent = info->mParent;
+	if (parent == 0)
+		parent = MTP_PARENT_ROOT;
+
+	mRequest.setParameter(1, info->mStorageID);
+	mRequest.setParameter(2, parent);
+
+	mData.reset();
+	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) {
+			mLastSendObjectInfoTransactionID = mRequest.getTransactionID();
+			mLastSendObjectInfoObjectHandle = mResponse.getParameter(3);
+			info->mStorageID = mResponse.getParameter(1);
+			info->mParent = mResponse.getParameter(2);
+			info->mHandle = mResponse.getParameter(3);
+			return info->mHandle;
+		}
+	}
+	return (MtpObjectHandle)-1;
+}
+
+bool MtpDevice::sendObject(MtpObjectHandle handle, int size, int srcFD) {
+	std::lock_guard<std::mutex> lg(mMutex);
+
+	if (mLastSendObjectInfoTransactionID + 1 != mTransactionID ||
+			mLastSendObjectInfoObjectHandle != handle) {
+		MTPE("A sendObject request must follow the sendObjectInfo request.");
+		return false;
+	}
+
+	mRequest.reset();
+	if (sendRequest(MTP_OPERATION_SEND_OBJECT)) {
+		mData.setOperationCode(mRequest.getOperationCode());
+		mData.setTransactionID(mRequest.getTransactionID());
+		const int writeResult = mData.write(mRequestOut, mPacketDivisionMode, srcFD, size);
+		const MtpResponseCode ret = readResponse();
+		return ret == MTP_RESPONSE_OK && writeResult > 0;
+	}
+	return false;
+}
+
+bool MtpDevice::deleteObject(MtpObjectHandle handle) {
+	std::lock_guard<std::mutex> lg(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) {
+	std::lock_guard<std::mutex> lg(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) {
+	std::lock_guard<std::mutex> lg(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;
+		if (property->read(mData))
+			return property;
+		else
+			delete property;
+	}
+	return NULL;
+}
+
+MtpProperty* MtpDevice::getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format) {
+	std::lock_guard<std::mutex> lg(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;
+	const MtpResponseCode ret = readResponse();
+	if (ret == MTP_RESPONSE_OK) {
+		MtpProperty* property = new MtpProperty;
+		if (property->read(mData))
+			return property;
+		else
+			delete property;
+	}
+	return NULL;
+}
+
+bool MtpDevice::getObjectPropValue(MtpObjectHandle handle, MtpProperty* property) {
+	if (property == nullptr)
+		return false;
+
+	std::lock_guard<std::mutex> lg(mMutex);
+
+	mRequest.reset();
+	mRequest.setParameter(1, handle);
+	mRequest.setParameter(2, property->getPropertyCode());
+	if (!sendRequest(MTP_OPERATION_GET_OBJECT_PROP_VALUE))
+		return false;
+	if (!readData())
+		return false;
+	if (readResponse() != MTP_RESPONSE_OK)
+		return false;
+	property->setCurrentValue(mData);
+	return true;
+}
+
+bool MtpDevice::readObject(MtpObjectHandle handle,
+						   ReadObjectCallback callback,
+						   uint32_t expectedLength,
+						   void* clientData) {
+	return readObjectInternal(handle, callback, &expectedLength, clientData);
+}
+
+// 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) {
+	MTPD("readObject: %s", destPath);
+	int fd = ::open(destPath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+	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);
+
+	bool result = readObject(handle, fd);
+	::close(fd);
+	return result;
+}
+
+bool MtpDevice::readObject(MtpObjectHandle handle, int fd) {
+	MTPD("readObject: %d", fd);
+	return readObjectInternal(handle, writeToFd, NULL /* expected size */, &fd);
+}
+
+bool MtpDevice::readObjectInternal(MtpObjectHandle handle,
+								   ReadObjectCallback callback,
+								   const uint32_t* expectedLength,
+								   void* clientData) {
+	std::lock_guard<std::mutex> lg(mMutex);
+
+	mRequest.reset();
+	mRequest.setParameter(1, handle);
+	if (!sendRequest(MTP_OPERATION_GET_OBJECT)) {
+		MTPE("Failed to send a read request.");
+		return false;
+	}
+
+	return readData(callback, expectedLength, nullptr, clientData);
+}
+
+bool MtpDevice::readData(ReadObjectCallback callback,
+							const uint32_t* expectedLength,
+							uint32_t* writtenSize,
+							void* clientData) {
+	if (!mData.readDataHeader(mRequestIn1)) {
+		MTPE("Failed to read header.");
+		return false;
+	}
+
+	// If object size 0 byte, the remote device may reply a response packet without sending any data
+	// packets.
+	if (mData.getContainerType() == MTP_CONTAINER_TYPE_RESPONSE) {
+		mResponse.copyFrom(mData);
+		return mResponse.getResponseCode() == MTP_RESPONSE_OK;
+	}
+
+	const uint32_t fullLength = mData.getContainerLength();
+	if (fullLength < MTP_CONTAINER_HEADER_SIZE) {
+		MTPE("fullLength is too short: %d", fullLength);
+		return false;
+	}
+	const uint32_t length = fullLength - MTP_CONTAINER_HEADER_SIZE;
+	if (expectedLength && length != *expectedLength) {
+		MTPE("readObject error length: %d", fullLength);
+		return false;
+	}
+
+	uint32_t offset = 0;
+	bool writingError = false;
+
+	{
+		int initialDataLength = 0;
+		void* const initialData = mData.getData(&initialDataLength);
+		if (fullLength > MTP_CONTAINER_HEADER_SIZE && initialDataLength == 0) {
+			// According to the MTP spec, the responder (MTP device) can choose two ways of sending
+			// data. a) The first packet contains the head and as much of the payload as possible
+			// b) The first packet contains only the header. The initiator (MTP host) needs
+			// to remember which way the responder used, and send upcoming data in the same way.
+			MTPD("Found short packet that contains only a header.");
+			mPacketDivisionMode = FIRST_PACKET_ONLY_HEADER;
+		}
+		if (initialData) {
+			if (initialDataLength > 0) {
+				if (!callback(initialData, offset, initialDataLength, clientData)) {
+					MTPE("Failed to write initial data.");
+					writingError = true;
+				}
+				offset += initialDataLength;
+			}
+			free(initialData);
+		}
+	}
+
+	// USB reads greater than 16K don't work.
+	char buffer1[MTP_BUFFER_SIZE], buffer2[MTP_BUFFER_SIZE];
+	mRequestIn1->buffer = buffer1;
+	mRequestIn2->buffer = buffer2;
+	struct usb_request* req = NULL;
+
+	while (offset < length) {
+		// Wait for previous read to complete.
+		void* writeBuffer = NULL;
+		int writeLength = 0;
+		if (req) {
+			const int read = mData.readDataWait(mDevice);
+			if (read < 0) {
+				MTPE("readDataWait failed.");
+				return false;
+			}
+			writeBuffer = req->buffer;
+			writeLength = read;
+		}
+
+		// Request to read next chunk.
+		const uint32_t nextOffset = offset + writeLength;
+		if (nextOffset < length) {
+			// Queue up a read request.
+			const size_t remaining = length - nextOffset;
+			req = (req == mRequestIn1 ? mRequestIn2 : mRequestIn1);
+			req->buffer_length = remaining > MTP_BUFFER_SIZE ?
+					static_cast<size_t>(MTP_BUFFER_SIZE) : remaining;
+			if (mData.readDataAsync(req) != 0) {
+				MTPE("readDataAsync failed");
+				return false;
+			}
+		}
+
+		// Write previous buffer.
+		if (writeBuffer && !writingError) {
+			if (!callback(writeBuffer, offset, writeLength, clientData)) {
+				MTPE("write failed");
+				writingError = true;
+			}
+		}
+		offset = nextOffset;
+	}
+
+	if (writtenSize) {
+		*writtenSize = length;
+	}
+
+	return readResponse() == MTP_RESPONSE_OK;
+}
+
+bool MtpDevice::readPartialObject(MtpObjectHandle handle,
+								  uint32_t offset,
+								  uint32_t size,
+								  uint32_t *writtenSize,
+								  ReadObjectCallback callback,
+								  void* clientData) {
+	std::lock_guard<std::mutex> lg(mMutex);
+
+	mRequest.reset();
+	mRequest.setParameter(1, handle);
+	mRequest.setParameter(2, offset);
+	mRequest.setParameter(3, size);
+	if (!sendRequest(MTP_OPERATION_GET_PARTIAL_OBJECT)) {
+		MTPE("Failed to send a read request.");
+		return false;
+	}
+	// The expected size is null because it requires the exact number of bytes to read though
+	// MTP_OPERATION_GET_PARTIAL_OBJECT allows devices to return shorter length of bytes than
+	// requested. Destination's buffer length should be checked in |callback|.
+	return readData(callback, nullptr /* expected size */, writtenSize, clientData);
+}
+
+bool MtpDevice::readPartialObject64(MtpObjectHandle handle,
+									uint64_t offset,
+									uint32_t size,
+									uint32_t *writtenSize,
+									ReadObjectCallback callback,
+									void* clientData) {
+	std::lock_guard<std::mutex> lg(mMutex);
+
+	mRequest.reset();
+	mRequest.setParameter(1, handle);
+	mRequest.setParameter(2, 0xffffffff & offset);
+	mRequest.setParameter(3, 0xffffffff & (offset >> 32));
+	mRequest.setParameter(4, size);
+	if (!sendRequest(MTP_OPERATION_GET_PARTIAL_OBJECT_64)) {
+		MTPE("Failed to send a read request.");
+		return false;
+	}
+	// The expected size is null because it requires the exact number of bytes to read though
+	// MTP_OPERATION_GET_PARTIAL_OBJECT_64 allows devices to return shorter length of bytes than
+	// requested. Destination's buffer length should be checked in |callback|.
+	return readData(callback, nullptr /* expected size */, writtenSize, clientData);
+}
+
+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, mPacketDivisionMode);
+	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 {
+		MTPD("readResponse failed\n");
+		return false;
+	}
+}
+
+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 {
+		MTPD("readResponse failed\n");
+		return -1;
+	}
+}
+
+int MtpDevice::submitEventRequest() {
+	if (!mEventMutex.try_lock()) {
+		// An event is being reaped on another thread.
+		return -1;
+	}
+	if (mProcessingEvent) {
+		// An event request was submitted, but no reapEventRequest called so far.
+		return -1;
+	}
+	std::lock_guard<std::mutex> lg(mEventMutexForInterrupt);
+	mEventPacket.sendRequest(mRequestIntr);
+	const int currentHandle = ++mCurrentEventHandle;
+	mProcessingEvent = true;
+	mEventMutex.unlock();
+	return currentHandle;
+}
+
+int MtpDevice::reapEventRequest(int handle, uint32_t (*parameters)[3]) {
+	std::lock_guard<std::mutex> lg(mEventMutex);
+	if (!mProcessingEvent || mCurrentEventHandle != handle || !parameters) {
+		return -1;
+	}
+	mProcessingEvent = false;
+	const int readSize = mEventPacket.readResponse(mRequestIntr->dev);
+	const int result = mEventPacket.getEventCode();
+	// MTP event has three parameters.
+	(*parameters)[0] = mEventPacket.getParameter(1);
+	(*parameters)[1] = mEventPacket.getParameter(2);
+	(*parameters)[2] = mEventPacket.getParameter(3);
+	return readSize != 0 ? result : 0;
+}
+
+void MtpDevice::discardEventRequest(int handle) {
+	std::lock_guard<std::mutex> lg(mEventMutexForInterrupt);
+	if (mCurrentEventHandle != handle) {
+		return;
+	}
+	usb_request_cancel(mRequestIntr);
+}
diff --git a/mtp/ffs/MtpDevice.h b/mtp/ffs/MtpDevice.h
new file mode 100644
index 0000000..da00a81
--- /dev/null
+++ b/mtp/ffs/MtpDevice.h
@@ -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.
+ */
+
+#ifndef _MTP_DEVICE_H
+#define _MTP_DEVICE_H
+
+#include "MtpEventPacket.h"
+#include "MtpDataPacket.h"
+#include "MtpRequestPacket.h"
+#include "MtpResponsePacket.h"
+#include "MtpTypes.h"
+
+#include <mutex>
+
+struct usb_device;
+struct usb_request;
+struct usb_endpoint_descriptor;
+
+class MtpDeviceInfo;
+class MtpEventPacket;
+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;
+	MtpEventPacket			mEventPacket;
+
+	// set to true if we received a response packet instead of a data packet
+	bool					mReceivedResponse;
+	bool					mProcessingEvent;
+	int						mCurrentEventHandle;
+
+	// to check if a sendObject request follows the last sendObjectInfo request.
+	MtpTransactionID		mLastSendObjectInfoTransactionID;
+	MtpObjectHandle			mLastSendObjectInfoObjectHandle;
+
+	// to ensure only one MTP transaction at a time
+	std::mutex				mMutex;
+	std::mutex				mEventMutex;
+	std::mutex				mEventMutexForInterrupt;
+
+	// Remember the device's packet division mode.
+	UrbPacketDivisionMode	mPacketDivisionMode;
+
+public:
+	typedef bool (*ReadObjectCallback)
+			(void* data, uint32_t offset, uint32_t length, void* clientData);
+
+	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(MtpObjectHandle handle, int size, int srcFD);
+	bool					deleteObject(MtpObjectHandle handle);
+	MtpObjectHandle			getParent(MtpObjectHandle handle);
+	MtpStorageID			getStorageID(MtpObjectHandle handle);
+
+	MtpObjectPropertyList*	getObjectPropsSupported(MtpObjectFormat format);
+
+	MtpProperty*			getDevicePropDesc(MtpDeviceProperty code);
+	MtpProperty*			getObjectPropDesc(MtpObjectProperty code, MtpObjectFormat format);
+
+	// Reads value of |property| for |handle|. Returns true on success.
+	bool					getObjectPropValue(MtpObjectHandle handle, MtpProperty* property);
+
+	bool					readObject(MtpObjectHandle handle, ReadObjectCallback callback,
+									uint32_t objectSize, void* clientData);
+	bool					readObject(MtpObjectHandle handle, const char* destPath, int group,
+									int perm);
+	bool					readObject(MtpObjectHandle handle, int fd);
+	bool					readPartialObject(MtpObjectHandle handle,
+											  uint32_t offset,
+											  uint32_t size,
+											  uint32_t *writtenSize,
+											  ReadObjectCallback callback,
+											  void* clientData);
+	bool					readPartialObject64(MtpObjectHandle handle,
+												uint64_t offset,
+												uint32_t size,
+												uint32_t *writtenSize,
+												ReadObjectCallback callback,
+												void* clientData);
+	// Starts a request to read MTP event from MTP device. It returns a request handle that
+	// can be used for blocking read or cancel. If other thread has already been processing an
+	// event returns -1.
+	int						submitEventRequest();
+	// Waits for MTP event from the device and returns MTP event code. It blocks the current thread
+	// until it receives an event from the device. |handle| should be a request handle returned
+	// by |submitEventRequest|. The function writes event parameters to |parameters|. Returns 0 for
+	// cancellations. Returns -1 for errors.
+	int						reapEventRequest(int handle, uint32_t (*parameters)[3]);
+	// Cancels an event request. |handle| should be request handle returned by
+	// |submitEventRequest|. If there is a thread blocked by |reapEventRequest| with the same
+	// |handle|, the thread will resume.
+	void					discardEventRequest(int handle);
+
+private:
+	// If |objectSize| is not NULL, it checks object size before reading data bytes.
+	bool					readObjectInternal(MtpObjectHandle handle,
+											   ReadObjectCallback callback,
+											   const uint32_t* objectSize,
+											   void* clientData);
+	// If |objectSize| is not NULL, it checks object size before reading data bytes.
+	bool					readData(ReadObjectCallback callback,
+									 const uint32_t* objectSize,
+									 uint32_t* writtenData,
+									 void* clientData);
+	bool					sendRequest(MtpOperationCode operation);
+	bool					sendData();
+	bool					readData();
+	bool					writeDataHeader(MtpOperationCode operation, int dataLength);
+	MtpResponseCode			readResponse();
+};
+
+#endif // _MTP_DEVICE_H
diff --git a/mtp/ffs/MtpDeviceInfo.cpp b/mtp/ffs/MtpDeviceInfo.cpp
new file mode 100644
index 0000000..fa2f95b
--- /dev/null
+++ b/mtp/ffs/MtpDeviceInfo.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.
+ */
+
+#define LOG_TAG "MtpDeviceInfo"
+
+#include "MtpDebug.h"
+#include "MtpDataPacket.h"
+#include "MtpDeviceInfo.h"
+#include "MtpStringBuffer.h"
+
+MtpDeviceInfo::MtpDeviceInfo()
+	:	mStandardVersion(0),
+		mVendorExtensionID(0),
+		mVendorExtensionVersion(0),
+		mVendorExtensionDesc(NULL),
+		mFunctionalMode(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);
+}
+
+bool MtpDeviceInfo::read(MtpDataPacket& packet) {
+	MtpStringBuffer string;
+
+	// read the device info
+	if (!packet.getUInt16(mStandardVersion)) return false;
+	if (!packet.getUInt32(mVendorExtensionID)) return false;
+	if (!packet.getUInt16(mVendorExtensionVersion)) return false;
+
+	if (!packet.getString(string)) return false;
+	mVendorExtensionDesc = strdup((const char *)string);
+	if (!mVendorExtensionDesc) return false;
+
+	if (!packet.getUInt16(mFunctionalMode)) return false;
+	mOperations = packet.getAUInt16();
+	if (!mOperations) return false;
+	mEvents = packet.getAUInt16();
+	if (!mEvents) return false;
+	mDeviceProperties = packet.getAUInt16();
+	if (!mDeviceProperties) return false;
+	mCaptureFormats = packet.getAUInt16();
+	if (!mCaptureFormats) return false;
+	mPlaybackFormats = packet.getAUInt16();
+	if (!mCaptureFormats) return false;
+
+	if (!packet.getString(string)) return false;
+	mManufacturer = strdup((const char *)string);
+	if (!mManufacturer) return false;
+	if (!packet.getString(string)) return false;
+	mModel = strdup((const char *)string);
+	if (!mModel) return false;
+	if (!packet.getString(string)) return false;
+	mVersion = strdup((const char *)string);
+	if (!mVersion) return false;
+	if (!packet.getString(string)) return false;
+	mSerial = strdup((const char *)string);
+	if (!mSerial) return false;
+
+	return true;
+}
+
+void MtpDeviceInfo::print() {
+	ALOGV("Device Info:\n\tmStandardVersion: %d\n\tmVendorExtensionID: %d\n\tmVendorExtensionVersiony: %d\n",
+			mStandardVersion, mVendorExtensionID, mVendorExtensionVersion);
+	ALOGV("\tmVendorExtensionDesc: %s\n\tmFunctionalMode: %d\n\tmManufacturer: %s\n\tmModel: %s\n\tmVersion: %s\n\tmSerial: %s\n",
+			mVendorExtensionDesc, mFunctionalMode, mManufacturer, mModel, mVersion, mSerial);
+}
diff --git a/mtp/ffs/MtpDeviceInfo.h b/mtp/ffs/MtpDeviceInfo.h
new file mode 100644
index 0000000..1690b61
--- /dev/null
+++ b/mtp/ffs/MtpDeviceInfo.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_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				mFunctionalMode;
+	UInt16List*				mOperations;
+	UInt16List*				mEvents;
+	MtpDevicePropertyList*	mDeviceProperties;
+	MtpObjectFormatList*	mCaptureFormats;
+	MtpObjectFormatList*	mPlaybackFormats;
+	char*					mManufacturer;
+	char*					mModel;
+	char*					mVersion;
+	char*					mSerial;
+
+public:
+							MtpDeviceInfo();
+	virtual					~MtpDeviceInfo();
+
+	bool					read(MtpDataPacket& packet);
+
+	void					print();
+};
+
+#endif // _MTP_DEVICE_INFO_H
diff --git a/mtp/ffs/MtpEventPacket.cpp b/mtp/ffs/MtpEventPacket.cpp
new file mode 100644
index 0000000..fa7db8c
--- /dev/null
+++ b/mtp/ffs/MtpEventPacket.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "MtpEventPacket"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "IMtpHandle.h"
+#include "MtpEventPacket.h"
+
+#include <usbhost/usbhost.h>
+
+MtpEventPacket::MtpEventPacket()
+	:	MtpPacket(512)
+{
+}
+
+MtpEventPacket::~MtpEventPacket() {
+}
+
+#ifdef MTP_DEVICE
+int MtpEventPacket::write(IMtpHandle *h) {
+	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 = h->sendEvent(event);
+	return (ret < 0 ? ret : 0);
+}
+#endif
+
+#ifdef MTP_HOST
+int MtpEventPacket::sendRequest(struct usb_request *request) {
+	request->buffer = mBuffer;
+	request->buffer_length = mBufferSize;
+	mPacketSize = 0;
+	if (usb_request_queue(request)) {
+		MTPE("usb_endpoint_queue failed, errno: %d", errno);
+		return -1;
+	}
+	return 0;
+}
+
+int MtpEventPacket::readResponse(struct usb_device *device) {
+	struct usb_request* const req = usb_request_wait(device, -1);
+	if (req) {
+		mPacketSize = req->actual_length;
+		return req->actual_length;
+	} else {
+		return -1;
+	}
+}
+#endif
diff --git a/mtp/ffs/MtpEventPacket.h b/mtp/ffs/MtpEventPacket.h
new file mode 100644
index 0000000..e8f50f4
--- /dev/null
+++ b/mtp/ffs/MtpEventPacket.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.
+ */
+
+#ifndef _MTP_EVENT_PACKET_H
+#define _MTP_EVENT_PACKET_H
+
+#include "MtpPacket.h"
+#include "mtp.h"
+
+#include <errno.h>
+
+class IMtpHandle;
+
+class MtpEventPacket : public MtpPacket {
+
+public:
+						MtpEventPacket();
+	virtual				~MtpEventPacket();
+
+#ifdef MTP_DEVICE
+	// write our data to the given usb handle
+	int					write(IMtpHandle *h);
+#endif
+
+#ifdef MTP_HOST
+	// read our buffer with the given request
+	int					sendRequest(struct usb_request *request);
+	int					readResponse(struct usb_device *device);
+#endif
+
+	inline MtpEventCode		getEventCode() const { return getContainerCode(); }
+	inline void				setEventCode(MtpEventCode code)
+													 { return setContainerCode(code); }
+};
+
+#endif // _MTP_EVENT_PACKET_H
diff --git a/mtp/ffs/MtpFfsCompatHandle.cpp b/mtp/ffs/MtpFfsCompatHandle.cpp
new file mode 100644
index 0000000..4027d60
--- /dev/null
+++ b/mtp/ffs/MtpFfsCompatHandle.cpp
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/functionfs.h>
+#include <mutex>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/endian.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "PosixAsyncIO.h"
+#include "MtpFfsCompatHandle.h"
+#include "mtp.h"
+
+#define FUNCTIONFS_ENDPOINT_ALLOC		_IOR('g', 231, __u32)
+
+namespace {
+
+// Must be divisible by all max packet size values
+constexpr int MAX_FILE_CHUNK_SIZE = 3145728;
+
+// Safe values since some devices cannot handle large DMAs
+// To get good performance, override these with
+// higher values per device using the properties
+// sys.usb.ffs.max_read and sys.usb.ffs.max_write
+constexpr int USB_FFS_MAX_WRITE = MTP_BUFFER_SIZE;
+constexpr int USB_FFS_MAX_READ = MTP_BUFFER_SIZE;
+
+static_assert(USB_FFS_MAX_WRITE > 0, "Max r/w values must be > 0!");
+static_assert(USB_FFS_MAX_READ > 0, "Max r/w values must be > 0!");
+
+constexpr unsigned int MAX_MTP_FILE_SIZE = 0xFFFFFFFF;
+
+constexpr size_t ENDPOINT_ALLOC_RETRIES = 10;
+
+} // anonymous namespace
+
+
+MtpFfsCompatHandle::MtpFfsCompatHandle(int controlFd) :
+	MtpFfsHandle(controlFd),
+	mMaxWrite(USB_FFS_MAX_WRITE),
+	mMaxRead(USB_FFS_MAX_READ) {}
+
+MtpFfsCompatHandle::~MtpFfsCompatHandle() {}
+
+int MtpFfsCompatHandle::writeHandle(int fd, const void* data, size_t len) {
+	int ret = 0;
+	const char* buf = static_cast<const char*>(data);
+	while (len > 0) {
+		int write_len = std::min(mMaxWrite, len);
+		int n = TEMP_FAILURE_RETRY(::write(fd, buf, write_len));
+
+		if (n < 0) {
+			PLOG(ERROR) << "write ERROR: fd = " << fd << ", n = " << n;
+			return -1;
+		} else if (n < write_len) {
+			errno = EIO;
+			PLOG(ERROR) << "less written than expected";
+			return -1;
+		}
+		buf += n;
+		len -= n;
+		ret += n;
+	}
+	return ret;
+}
+
+int MtpFfsCompatHandle::readHandle(int fd, void* data, size_t len) {
+	int ret = 0;
+	char* buf = static_cast<char*>(data);
+	while (len > 0) {
+		int read_len = std::min(mMaxRead, len);
+		int n = TEMP_FAILURE_RETRY(::read(fd, buf, read_len));
+		if (n < 0) {
+			PLOG(ERROR) << "read ERROR: fd = " << fd << ", n = " << n;
+			return -1;
+		}
+		ret += n;
+		if (n < read_len) // done reading early
+			break;
+		buf += n;
+		len -= n;
+	}
+	return ret;
+}
+
+int MtpFfsCompatHandle::start(bool ptp) {
+	if (!openEndpoints(ptp))
+		return -1;
+
+	for (unsigned i = 0; i < NUM_IO_BUFS; i++) {
+		mIobuf[i].bufs.resize(MAX_FILE_CHUNK_SIZE);
+		posix_madvise(mIobuf[i].bufs.data(), MAX_FILE_CHUNK_SIZE,
+				POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED);
+	}
+
+	// Get device specific r/w size
+	mMaxWrite = android::base::GetIntProperty("sys.usb.ffs.max_write", USB_FFS_MAX_WRITE);
+	mMaxRead = android::base::GetIntProperty("sys.usb.ffs.max_read", USB_FFS_MAX_READ);
+
+	size_t attempts = 0;
+	while (mMaxWrite >= USB_FFS_MAX_WRITE && mMaxRead >= USB_FFS_MAX_READ &&
+			attempts < ENDPOINT_ALLOC_RETRIES) {
+		// If larger contiguous chunks of memory aren't available, attempt to try
+		// smaller allocations.
+		if (ioctl(mBulkIn, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(mMaxWrite)) ||
+			ioctl(mBulkOut, FUNCTIONFS_ENDPOINT_ALLOC, static_cast<__u32>(mMaxRead))) {
+			if (errno == ENODEV) {
+				// Driver hasn't enabled endpoints yet.
+				std::this_thread::sleep_for(std::chrono::milliseconds(100));
+				attempts += 1;
+				continue;
+			}
+			mMaxWrite /= 2;
+			mMaxRead /=2;
+		} else {
+			return 0;
+		}
+	}
+	// Try to start MtpServer anyway, with the smallest max r/w values
+	mMaxWrite = USB_FFS_MAX_WRITE;
+	mMaxRead = USB_FFS_MAX_READ;
+	PLOG(ERROR) << "Functionfs could not allocate any memory!";
+	return 0;
+}
+
+int MtpFfsCompatHandle::read(void* data, size_t len) {
+	return readHandle(mBulkOut, data, len);
+}
+
+int MtpFfsCompatHandle::write(const void* data, size_t len) {
+	return writeHandle(mBulkIn, data, len);
+}
+
+int MtpFfsCompatHandle::receiveFile(mtp_file_range mfr, bool zero_packet) {
+	// When receiving files, the incoming length is given in 32 bits.
+	// A >4G file is given as 0xFFFFFFFF
+	uint32_t file_length = mfr.length;
+	uint64_t offset = mfr.offset;
+	int packet_size = getPacketSize(mBulkOut);
+
+	unsigned char *data = mIobuf[0].bufs.data();
+	unsigned char *data2 = mIobuf[1].bufs.data();
+
+	struct aiocb aio;
+	aio.aio_fildes = mfr.fd;
+	aio.aio_buf = nullptr;
+	struct aiocb *aiol[] = {&aio};
+	int ret = -1;
+	size_t length;
+	bool read = false;
+	bool write = false;
+
+	posix_fadvise(mfr.fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
+
+	// Break down the file into pieces that fit in buffers
+	while (file_length > 0 || write) {
+		if (file_length > 0) {
+			length = std::min(static_cast<uint32_t>(MAX_FILE_CHUNK_SIZE), file_length);
+
+			// Read data from USB, handle errors after waiting for write thread.
+			ret = readHandle(mBulkOut, data, length);
+
+			if (file_length != MAX_MTP_FILE_SIZE && ret < static_cast<int>(length)) {
+				ret = -1;
+				errno = EIO;
+			}
+			read = true;
+		}
+
+		if (write) {
+			// get the return status of the last write request
+			aio_suspend(aiol, 1, nullptr);
+
+			int written = aio_return(&aio);
+			if (written == -1) {
+				errno = aio_error(&aio);
+				return -1;
+			}
+			if (static_cast<size_t>(written) < aio.aio_nbytes) {
+				errno = EIO;
+				return -1;
+			}
+			write = false;
+		}
+
+		// If there was an error reading above
+		if (ret == -1) {
+			return -1;
+		}
+
+		if (read) {
+			if (file_length == MAX_MTP_FILE_SIZE) {
+				// For larger files, receive until a short packet is received.
+				if (static_cast<size_t>(ret) < length) {
+					file_length = 0;
+				}
+			} else {
+				file_length -= ret;
+			}
+			// Enqueue a new write request
+			aio_prepare(&aio, data, length, offset);
+			aio_write(&aio);
+
+			offset += ret;
+			std::swap(data, data2);
+
+			write = true;
+			read = false;
+		}
+	}
+	// Receive an empty packet if size is a multiple of the endpoint size.
+	if (ret % packet_size == 0 || zero_packet) {
+		if (TEMP_FAILURE_RETRY(::read(mBulkOut, data, packet_size)) != 0) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+int MtpFfsCompatHandle::sendFile(mtp_file_range mfr) {
+	uint64_t file_length = mfr.length;
+	uint32_t given_length = std::min(static_cast<uint64_t>(MAX_MTP_FILE_SIZE),
+			file_length + sizeof(mtp_data_header));
+	uint64_t offset = mfr.offset;
+	int packet_size = getPacketSize(mBulkIn);
+
+	// If file_length is larger than a size_t, truncating would produce the wrong comparison.
+	// Instead, promote the left side to 64 bits, then truncate the small result.
+	int init_read_len = std::min(
+			static_cast<uint64_t>(packet_size - sizeof(mtp_data_header)), file_length);
+
+	unsigned char *data = mIobuf[0].bufs.data();
+	unsigned char *data2 = mIobuf[1].bufs.data();
+
+	posix_fadvise(mfr.fd, 0, 0, POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE);
+
+	struct aiocb aio;
+	aio.aio_fildes = mfr.fd;
+	struct aiocb *aiol[] = {&aio};
+	int ret, length;
+	int error = 0;
+	bool read = false;
+	bool write = false;
+
+	// Send the header data
+	mtp_data_header *header = reinterpret_cast<mtp_data_header*>(data);
+	header->length = htole32(given_length);
+	header->type = htole16(2); /* data packet */
+	header->command = htole16(mfr.command);
+	header->transaction_id = htole32(mfr.transaction_id);
+
+	// Some hosts don't support header/data separation even though MTP allows it
+	// Handle by filling first packet with initial file data
+	if (TEMP_FAILURE_RETRY(pread(mfr.fd, reinterpret_cast<char*>(data) +
+					sizeof(mtp_data_header), init_read_len, offset))
+			!= init_read_len) return -1;
+	if (writeHandle(mBulkIn, data, sizeof(mtp_data_header) + init_read_len) == -1) return -1;
+	file_length -= init_read_len;
+	offset += init_read_len;
+	ret = init_read_len + sizeof(mtp_data_header);
+
+	// Break down the file into pieces that fit in buffers
+	while (file_length > 0) {
+		if (read) {
+			// Wait for the previous read to finish
+			aio_suspend(aiol, 1, nullptr);
+			ret = aio_return(&aio);
+			if (ret == -1) {
+				errno = aio_error(&aio);
+				return -1;
+			}
+			if (static_cast<size_t>(ret) < aio.aio_nbytes) {
+				errno = EIO;
+				return -1;
+			}
+
+			file_length -= ret;
+			offset += ret;
+			std::swap(data, data2);
+			read = false;
+			write = true;
+		}
+
+		if (error == -1) {
+			return -1;
+		}
+
+		if (file_length > 0) {
+			length = std::min(static_cast<uint64_t>(MAX_FILE_CHUNK_SIZE), file_length);
+			// Queue up another read
+			aio_prepare(&aio, data, length, offset);
+			aio_read(&aio);
+			read = true;
+		}
+
+		if (write) {
+			if (writeHandle(mBulkIn, data2, ret) == -1) {
+				error = -1;
+			}
+			write = false;
+		}
+	}
+
+	if (ret % packet_size == 0) {
+		// If the last packet wasn't short, send a final empty packet
+		if (TEMP_FAILURE_RETRY(::write(mBulkIn, data, 0)) != 0) {
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
diff --git a/mtp/ffs/MtpFfsCompatHandle.h b/mtp/ffs/MtpFfsCompatHandle.h
new file mode 100644
index 0000000..6f47e60
--- /dev/null
+++ b/mtp/ffs/MtpFfsCompatHandle.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_FFS_COMPAT_HANDLE_H
+#define _MTP_FFS_COMPAT_HANDLE_H
+
+#include <MtpFfsHandle.h>
+
+template <class T> class MtpFfsHandleTest;
+
+class MtpFfsCompatHandle : public MtpFfsHandle {
+	template <class T> friend class MtpFfsHandleTest;
+private:
+	int writeHandle(int fd, const void *data, size_t len);
+	int readHandle(int fd, void *data, size_t len);
+
+	size_t mMaxWrite;
+	size_t mMaxRead;
+
+public:
+	int read(void* data, size_t len) override;
+	int write(const void* data, size_t len) override;
+	int receiveFile(mtp_file_range mfr, bool zero_packet) override;
+	int sendFile(mtp_file_range mfr) override;
+
+	/**
+	 * Open ffs endpoints and allocate necessary kernel and user memory.
+	 * Will sleep until endpoints are enabled, for up to 1 second.
+	 */
+	int start(bool ptp) override;
+
+	MtpFfsCompatHandle(int controlFd);
+	~MtpFfsCompatHandle();
+};
+
+#endif // _MTP_FFS_COMPAT_HANDLE_H
+
diff --git a/mtp/ffs/MtpFfsHandle.cpp b/mtp/ffs/MtpFfsHandle.cpp
new file mode 100644
index 0000000..01b6f2e
--- /dev/null
+++ b/mtp/ffs/MtpFfsHandle.cpp
@@ -0,0 +1,674 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <asyncio/AsyncIO.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <memory>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/eventfd.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/poll.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "PosixAsyncIO.h"
+#include "MtpDescriptors.h"
+#include "MtpFfsHandle.h"
+#include "mtp.h"
+#include "MtpDebug.h"
+
+namespace {
+
+constexpr unsigned AIO_BUFS_MAX = 128;
+constexpr unsigned AIO_BUF_LEN = 16384;
+
+constexpr unsigned FFS_NUM_EVENTS = 5;
+
+constexpr unsigned MAX_FILE_CHUNK_SIZE = AIO_BUFS_MAX * AIO_BUF_LEN;
+
+constexpr uint32_t MAX_MTP_FILE_SIZE = 0xFFFFFFFF;
+
+struct timespec ZERO_TIMEOUT = { 0, 0 };
+
+struct mtp_device_status {
+	uint16_t  wLength;
+	uint16_t  wCode;
+};
+
+} // anonymous namespace
+
+int MtpFfsHandle::getPacketSize(int ffs_fd) {
+	struct usb_endpoint_descriptor desc;
+	if (ioctl(ffs_fd, FUNCTIONFS_ENDPOINT_DESC, reinterpret_cast<unsigned long>(&desc))) {
+		MTPE("Could not get FFS bulk-in descriptor\n");
+		return MAX_PACKET_SIZE_HS;
+	} else {
+		return desc.wMaxPacketSize;
+	}
+}
+
+MtpFfsHandle::MtpFfsHandle(int controlFd) {
+	mControl.reset(controlFd);
+}
+
+MtpFfsHandle::~MtpFfsHandle() {}
+
+void MtpFfsHandle::closeEndpoints() {
+	mIntr.reset();
+	mBulkIn.reset();
+	mBulkOut.reset();
+}
+
+bool MtpFfsHandle::openEndpoints(bool ptp) {
+	if (mBulkIn < 0) {
+		mBulkIn.reset(TEMP_FAILURE_RETRY(open(ptp ? FFS_PTP_EP_IN : FFS_MTP_EP_IN, O_RDWR)));
+		if (mBulkIn < 0) {
+			MTPE("cannot open bulk in ep\n");
+			return false;
+		}
+	}
+
+	if (mBulkOut < 0) {
+		mBulkOut.reset(TEMP_FAILURE_RETRY(open(ptp ? FFS_PTP_EP_OUT : FFS_MTP_EP_OUT, O_RDWR)));
+		if (mBulkOut < 0) {
+			MTPE("cannot open bulk out ep\n");
+			return false;
+		}
+	}
+
+	if (mIntr < 0) {
+		mIntr.reset(TEMP_FAILURE_RETRY(open(ptp ? FFS_PTP_EP_INTR : FFS_MTP_EP_INTR, O_RDWR)));
+		if (mIntr < 0) {
+			MTPE("cannot open intr ep\n");
+			return false;
+		}
+	}
+	return true;
+}
+
+void MtpFfsHandle::advise(int fd) {
+	for (unsigned i = 0; i < NUM_IO_BUFS; i++) {
+		if (posix_madvise(mIobuf[i].bufs.data(), MAX_FILE_CHUNK_SIZE,
+				POSIX_MADV_SEQUENTIAL | POSIX_MADV_WILLNEED) < 0)
+			MTPE("Failed to madvise\n");
+	}
+	if (posix_fadvise(fd, 0, 0,
+				POSIX_FADV_SEQUENTIAL | POSIX_FADV_NOREUSE | POSIX_FADV_WILLNEED) < 0)
+		MTPE("Failed to fadvise\n");
+}
+
+bool MtpFfsHandle::writeDescriptors(bool ptp) {
+	return ::writeDescriptors(mControl, ptp);
+}
+
+void MtpFfsHandle::closeConfig() {
+	mControl.reset();
+}
+
+int MtpFfsHandle::doAsync(void* data, size_t len, bool read, bool zero_packet) {
+	struct io_event ioevs[AIO_BUFS_MAX];
+	size_t total = 0;
+
+	while (total < len) {
+		size_t this_len = std::min(len - total, static_cast<size_t>(AIO_BUF_LEN * AIO_BUFS_MAX));
+		int num_bufs = this_len / AIO_BUF_LEN + (this_len % AIO_BUF_LEN == 0 ? 0 : 1);
+		for (int i = 0; i < num_bufs; i++) {
+			mIobuf[0].buf[i] = reinterpret_cast<unsigned char*>(data) + total + i * AIO_BUF_LEN;
+		}
+		int ret = iobufSubmit(&mIobuf[0], read ? mBulkOut : mBulkIn, this_len, read);
+		if (ret < 0) return -1;
+		ret = waitEvents(&mIobuf[0], ret, ioevs, nullptr);
+		if (ret < 0) return -1;
+		total += ret;
+		if (static_cast<size_t>(ret) < this_len) break;
+	}
+
+	int packet_size = getPacketSize(read ? mBulkOut : mBulkIn);
+	if (len % packet_size == 0 && zero_packet) {
+		int ret = iobufSubmit(&mIobuf[0], read ? mBulkOut : mBulkIn, 0, read);
+		if (ret < 0) return -1;
+		ret = waitEvents(&mIobuf[0], ret, ioevs, nullptr);
+		if (ret < 0) return -1;
+	}
+
+	for (unsigned i = 0; i < AIO_BUFS_MAX; i++) {
+		mIobuf[0].buf[i] = mIobuf[0].bufs.data() + i * AIO_BUF_LEN;
+	}
+	return total;
+}
+
+int MtpFfsHandle::read(void* data, size_t len) {
+	// Zero packets are handled by receiveFile()
+	return doAsync(data, len, true, false);
+}
+
+int MtpFfsHandle::write(const void* data, size_t len) {
+	return doAsync(const_cast<void*>(data), len, false, true);
+}
+
+int MtpFfsHandle::handleEvent() {
+
+	std::vector<usb_functionfs_event> events(FFS_NUM_EVENTS);
+	usb_functionfs_event *event = events.data();
+	int nbytes = TEMP_FAILURE_RETRY(::read(mControl, event,
+				events.size() * sizeof(usb_functionfs_event)));
+	if (nbytes == -1) {
+		return -1;
+	}
+	int ret = 0;
+	for (size_t n = nbytes / sizeof *event; n; --n, ++event) {
+		switch (event->type) {
+		case FUNCTIONFS_BIND:
+		case FUNCTIONFS_ENABLE:
+			ret = 0;
+			errno = 0;
+			break;
+		case FUNCTIONFS_UNBIND:
+		case FUNCTIONFS_DISABLE:
+			errno = ESHUTDOWN;
+			ret = -1;
+			break;
+		case FUNCTIONFS_SETUP:
+			if (handleControlRequest(&event->u.setup) == -1)
+				ret = -1;
+			break;
+		case FUNCTIONFS_SUSPEND:
+		case FUNCTIONFS_RESUME:
+			break;
+		default:
+			MTPE("Mtp Event (unknown)\n");
+		}
+	}
+	return ret;
+}
+
+int MtpFfsHandle::handleControlRequest(const struct usb_ctrlrequest *setup) {
+	uint8_t type = setup->bRequestType;
+	uint8_t code = setup->bRequest;
+	uint16_t length = setup->wLength;
+	uint16_t index = setup->wIndex;
+	uint16_t value = setup->wValue;
+	std::vector<char> buf;
+	buf.resize(length);
+	int ret = 0;
+
+	if (!(type & USB_DIR_IN)) {
+		if (::read(mControl, buf.data(), length) != length) {
+			MTPE("Mtp error ctrlreq read data");
+		}
+	}
+
+	if ((type & USB_TYPE_MASK) == USB_TYPE_CLASS && index == 0 && value == 0) {
+		switch(code) {
+		case MTP_REQ_RESET:
+		case MTP_REQ_CANCEL:
+			errno = ECANCELED;
+			ret = -1;
+			break;
+		case MTP_REQ_GET_DEVICE_STATUS:
+		{
+			if (length < sizeof(struct mtp_device_status) + 4) {
+				errno = EINVAL;
+				return -1;
+			}
+			struct mtp_device_status *st = reinterpret_cast<struct mtp_device_status*>(buf.data());
+			st->wLength = htole16(sizeof(st));
+			if (mCanceled) {
+				st->wLength += 4;
+				st->wCode = MTP_RESPONSE_TRANSACTION_CANCELLED;
+				uint16_t *endpoints = reinterpret_cast<uint16_t*>(st + 1);
+				endpoints[0] = ioctl(mBulkIn, FUNCTIONFS_ENDPOINT_REVMAP);
+				endpoints[1] = ioctl(mBulkOut, FUNCTIONFS_ENDPOINT_REVMAP);
+				mCanceled = false;
+			} else {
+				st->wCode = MTP_RESPONSE_OK;
+			}
+			length = st->wLength;
+			break;
+		}
+		default:
+			MTPE("Unrecognized Mtp class request!\n");
+		}
+	} else {
+		MTPE("Unrecognized request type\n");
+	}
+
+	if (type & USB_DIR_IN) {
+		if (::write(mControl, buf.data(), length) != length) {
+			MTPE("Mtp error ctrlreq write data");
+		}
+	}
+	return 0;
+}
+
+int MtpFfsHandle::start(bool ptp) {
+	if (!openEndpoints(ptp))
+		return -1;
+
+	for (unsigned i = 0; i < NUM_IO_BUFS; i++) {
+		mIobuf[i].bufs.resize(MAX_FILE_CHUNK_SIZE);
+		mIobuf[i].iocb.resize(AIO_BUFS_MAX);
+		mIobuf[i].iocbs.resize(AIO_BUFS_MAX);
+		mIobuf[i].buf.resize(AIO_BUFS_MAX);
+		for (unsigned j = 0; j < AIO_BUFS_MAX; j++) {
+			mIobuf[i].buf[j] = mIobuf[i].bufs.data() + j * AIO_BUF_LEN;
+			mIobuf[i].iocb[j] = &mIobuf[i].iocbs[j];
+		}
+	}
+
+	memset(&mCtx, 0, sizeof(mCtx));
+	if (io_setup(AIO_BUFS_MAX, &mCtx) < 0) {
+		MTPE("unable to setup aio");
+		return -1;
+	}
+	mEventFd.reset(eventfd(0, EFD_NONBLOCK));
+	mPollFds[0].fd = mControl;
+	mPollFds[0].events = POLLIN;
+	mPollFds[1].fd = mEventFd;
+	mPollFds[1].events = POLLIN;
+
+	mCanceled = false;
+	return 0;
+}
+
+void MtpFfsHandle::close() {
+	io_destroy(mCtx);
+	closeEndpoints();
+	closeConfig();
+}
+
+int MtpFfsHandle::waitEvents(__attribute__((unused)) struct io_buffer *buf, int min_events, struct io_event *events,
+		int *counter) {
+	int num_events = 0;
+	int ret = 0;
+	int error = 0;
+
+	while (num_events < min_events) {
+		if (poll(mPollFds, 2, 0) == -1) {
+			MTPE("Mtp error during poll()\n");
+			return -1;
+		}
+		if (mPollFds[0].revents & POLLIN) {
+			mPollFds[0].revents = 0;
+			if (handleEvent() == -1) {
+				error = errno;
+			}
+		}
+		if (mPollFds[1].revents & POLLIN) {
+			mPollFds[1].revents = 0;
+			uint64_t ev_cnt = 0;
+
+			if (::read(mEventFd, &ev_cnt, sizeof(ev_cnt)) == -1) {
+				MTPE("Mtp unable to read eventfd\n");
+				error = errno;
+				continue;
+			}
+
+			// It's possible that io_getevents will return more events than the eventFd reported,
+			// since events may appear in the time between the calls. In this case, the eventFd will
+			// show up as readable next iteration, but there will be fewer or no events to actually
+			// wait for. Thus we never want io_getevents to block.
+			int this_events = TEMP_FAILURE_RETRY(io_getevents(mCtx, 0, AIO_BUFS_MAX, events, &ZERO_TIMEOUT));
+			if (this_events == -1) {
+				MTPE("Mtp error getting events");
+				error = errno;
+			}
+			// Add up the total amount of data and find errors on the way.
+			for (unsigned j = 0; j < static_cast<unsigned>(this_events); j++) {
+				if (events[j].res < 0) {
+					errno = -events[j].res;
+					MTPE("Mtp got error event\n");
+					error = errno;
+				}
+				ret += events[j].res;
+			}
+			num_events += this_events;
+			if (counter)
+				*counter += this_events;
+		}
+		if (error) {
+			errno = error;
+			ret = -1;
+			break;
+		}
+	}
+	return ret;
+}
+
+void MtpFfsHandle::cancelTransaction() {
+	// Device cancels by stalling both bulk endpoints.
+	if (::read(mBulkIn, nullptr, 0) != -1 || errno != EBADMSG)
+		MTPE("Mtp stall failed on bulk in\n");
+	if (::write(mBulkOut, nullptr, 0) != -1 || errno != EBADMSG)
+		MTPE("Mtp stall failed on bulk out\n");
+	mCanceled = true;
+	errno = ECANCELED;
+}
+
+int MtpFfsHandle::cancelEvents(struct iocb **iocb, struct io_event *events, unsigned start,
+		unsigned end) {
+	// Some manpages for io_cancel are out of date and incorrect.
+	// io_cancel will return -EINPROGRESS on success and does
+	// not place the event in the given memory. We have to use
+	// io_getevents to wait for all the events we cancelled.
+	int ret = 0;
+	unsigned num_events = 0;
+	int save_errno = errno;
+	errno = 0;
+
+	for (unsigned j = start; j < end; j++) {
+		if (io_cancel(mCtx, iocb[j], nullptr) != -1 || errno != EINPROGRESS) {
+			MTPE("Mtp couldn't cancel request\n");
+		} else {
+			num_events++;
+		}
+	}
+	if (num_events != end - start) {
+		ret = -1;
+		errno = EIO;
+	}
+	int evs = TEMP_FAILURE_RETRY(io_getevents(mCtx, num_events, AIO_BUFS_MAX, events, nullptr));
+	if (static_cast<unsigned>(evs) != num_events) {
+		MTPE("Mtp couldn't cancel all requests\n");
+		ret = -1;
+	}
+
+	uint64_t ev_cnt = 0;
+	if (num_events && ::read(mEventFd, &ev_cnt, sizeof(ev_cnt)) == -1)
+		MTPE("Mtp Unable to read event fd\n");
+
+	if (ret == 0) {
+		// Restore errno since it probably got overriden with EINPROGRESS.
+		errno = save_errno;
+	}
+	return ret;
+}
+
+int MtpFfsHandle::iobufSubmit(struct io_buffer *buf, int fd, unsigned length, bool read) {
+	int ret = 0;
+	buf->actual = AIO_BUFS_MAX;
+	for (unsigned j = 0; j < AIO_BUFS_MAX; j++) {
+		unsigned rq_length = std::min(AIO_BUF_LEN, length - AIO_BUF_LEN * j);
+		io_prep(buf->iocb[j], fd, buf->buf[j], rq_length, 0, read);
+		buf->iocb[j]->aio_flags |= IOCB_FLAG_RESFD;
+		buf->iocb[j]->aio_resfd = mEventFd;
+
+		// Not enough data, so table is truncated.
+		if (rq_length < AIO_BUF_LEN || length == AIO_BUF_LEN * (j + 1)) {
+			buf->actual = j + 1;
+			break;
+		}
+	}
+
+	ret = io_submit(mCtx, buf->actual, buf->iocb.data());
+	if (ret != static_cast<int>(buf->actual)) {
+		MTPE("Mtp io_submit\n");
+		if (ret != -1) {
+			errno = EIO;
+		}
+		ret = -1;
+	}
+	return ret;
+}
+
+int MtpFfsHandle::receiveFile(mtp_file_range mfr, bool zero_packet) {
+	// When receiving files, the incoming length is given in 32 bits.
+	// A >=4G file is given as 0xFFFFFFFF
+	uint32_t file_length = mfr.length;
+	uint64_t offset = mfr.offset;
+
+	struct aiocb aio;
+	aio.aio_fildes = mfr.fd;
+	aio.aio_buf = nullptr;
+	struct aiocb *aiol[] = {&aio};
+
+	int ret = -1;
+	unsigned i = 0;
+	size_t length;
+	struct io_event ioevs[AIO_BUFS_MAX];
+	bool has_write = false;
+	bool error = false;
+	bool write_error = false;
+	int packet_size = getPacketSize(mBulkOut);
+	bool short_packet = false;
+	advise(mfr.fd);
+
+	// Break down the file into pieces that fit in buffers
+	while (file_length > 0 || has_write) {
+		// Queue an asynchronous read from USB.
+		if (file_length > 0) {
+			length = std::min(static_cast<uint32_t>(MAX_FILE_CHUNK_SIZE), file_length);
+			if (iobufSubmit(&mIobuf[i], mBulkOut, length, true) == -1)
+				error = true;
+		}
+
+		// Get the return status of the last write request.
+		if (has_write) {
+			aio_suspend(aiol, 1, nullptr);
+			int written = aio_return(&aio);
+			if (static_cast<size_t>(written) < aio.aio_nbytes) {
+				errno = written == -1 ? aio_error(&aio) : EIO;
+				MTPE("Mtp error writing to disk\n");
+				write_error = true;
+			}
+			has_write = false;
+		}
+
+		if (error) {
+			return -1;
+		}
+
+		// Get the result of the read request, and queue a write to disk.
+		if (file_length > 0) {
+			unsigned num_events = 0;
+			ret = 0;
+			unsigned short_i = mIobuf[i].actual;
+			while (num_events < short_i) {
+				// Get all events up to the short read, if there is one.
+				// We must wait for each event since data transfer could end at any time.
+				int this_events = 0;
+				int event_ret = waitEvents(&mIobuf[i], 1, ioevs, &this_events);
+				num_events += this_events;
+
+				if (event_ret == -1) {
+					cancelEvents(mIobuf[i].iocb.data(), ioevs, num_events, mIobuf[i].actual);
+					return -1;
+				}
+				ret += event_ret;
+				for (int j = 0; j < this_events; j++) {
+					// struct io_event contains a pointer to the associated struct iocb as a __u64.
+					if (static_cast<__u64>(ioevs[j].res) <
+							reinterpret_cast<struct iocb*>(ioevs[j].obj)->aio_nbytes) {
+						// We've found a short event. Store the index since
+						// events won't necessarily arrive in the order they are queued.
+						short_i = (ioevs[j].obj - reinterpret_cast<uint64_t>(mIobuf[i].iocbs.data()))
+							/ sizeof(struct iocb) + 1;
+						short_packet = true;
+					}
+				}
+			}
+			if (short_packet) {
+				if (cancelEvents(mIobuf[i].iocb.data(), ioevs, short_i, mIobuf[i].actual)) {
+					write_error = true;
+				}
+			}
+			if (file_length == MAX_MTP_FILE_SIZE) {
+				// For larger files, receive until a short packet is received.
+				if (static_cast<size_t>(ret) < length) {
+					file_length = 0;
+				}
+			} else if (ret < static_cast<int>(length)) {
+				// If file is less than 4G and we get a short packet, it's an error.
+				errno = EIO;
+				MTPE("Mtp got unexpected short packet\n");
+				return -1;
+			} else {
+				file_length -= ret;
+			}
+
+			if (write_error) {
+				cancelTransaction();
+				return -1;
+			}
+
+			// Enqueue a new write request
+			aio_prepare(&aio, mIobuf[i].bufs.data(), ret, offset);
+			aio_write(&aio);
+
+			offset += ret;
+			i = (i + 1) % NUM_IO_BUFS;
+			has_write = true;
+		}
+	}
+	if ((ret % packet_size == 0 && !short_packet) || zero_packet) {
+		// Receive an empty packet if size is a multiple of the endpoint size
+		// and we didn't already get an empty packet from the header or large file.
+		if (read(mIobuf[0].bufs.data(), packet_size) != 0) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+int MtpFfsHandle::sendFile(mtp_file_range mfr) {
+	uint64_t file_length = mfr.length;
+	uint32_t given_length = std::min(static_cast<uint64_t>(MAX_MTP_FILE_SIZE),
+			file_length + sizeof(mtp_data_header));
+	uint64_t offset = mfr.offset;
+	int packet_size = getPacketSize(mBulkIn);
+
+	// If file_length is larger than a size_t, truncating would produce the wrong comparison.
+	// Instead, promote the left side to 64 bits, then truncate the small result.
+	int init_read_len = std::min(
+			static_cast<uint64_t>(packet_size - sizeof(mtp_data_header)), file_length);
+
+	advise(mfr.fd);
+
+	struct aiocb aio;
+	aio.aio_fildes = mfr.fd;
+	struct aiocb *aiol[] = {&aio};
+	int ret = 0;
+	int length, num_read;
+	unsigned i = 0;
+	struct io_event ioevs[AIO_BUFS_MAX];
+	bool error = false;
+	bool has_write = false;
+
+	// Send the header data
+	mtp_data_header *header = reinterpret_cast<mtp_data_header*>(mIobuf[0].bufs.data());
+	header->length = htole32(given_length);
+	header->type = htole16(2); // data packet
+	header->command = htole16(mfr.command);
+	header->transaction_id = htole32(mfr.transaction_id);
+
+	// Some hosts don't support header/data separation even though MTP allows it
+	// Handle by filling first packet with initial file data
+	if (TEMP_FAILURE_RETRY(pread(mfr.fd, mIobuf[0].bufs.data() +
+					sizeof(mtp_data_header), init_read_len, offset))
+			!= init_read_len) return -1;
+	if (doAsync(mIobuf[0].bufs.data(), sizeof(mtp_data_header) + init_read_len,
+				false, false /* zlps are handled below */) == -1)
+		return -1;
+	file_length -= init_read_len;
+	offset += init_read_len;
+	ret = init_read_len + sizeof(mtp_data_header);
+
+	// Break down the file into pieces that fit in buffers
+	while(file_length > 0 || has_write) {
+		if (file_length > 0) {
+			// Queue up a read from disk.
+			length = std::min(static_cast<uint64_t>(MAX_FILE_CHUNK_SIZE), file_length);
+			aio_prepare(&aio, mIobuf[i].bufs.data(), length, offset);
+			aio_read(&aio);
+		}
+
+		if (has_write) {
+			// Wait for usb write. Cancel unwritten portion if there's an error.
+			int num_events = 0;
+			if (waitEvents(&mIobuf[(i-1)%NUM_IO_BUFS], mIobuf[(i-1)%NUM_IO_BUFS].actual, ioevs,
+						&num_events) != ret) {
+				error = true;
+				cancelEvents(mIobuf[(i-1)%NUM_IO_BUFS].iocb.data(), ioevs, num_events,
+						mIobuf[(i-1)%NUM_IO_BUFS].actual);
+			}
+			has_write = false;
+		}
+
+		if (file_length > 0) {
+			// Wait for the previous read to finish
+			aio_suspend(aiol, 1, nullptr);
+			num_read = aio_return(&aio);
+			if (static_cast<size_t>(num_read) < aio.aio_nbytes) {
+				errno = num_read == -1 ? aio_error(&aio) : EIO;
+				MTPE("Mtp error reading from disk\n");
+				cancelTransaction();
+				return -1;
+			}
+
+			file_length -= num_read;
+			offset += num_read;
+
+			if (error) {
+				return -1;
+			}
+
+			// Queue up a write to usb.
+			if (iobufSubmit(&mIobuf[i], mBulkIn, num_read, false) == -1) {
+				return -1;
+			}
+			has_write = true;
+			ret = num_read;
+		}
+
+		i = (i + 1) % NUM_IO_BUFS;
+	}
+
+	if (ret % packet_size == 0) {
+		// If the last packet wasn't short, send a final empty packet
+		if (write(mIobuf[0].bufs.data(), 0) != 0) {
+			return -1;
+		}
+	}
+	return 0;
+}
+
+int MtpFfsHandle::sendEvent(mtp_event me) {
+	// Mimic the behavior of f_mtp by sending the event async.
+	// Events aren't critical to the connection, so we don't need to check the return value.
+	char *temp = new char[me.length];
+	memcpy(temp, me.data, me.length);
+	me.data = temp;
+	std::thread t([this, me]() { return this->doSendEvent(me); });
+	t.detach();
+	return 0;
+}
+
+void MtpFfsHandle::doSendEvent(mtp_event me) {
+	unsigned length = me.length;
+	int ret = ::write(mIntr, me.data, length);
+	if (static_cast<unsigned>(ret) != length)
+		MTPE("Mtp error sending event thread!\n");
+	delete[] reinterpret_cast<char*>(me.data);
+}
+
diff --git a/mtp/ffs/MtpFfsHandle.h b/mtp/ffs/MtpFfsHandle.h
new file mode 100644
index 0000000..20f74fa
--- /dev/null
+++ b/mtp/ffs/MtpFfsHandle.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_FFS_HANDLE_H
+#define _MTP_FFS_HANDLE_H
+
+#include <android-base/unique_fd.h>
+#include <linux/aio_abi.h>
+#include <mutex>
+#include <sys/poll.h>
+#include <time.h>
+#include <thread>
+#include <vector>
+
+#include <IMtpHandle.h>
+
+constexpr int NUM_IO_BUFS = 2;
+
+struct io_buffer {
+	std::vector<struct iocb> iocbs;		// Holds memory for all iocbs. Not used directly.
+	std::vector<struct iocb*> iocb;		// Pointers to individual iocbs, for syscalls
+	std::vector<unsigned char> bufs;	// A large buffer, used with filesystem io
+	std::vector<unsigned char*> buf;	// Pointers within the larger buffer, for syscalls
+	unsigned actual;					// The number of buffers submitted for this request
+};
+
+template <class T> class MtpFfsHandleTest;
+
+class MtpFfsHandle : public IMtpHandle {
+	template <class T> friend class MtpFfsHandleTest;
+protected:
+	void closeConfig();
+	void closeEndpoints();
+	void advise(int fd);
+	int handleControlRequest(const struct usb_ctrlrequest *request);
+	int doAsync(void* data, size_t len, bool read, bool zero_packet);
+	int handleEvent();
+	void cancelTransaction();
+	void doSendEvent(mtp_event me);
+	bool openEndpoints(bool ptp);
+
+	static int getPacketSize(int ffs_fd);
+
+	bool mCanceled;
+
+	android::base::unique_fd mControl;
+	// "in" from the host's perspective => sink for mtp server
+	android::base::unique_fd mBulkIn;
+	// "out" from the host's perspective => source for mtp server
+	android::base::unique_fd mBulkOut;
+	android::base::unique_fd mIntr;
+
+	aio_context_t mCtx;
+
+	android::base::unique_fd mEventFd;
+	struct pollfd mPollFds[2];
+
+	struct io_buffer mIobuf[NUM_IO_BUFS];
+
+	// Submit an io request of given length. Return amount submitted or -1.
+	int iobufSubmit(struct io_buffer *buf, int fd, unsigned length, bool read);
+
+	// Cancel submitted requests from start to end in the given array. Return 0 or -1.
+	int cancelEvents(struct iocb **iocb, struct io_event *events, unsigned start, unsigned end);
+
+	// Wait for at minimum the given number of events. Returns the amount of data in the returned
+	// events. Increments counter by the number of events returned.
+	int waitEvents(struct io_buffer *buf, int min_events, struct io_event *events, int *counter);
+
+public:
+	int read(void *data, size_t len) override;
+	int write(const void *data, size_t len) override;
+
+	int receiveFile(mtp_file_range mfr, bool zero_packet) override;
+	int sendFile(mtp_file_range mfr) override;
+	int sendEvent(mtp_event me) override;
+
+	int start(bool ptp) override;
+	void close() override;
+
+	bool writeDescriptors(bool ptp);
+
+	MtpFfsHandle(int controlFd);
+	~MtpFfsHandle();
+};
+
+struct mtp_data_header {
+	/* length of packet, including this header */
+	__le32 length;
+	/* container type (2 for data packet) */
+	__le16 type;
+	/* MTP command code */
+	__le16 command;
+	/* MTP transaction ID */
+	__le32 transaction_id;
+};
+
+#endif // _MTP_FFS_HANDLE_H
+
diff --git a/mtp/MtpMessage.hpp b/mtp/ffs/MtpMessage.hpp
similarity index 100%
rename from mtp/MtpMessage.hpp
rename to mtp/ffs/MtpMessage.hpp
diff --git a/mtp/ffs/MtpObjectInfo.cpp b/mtp/ffs/MtpObjectInfo.cpp
new file mode 100644
index 0000000..3b4d80c
--- /dev/null
+++ b/mtp/ffs/MtpObjectInfo.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "MtpObjectInfo"
+
+#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);
+}
+
+bool MtpObjectInfo::read(MtpDataPacket& packet) {
+	MtpStringBuffer string;
+	time_t time;
+
+	if (!packet.getUInt32(mStorageID)) return false;
+	if (!packet.getUInt16(mFormat)) return false;
+	if (!packet.getUInt16(mProtectionStatus)) return false;
+	if (!packet.getUInt32(mCompressedSize)) return false;
+	if (!packet.getUInt16(mThumbFormat)) return false;
+	if (!packet.getUInt32(mThumbCompressedSize)) return false;
+	if (!packet.getUInt32(mThumbPixWidth)) return false;
+	if (!packet.getUInt32(mThumbPixHeight)) return false;
+	if (!packet.getUInt32(mImagePixWidth)) return false;
+	if (!packet.getUInt32(mImagePixHeight)) return false;
+	if (!packet.getUInt32(mImagePixDepth)) return false;
+	if (!packet.getUInt32(mParent)) return false;
+	if (!packet.getUInt16(mAssociationType)) return false;
+	if (!packet.getUInt32(mAssociationDesc)) return false;
+	if (!packet.getUInt32(mSequenceNumber)) return false;
+
+	if (!packet.getString(string)) return false;
+	mName = strdup((const char *)string);
+	if (!mName) return false;
+
+	if (!packet.getString(string)) return false;
+	if (parseDateTime((const char*)string, time))
+		mDateCreated = time;
+
+	if (!packet.getString(string)) return false;
+	if (parseDateTime((const char*)string, time))
+		mDateModified = time;
+
+	if (!packet.getString(string)) return false;
+	mKeywords = strdup((const char *)string);
+	if (!mKeywords) return false;
+
+	return true;
+}
+
+void MtpObjectInfo::print() {
+	MTPD("MtpObject Info %08X: %s\n", mHandle, mName);
+	MTPD("	mStorageID: %08X mFormat: %04X mProtectionStatus: %d\n",
+			mStorageID, mFormat, mProtectionStatus);
+	MTPD("	mCompressedSize: %d mThumbFormat: %04X mThumbCompressedSize: %d\n",
+			mCompressedSize, mFormat, mThumbCompressedSize);
+	MTPD("	mThumbPixWidth: %d mThumbPixHeight: %d\n", mThumbPixWidth, mThumbPixHeight);
+	MTPD("	mImagePixWidth: %d mImagePixHeight: %d mImagePixDepth: %d\n",
+			mImagePixWidth, mImagePixHeight, mImagePixDepth);
+	MTPD("	mParent: %08X mAssociationType: %04X mAssociationDesc: %04X\n",
+			mParent, mAssociationType, mAssociationDesc);
+	MTPD("	mSequenceNumber: %d mDateCreated: %ld mDateModified: %ld mKeywords: %s\n",
+			mSequenceNumber, mDateCreated, mDateModified, mKeywords);
+}
diff --git a/mtp/ffs/MtpObjectInfo.h b/mtp/ffs/MtpObjectInfo.h
new file mode 100644
index 0000000..74e3719
--- /dev/null
+++ b/mtp/ffs/MtpObjectInfo.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_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:
+	explicit			MtpObjectInfo(MtpObjectHandle handle);
+	virtual				~MtpObjectInfo();
+
+	bool				read(MtpDataPacket& packet);
+
+	void				print();
+};
+
+#endif // _MTP_OBJECT_INFO_H
diff --git a/mtp/ffs/MtpPacket.cpp b/mtp/ffs/MtpPacket.cpp
new file mode 100644
index 0000000..9d6f875
--- /dev/null
+++ b/mtp/ffs/MtpPacket.cpp
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "MtpPacket"
+
+#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(size_t 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++) {
+		bufptr += snprintf(bufptr, sizeof(buffer) - (bufptr - buffer), "%02X ",
+						   mBuffer[i]);
+		if (i % DUMP_BYTES_PER_ROW == (DUMP_BYTES_PER_ROW - 1)) {
+			ALOGV("%s", buffer);
+			bufptr = buffer;
+		}
+	}
+	if (bufptr != buffer) {
+		// print last line
+		ALOGV("%s", buffer);
+	}
+	ALOGV("\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/ffs/MtpPacket.h b/mtp/ffs/MtpPacket.h
new file mode 100644
index 0000000..b757091
--- /dev/null
+++ b/mtp/ffs/MtpPacket.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_PACKET_H
+#define _MTP_PACKET_H
+
+#include <android-base/macros.h>
+
+#include "MtpDebug.h"
+#include "MtpTypes.h"
+
+struct usb_device;
+struct usb_request;
+
+class MtpPacket {
+
+protected:
+	uint8_t*			mBuffer;
+	// current size of the buffer
+	size_t				mBufferSize;
+	// number of bytes to add when resizing the buffer
+	size_t				mAllocationIncrement;
+	// size of the data in the packet
+	size_t				mPacketSize;
+
+public:
+	explicit			MtpPacket(int bufferSize);
+	virtual				~MtpPacket();
+
+	// sets packet size to the default container size and sets buffer to zero
+	virtual void		reset();
+
+	void				allocate(size_t 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);
+
+	DISALLOW_COPY_AND_ASSIGN(MtpPacket);
+};
+
+#endif // _MTP_PACKET_H
diff --git a/mtp/ffs/MtpProperty.cpp b/mtp/ffs/MtpProperty.cpp
new file mode 100644
index 0000000..126cb79
--- /dev/null
+++ b/mtp/ffs/MtpProperty.cpp
@@ -0,0 +1,570 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "MtpProperty"
+
+#include <inttypes.h>
+#include <cutils/compiler.h>
+#include <iomanip>
+#include <sstream>
+#include <string>
+
+#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 (uint32_t i = 0; i < mDefaultArrayLength; i++)
+				free(mDefaultArrayValues[i].str);
+		}
+		if (mCurrentArrayValues) {
+			for (uint32_t i = 0; i < mCurrentArrayLength; i++)
+				free(mCurrentArrayValues[i].str);
+		}
+		if (mEnumValues) {
+			for (uint16_t i = 0; i < mEnumLength; i++)
+				free(mEnumValues[i].str);
+		}
+	}
+	delete[] mDefaultArrayValues;
+	delete[] mCurrentArrayValues;
+	delete[] mEnumValues;
+}
+
+bool MtpProperty::read(MtpDataPacket& packet) {
+	uint8_t temp8;
+
+	if (!packet.getUInt16(mCode)) return false;
+	bool deviceProp = isDeviceProperty();
+	if (!packet.getUInt16(mType)) return false;
+	if (!packet.getUInt8(temp8)) return false;
+	mWriteable = (temp8 == 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 (!mDefaultArrayValues) return false;
+			if (deviceProp) {
+				mCurrentArrayValues = readArrayValues(packet, mCurrentArrayLength);
+				if (!mCurrentArrayValues) return false;
+			}
+			break;
+		default:
+			if (!readValue(packet, mDefaultValue)) return false;
+			if (deviceProp) {
+				if (!readValue(packet, mCurrentValue)) return false;
+			}
+	}
+	if (!deviceProp) {
+		if (!packet.getUInt32(mGroupCode)) return false;
+	}
+	if (!packet.getUInt8(mFormFlag)) return false;
+
+	if (mFormFlag == kFormRange) {
+			if (!readValue(packet, mMinimumValue)) return false;
+			if (!readValue(packet, mMaximumValue)) return false;
+			if (!readValue(packet, mStepSize)) return false;
+	} else if (mFormFlag == kFormEnum) {
+		if (!packet.getUInt16(mEnumLength)) return false;
+		mEnumValues = new MtpPropertyValue[mEnumLength];
+		for (int i = 0; i < mEnumLength; i++) {
+			if (!readValue(packet, mEnumValues[i])) return false;
+		}
+	}
+
+	return true;
+}
+
+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);
+	}
+	if (!deviceProp)
+		packet.putUInt32(mGroupCode);
+	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::setCurrentValue(MtpDataPacket& packet) {
+	free(mCurrentValue.str);
+	mCurrentValue.str = NULL;
+	readValue(packet, mCurrentValue);
+}
+
+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() {
+	std::string buffer;
+	bool deviceProp = isDeviceProperty();
+	if (deviceProp)
+		MTPD("	  %s (%04X)", MtpDebug::getDevicePropCodeName(mCode), mCode);
+	else
+		MTPD("	  %s (%04X)", MtpDebug::getObjectPropCodeName(mCode), mCode);
+	MTPD("	  type %04X", mType);
+	MTPD("	  writeable %s", (mWriteable ? "true" : "false"));
+	buffer = "	  default value: ";
+	print(mDefaultValue, buffer);
+	MTPD("%s", buffer.c_str());
+	if (deviceProp) {
+		buffer = "	  current value: ";
+		print(mCurrentValue, buffer);
+		MTPD("%s", buffer.c_str());
+	}
+	switch (mFormFlag) {
+		case kFormNone:
+			break;
+		case kFormRange:
+			buffer = "	  Range (";
+			print(mMinimumValue, buffer);
+			buffer += ", ";
+			print(mMaximumValue, buffer);
+			buffer += ", ";
+			print(mStepSize, buffer);
+			buffer += ")";
+			MTPD("%s", buffer.c_str());
+			break;
+		case kFormEnum:
+			buffer = "	  Enum { ";
+			for (int i = 0; i < mEnumLength; i++) {
+				print(mEnumValues[i], buffer);
+				buffer += " ";
+			}
+			buffer += "}";
+			MTPD("%s", buffer.c_str());
+			break;
+		case kFormDateTime:
+			MTPD("	  DateTime\n");
+			break;
+		default:
+			MTPD("	  form %d\n", mFormFlag);
+			break;
+	}
+}
+
+void MtpProperty::print(MtpPropertyValue& value, std::string& buffer) {
+	std::ostringstream s;
+	switch (mType) {
+		case MTP_TYPE_INT8:
+			buffer += std::to_string(value.u.i8);
+			break;
+		case MTP_TYPE_UINT8:
+			buffer += std::to_string(value.u.u8);
+			break;
+		case MTP_TYPE_INT16:
+			buffer += std::to_string(value.u.i16);
+			break;
+		case MTP_TYPE_UINT16:
+			buffer += std::to_string(value.u.u16);
+			break;
+		case MTP_TYPE_INT32:
+			buffer += std::to_string(value.u.i32);
+			break;
+		case MTP_TYPE_UINT32:
+			buffer += std::to_string(value.u.u32);
+			break;
+		case MTP_TYPE_INT64:
+			buffer += std::to_string(value.u.i64);
+			break;
+		case MTP_TYPE_UINT64:
+			buffer += std::to_string(value.u.u64);
+			break;
+		case MTP_TYPE_INT128:
+			for (auto i : value.u.i128) {
+				s << std::hex << std::setfill('0') << std::uppercase << i;
+			}
+			buffer += s.str();
+			break;
+		case MTP_TYPE_UINT128:
+			for (auto i : value.u.u128) {
+				s << std::hex << std::setfill('0') << std::uppercase << i;
+			}
+			buffer += s.str();
+			break;
+		case MTP_TYPE_STR:
+			buffer += value.str;
+			break;
+		default:
+			MTPE("unsupported type for MtpProperty::print\n");
+			break;
+	}
+}
+
+bool MtpProperty::readValue(MtpDataPacket& packet, MtpPropertyValue& value) {
+	MtpStringBuffer stringBuffer;
+
+	switch (mType) {
+		case MTP_TYPE_INT8:
+		case MTP_TYPE_AINT8:
+			if (!packet.getInt8(value.u.i8)) return false;
+			break;
+		case MTP_TYPE_UINT8:
+		case MTP_TYPE_AUINT8:
+			if (!packet.getUInt8(value.u.u8)) return false;
+			break;
+		case MTP_TYPE_INT16:
+		case MTP_TYPE_AINT16:
+			if (!packet.getInt16(value.u.i16)) return false;
+			break;
+		case MTP_TYPE_UINT16:
+		case MTP_TYPE_AUINT16:
+			if (!packet.getUInt16(value.u.u16)) return false;
+			break;
+		case MTP_TYPE_INT32:
+		case MTP_TYPE_AINT32:
+			if (!packet.getInt32(value.u.i32)) return false;
+			break;
+		case MTP_TYPE_UINT32:
+		case MTP_TYPE_AUINT32:
+			if (!packet.getUInt32(value.u.u32)) return false;
+			break;
+		case MTP_TYPE_INT64:
+		case MTP_TYPE_AINT64:
+			if (!packet.getInt64(value.u.i64)) return false;
+			break;
+		case MTP_TYPE_UINT64:
+		case MTP_TYPE_AUINT64:
+			if (!packet.getUInt64(value.u.u64)) return false;
+			break;
+		case MTP_TYPE_INT128:
+		case MTP_TYPE_AINT128:
+			if (!packet.getInt128(value.u.i128)) return false;
+			break;
+		case MTP_TYPE_UINT128:
+		case MTP_TYPE_AUINT128:
+			if (!packet.getUInt128(value.u.u128)) return false;
+			break;
+		case MTP_TYPE_STR:
+			if (!packet.getString(stringBuffer)) return false;
+			value.str = strdup(stringBuffer);
+			break;
+		default:
+			MTPE("unknown type %04X in MtpProperty::readValue", mType);
+			return false;
+	}
+	return true;
+}
+
+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, uint32_t& length) {
+	if (!packet.getUInt32(length)) return NULL;
+
+	// Fail if resulting array is over 2GB.  This is because the maximum array
+	// size may be less than SIZE_MAX on some platforms.
+	if ( CC_UNLIKELY(
+			length == 0 ||
+			length >= INT32_MAX / sizeof(MtpPropertyValue)) ) {
+		length = 0;
+		return NULL;
+	}
+	MtpPropertyValue* result = new MtpPropertyValue[length];
+	for (uint32_t i = 0; i < length; i++)
+		if (!readValue(packet, result[i])) {
+			delete [] result;
+			return NULL;
+		}
+	return result;
+}
+
+void MtpProperty::writeArrayValues(MtpDataPacket& packet, MtpPropertyValue* values, uint32_t length) {
+	packet.putUInt32(length);
+	for (uint32_t i = 0; i < length; i++)
+		writeValue(packet, values[i]);
+}
diff --git a/mtp/ffs/MtpProperty.h b/mtp/ffs/MtpProperty.h
new file mode 100644
index 0000000..43ec7c3
--- /dev/null
+++ b/mtp/ffs/MtpProperty.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.
+ */
+
+#ifndef _MTP_PROPERTY_H
+#define _MTP_PROPERTY_H
+
+#include "MtpTypes.h"
+
+#include <string>
+
+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
+	uint32_t			mDefaultArrayLength;
+	MtpPropertyValue*	mDefaultArrayValues;
+	uint32_t			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
+	uint16_t			mEnumLength;
+	MtpPropertyValue*	mEnumValues;
+
+public:
+						MtpProperty();
+						MtpProperty(MtpPropertyCode propCode,
+									 MtpDataType type,
+									 bool writeable = false,
+									 int defaultValue = 0);
+	virtual				~MtpProperty();
+
+	MtpPropertyCode getPropertyCode() const { return mCode; }
+	MtpDataType getDataType() const { return mType; }
+
+	bool				read(MtpDataPacket& packet);
+	void				write(MtpDataPacket& packet);
+
+	void				setDefaultValue(const uint16_t* string);
+	void				setCurrentValue(const uint16_t* string);
+	void				setCurrentValue(MtpDataPacket& packet);
+	const MtpPropertyValue& getCurrentValue() { return mCurrentValue; }
+
+	void				setFormRange(int min, int max, int step);
+	void				setFormEnum(const int* values, int count);
+	void				setFormDateTime();
+
+	void				print();
+
+	inline bool			isDeviceProperty() const {
+							return (   ((mCode & 0xF000) == 0x5000)
+									|| ((mCode & 0xF800) == 0xD000));
+						}
+
+private:
+	bool				readValue(MtpDataPacket& packet, MtpPropertyValue& value);
+	void				writeValue(MtpDataPacket& packet, MtpPropertyValue& value);
+	MtpPropertyValue*	readArrayValues(MtpDataPacket& packet, uint32_t& length);
+	void				writeArrayValues(MtpDataPacket& packet,
+											MtpPropertyValue* values, uint32_t length);
+	void				print(MtpPropertyValue& value, std::string& buffer);
+};
+
+#endif // _MTP_PROPERTY_H
diff --git a/mtp/MtpRequestPacket.cpp b/mtp/ffs/MtpRequestPacket.cpp
similarity index 61%
copy from mtp/MtpRequestPacket.cpp
copy to mtp/ffs/MtpRequestPacket.cpp
index 754e205..8ef1f3c 100644
--- a/mtp/MtpRequestPacket.cpp
+++ b/mtp/ffs/MtpRequestPacket.cpp
@@ -5,29 +5,30 @@
  * 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
+ *		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++
  */
 
+#define LOG_TAG "MtpRequestPacket"
+
 #include <stdio.h>
 #include <sys/types.h>
 #include <fcntl.h>
 
-#include "MtpRequestPacket.h"
+#include "IMtpHandle.h"
 #include "MtpDebug.h"
+#include "MtpRequestPacket.h"
 
 #include <usbhost/usbhost.h>
 
-
 MtpRequestPacket::MtpRequestPacket()
-	:	MtpPacket(512)
+	:	MtpPacket(512),
+		mParameterCount(0)
 {
 }
 
@@ -35,14 +36,24 @@
 }
 
 #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;
+int MtpRequestPacket::read(IMtpHandle *h) {
+	int ret = h->read(mBuffer, mBufferSize);
+	if (ret < 0) {
+		// file read error
+		return ret;
+	}
+
+	// request packet should have 12 byte header followed by 0 to 5 32-bit arguments
+	const size_t read_size = static_cast<size_t>(ret);
+	if (read_size >= MTP_CONTAINER_HEADER_SIZE
+			&& read_size <= MTP_CONTAINER_HEADER_SIZE + 5 * sizeof(uint32_t)
+			&& ((read_size - MTP_CONTAINER_HEADER_SIZE) & 3) == 0) {
+		mPacketSize = read_size;
+		mParameterCount = (read_size - MTP_CONTAINER_HEADER_SIZE) / sizeof(uint32_t);
+	} else {
+		MTPE("Malformed MTP request packet");
+		ret = -1;
+	}
 	return ret;
 }
 #endif
@@ -58,4 +69,3 @@
 	return transfer(request);
 }
 #endif
-
diff --git a/mtp/ffs/MtpRequestPacket.h b/mtp/ffs/MtpRequestPacket.h
new file mode 100644
index 0000000..f05335b
--- /dev/null
+++ b/mtp/ffs/MtpRequestPacket.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_REQUEST_PACKET_H
+#define _MTP_REQUEST_PACKET_H
+
+#include "MtpPacket.h"
+#include "mtp.h"
+
+class IMtpHandle;
+struct usb_request;
+
+class MtpRequestPacket : public MtpPacket {
+
+public:
+						MtpRequestPacket();
+	virtual				~MtpRequestPacket();
+
+#ifdef MTP_DEVICE
+	// fill our buffer with data from the given usb handle
+	int					read(IMtpHandle *h);
+#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); }
+	inline int					getParameterCount() const { return mParameterCount; }
+
+private:
+	int		mParameterCount;
+};
+
+#endif // _MTP_REQUEST_PACKET_H
diff --git a/mtp/ffs/MtpResponsePacket.cpp b/mtp/ffs/MtpResponsePacket.cpp
new file mode 100644
index 0000000..641a4fc
--- /dev/null
+++ b/mtp/ffs/MtpResponsePacket.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "MtpResponsePacket"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+
+#include "IMtpHandle.h"
+#include "MtpResponsePacket.h"
+
+#include <usbhost/usbhost.h>
+
+MtpResponsePacket::MtpResponsePacket()
+	:	MtpPacket(512)
+{
+}
+
+MtpResponsePacket::~MtpResponsePacket() {
+}
+
+#ifdef MTP_DEVICE
+int MtpResponsePacket::write(IMtpHandle *h) {
+	putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+	putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_RESPONSE);
+	int ret = h->write(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/ffs/MtpResponsePacket.h b/mtp/ffs/MtpResponsePacket.h
new file mode 100644
index 0000000..4bde1ca
--- /dev/null
+++ b/mtp/ffs/MtpResponsePacket.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.
+ */
+
+#ifndef _MTP_RESPONSE_PACKET_H
+#define _MTP_RESPONSE_PACKET_H
+
+#include "MtpPacket.h"
+#include "mtp.h"
+
+class IMtpHandle;
+
+class MtpResponsePacket : public MtpPacket {
+
+public:
+						MtpResponsePacket();
+	virtual				~MtpResponsePacket();
+
+#ifdef MTP_DEVICE
+	// write our data to the given usb handle
+	int					write(IMtpHandle *h);
+#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/ffs/MtpServer.cpp b/mtp/ffs/MtpServer.cpp
new file mode 100755
index 0000000..5f17ff2
--- /dev/null
+++ b/mtp/ffs/MtpServer.cpp
@@ -0,0 +1,1459 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <algorithm>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <chrono>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#define LOG_TAG "MtpServer"
+
+#include "MtpDebug.h"
+#include "mtp_MtpDatabase.hpp"
+#include "MtpDescriptors.h"
+#include "MtpDevHandle.h"
+#include "MtpFfsCompatHandle.h"
+#include "MtpFfsHandle.h"
+#include "MtpObjectInfo.h"
+#include "MtpProperty.h"
+#include "MtpServer.h"
+#include "MtpStorage.h"
+#include "MtpStringBuffer.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_DEVICE_PROP_CHANGED,
+};
+
+MtpServer::MtpServer(IMtpDatabase* database, int controlFd, bool ptp,
+					const char *deviceInfoManufacturer,
+					const char *deviceInfoModel,
+					const char *deviceInfoDeviceVersion,
+					const char *deviceInfoSerialNumber)
+	:	mDatabase(database),
+		mPtp(ptp),
+		mDeviceInfoManufacturer(deviceInfoManufacturer),
+		mDeviceInfoModel(deviceInfoModel),
+		mDeviceInfoDeviceVersion(deviceInfoDeviceVersion),
+		mDeviceInfoSerialNumber(deviceInfoSerialNumber),
+		mSessionID(0),
+		mSessionOpen(false),
+		mSendObjectHandle(kInvalidObjectHandle),
+		mSendObjectFormat(0),
+		mSendObjectFileSize(0),
+		mSendObjectModifiedTime(0)
+{
+	bool ffs_ok = access(FFS_MTP_EP0, W_OK) == 0;
+	if (ffs_ok) {
+		bool aio_compat = android::base::GetBoolProperty("sys.usb.ffs.aio_compat", false);
+		mHandle = aio_compat ? new MtpFfsCompatHandle(controlFd) : new MtpFfsHandle(controlFd);
+		mHandle->writeDescriptors(mPtp);
+	} else {
+		mHandle = new MtpDevHandle();
+	}
+}
+
+MtpServer::~MtpServer() {
+}
+
+void MtpServer::addStorage(MtpStorage* storage) {
+	std::lock_guard<std::mutex> lg(mMutex);
+	mDatabase->createDB(storage, storage->getStorageID());
+	mStorages.push_back(storage);
+	sendStoreAdded(storage->getStorageID());
+}
+
+void MtpServer::removeStorage(MtpStorage* storage) {
+	std::lock_guard<std::mutex> lg(mMutex);
+	auto iter = std::find(mStorages.begin(), mStorages.end(), storage);
+	if (iter != mStorages.end()) {
+		sendStoreRemoved(storage->getStorageID());
+		mStorages.erase(iter);
+	}
+}
+
+MtpStorage* MtpServer::getStorage(MtpStorageID id) {
+	if (id == 0)
+		return mStorages[0];
+	for (MtpStorage *storage : mStorages) {
+		if (storage->getStorageID() == id)
+			return storage;
+	}
+	return nullptr;
+}
+
+bool MtpServer::hasStorage(MtpStorageID id) {
+	if (id == 0 || id == 0xFFFFFFFF)
+		return mStorages.size() > 0;
+	return (getStorage(id) != nullptr);
+}
+
+void MtpServer::run() {
+	if (mHandle->start(mPtp)) {
+		MTPE("Failed to start usb driver!");
+		mHandle->close();
+		return;
+	}
+
+	while (1) {
+		int ret = mRequest.read(mHandle);
+		if (ret < 0) {
+			MTPE("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\n", MtpDebug::getOperationCodeName(operation));
+		// 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(mHandle);
+			if (ret < 0) {
+				MTPE("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:");
+		} else {
+			mData.reset();
+		}
+
+		if (handleRequest()) {
+			if (!dataIn && mData.hasData()) {
+				mData.setOperationCode(operation);
+				mData.setTransactionID(transaction);
+				MTPD("sending data:");
+				ret = mData.write(mHandle);
+				if (ret < 0) {
+					MTPE("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", mResponse.getResponseCode());
+			ret = mResponse.write(mHandle);
+			const int savedErrno = errno;
+			if (ret < 0) {
+				MTPE("request write returned %d, errno: %d", ret, errno);
+				if (savedErrno == 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();
+
+	mHandle->close();
+}
+
+void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
+	MTPD("MtpServer::sendObjectAdded %d\n", handle);
+	sendEvent(MTP_EVENT_OBJECT_ADDED, handle);
+}
+
+void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
+	MTPD("MtpServer::sendObjectRemoved %d\n", handle);
+	sendEvent(MTP_EVENT_OBJECT_REMOVED, handle);
+}
+
+void MtpServer::sendStoreRemoved(MtpStorageID id) {
+	MTPD("MtpServer::sendStoreRemoved %08X\n", id);
+	sendEvent(MTP_EVENT_STORE_REMOVED, id);
+}
+
+void MtpServer::sendStoreAdded(MtpStorageID id) {
+	MTPD("MtpServer::sendStoreAdded %08X\n", id);
+	sendEvent(MTP_EVENT_STORE_ADDED, id);
+}
+
+void MtpServer::sendObjectUpdated(MtpObjectHandle handle) {
+	MTPD("MtpServer::sendObjectUpdated %d\n", handle);
+	sendEvent(MTP_EVENT_OBJECT_PROP_CHANGED, handle);
+}
+
+void MtpServer::sendDevicePropertyChanged(MtpDeviceProperty property) {
+	MTPD("MtpServer::sendDevicePropertyChanged %d\n", property);
+	sendEvent(MTP_EVENT_DEVICE_PROP_CHANGED, property);
+}
+
+void MtpServer::sendObjectInfoChanged(MtpObjectHandle handle) {
+	MTPD("MtpServer::sendObjectInfoChanged %d\n", handle);
+	sendEvent(MTP_EVENT_OBJECT_INFO_CHANGED, handle);
+}
+
+void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) {
+	if (mSessionOpen) {
+		mEvent.setEventCode(code);
+		mEvent.setTransactionID(mRequest.getTransactionID());
+		mEvent.setParameter(1, param1);
+		if (mEvent.write(mHandle))
+			MTPE("Mtp send event failed: %s\n", strerror(errno));
+	}
+}
+
+void MtpServer::addEditObject(MtpObjectHandle handle, MtpStringBuffer& path,
+		uint64_t size, MtpObjectFormat format, int fd) {
+	ObjectEdit*  edit = new ObjectEdit(handle, path, size, format, fd);
+	mObjectEditList.push_back(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 nullptr;
+}
+
+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.erase(mObjectEditList.begin() + i);
+			return;
+		}
+	}
+	MTPE("ObjectEdit not found in removeEditObject");
+}
+
+void MtpServer::commitEdit(ObjectEdit* edit) {
+	mDatabase->rescanFile((const char *)edit->mPath, edit->mHandle, edit->mFormat);
+}
+
+
+bool MtpServer::handleRequest() {
+	std::lock_guard<std::mutex> lg(mMutex);
+
+	MtpOperationCode operation = mRequest.getOperationCode();
+	MtpResponseCode response;
+
+	mResponse.reset();
+
+	if (mSendObjectHandle != kInvalidObjectHandle && operation != MTP_OPERATION_SEND_OBJECT) {
+		mSendObjectHandle = kInvalidObjectHandle;
+		mSendObjectFormat = 0;
+		mSendObjectModifiedTime = 0;
+	}
+
+	int containertype = mRequest.getContainerType();
+	if (containertype != MTP_CONTAINER_TYPE_COMMAND) {
+		MTPE("wrong container type %d", containertype);
+		return false;
+	}
+
+	MTPD("got command %s (%x)\n", MtpDebug::getOperationCodeName(operation), operation);
+
+	switch (operation) {
+		case MTP_OPERATION_GET_DEVICE_INFO:
+			response = doGetDeviceInfo();
+			break;
+		case MTP_OPERATION_OPEN_SESSION:
+			response = doOpenSession();
+			break;
+		case MTP_OPERATION_RESET_DEVICE:
+		case MTP_OPERATION_CLOSE_SESSION:
+			response = doCloseSession();
+			break;
+		case MTP_OPERATION_GET_STORAGE_IDS:
+			response = doGetStorageIDs();
+			break;
+		 case MTP_OPERATION_GET_STORAGE_INFO:
+			response = doGetStorageInfo();
+			break;
+		case MTP_OPERATION_GET_OBJECT_PROPS_SUPPORTED:
+			response = doGetObjectPropsSupported();
+			break;
+		case MTP_OPERATION_GET_OBJECT_HANDLES:
+			response = doGetObjectHandles();
+			break;
+		case MTP_OPERATION_GET_NUM_OBJECTS:
+			response = doGetNumObjects();
+			break;
+		case MTP_OPERATION_GET_OBJECT_REFERENCES:
+			response = doGetObjectReferences();
+			break;
+		case MTP_OPERATION_SET_OBJECT_REFERENCES:
+			response = doSetObjectReferences();
+			break;
+		case MTP_OPERATION_GET_OBJECT_PROP_VALUE:
+			response = doGetObjectPropValue();
+			break;
+		case MTP_OPERATION_SET_OBJECT_PROP_VALUE:
+			response = doSetObjectPropValue();
+			break;
+		case MTP_OPERATION_GET_DEVICE_PROP_VALUE:
+			response = doGetDevicePropValue();
+			break;
+		case MTP_OPERATION_SET_DEVICE_PROP_VALUE:
+			response = doSetDevicePropValue();
+			break;
+		case MTP_OPERATION_RESET_DEVICE_PROP_VALUE:
+			response = doResetDevicePropValue();
+			break;
+		case MTP_OPERATION_GET_OBJECT_PROP_LIST:
+			response = doGetObjectPropList();
+			break;
+		case MTP_OPERATION_GET_OBJECT_INFO:
+			response = doGetObjectInfo();
+			break;
+		case MTP_OPERATION_GET_OBJECT:
+			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:
+			response = doSendObjectInfo();
+			break;
+		case MTP_OPERATION_SEND_OBJECT:
+			response = doSendObject();
+			break;
+		case MTP_OPERATION_DELETE_OBJECT:
+			response = doDeleteObject();
+			break;
+		case MTP_OPERATION_COPY_OBJECT:
+			response = doCopyObject();
+			break;
+		case MTP_OPERATION_MOVE_OBJECT:
+			response = doMoveObject();
+			break;
+		case MTP_OPERATION_GET_OBJECT_PROP_DESC:
+			response = doGetObjectPropDesc();
+			break;
+		case MTP_OPERATION_GET_DEVICE_PROP_DESC:
+			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 (%x)",
+					MtpDebug::getOperationCodeName(operation), 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;
+
+	MtpObjectFormatList* playbackFormats = mDatabase->getSupportedPlaybackFormats();
+	MtpObjectFormatList* captureFormats = mDatabase->getSupportedCaptureFormats();
+	MtpDevicePropertyList* deviceProperties = mDatabase->getSupportedDeviceProperties();
+
+	// fill in device info
+	mData.putUInt16(MTP_STANDARD_VERSION);
+	if (mPtp) {
+		mData.putUInt32(0);
+	} else {
+		// MTP Vendor Extension ID
+		mData.putUInt32(6);
+	}
+	mData.putUInt16(MTP_STANDARD_VERSION);
+	if (mPtp) {
+		// no extensions
+		string.set("");
+	} else {
+		// MTP extensions
+		string.set("microsoft.com: 1.0; android.com: 1.0;");
+	}
+	mData.putString(string); // MTP Extensions
+	mData.putUInt16(0); //Functional Mode
+	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
+
+	mData.putString(mDeviceInfoManufacturer); // Manufacturer
+	mData.putString(mDeviceInfoModel); // Model
+	mData.putString(mDeviceInfoDeviceVersion); // Device Version
+	mData.putString(mDeviceInfoSerialNumber); // 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;
+	}
+	if (mRequest.getParameterCount() < 1)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+
+	mSessionID = mRequest.getParameter(1);
+	mSessionOpen = true;
+
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doCloseSession() {
+	if (!mSessionOpen)
+		return MTP_RESPONSE_SESSION_NOT_OPEN;
+	mSessionID = 0;
+	mSessionOpen = false;
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetStorageIDs() {
+	if (!mSessionOpen)
+		return MTP_RESPONSE_SESSION_NOT_OPEN;
+
+	int count = mStorages.size();
+	mData.putUInt32(count);
+	for (int i = 0; i < count; i++)
+		mData.putUInt32(mStorages[i]->getStorageID());
+
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetStorageInfo() {
+	MtpStringBuffer   string;
+
+	if (!mSessionOpen)
+		return MTP_RESPONSE_SESSION_NOT_OPEN;
+	if (mRequest.getParameterCount() < 1)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+
+	MtpStorageID id = mRequest.getParameter(1);
+	MtpStorage* storage = getStorage(id);
+	if (!storage)
+		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() {
+	if (!mSessionOpen)
+		return MTP_RESPONSE_SESSION_NOT_OPEN;
+	if (mRequest.getParameterCount() < 1)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	MtpObjectFormat format = mRequest.getParameter(1);
+	MtpObjectPropertyList* properties = mDatabase->getSupportedObjectProperties(format);
+	mData.putAUInt16(properties);
+	delete properties;
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetObjectHandles() {
+	if (!mSessionOpen)
+		return MTP_RESPONSE_SESSION_NOT_OPEN;
+	if (mRequest.getParameterCount() < 3)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	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;
+
+	MtpObjectHandleList* handles = mDatabase->getObjectList(storageID, format, parent);
+	if (handles == NULL)
+		return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+	mData.putAUInt32(handles);
+	delete handles;
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetNumObjects() {
+	if (!mSessionOpen)
+		return MTP_RESPONSE_SESSION_NOT_OPEN;
+	if (mRequest.getParameterCount() < 3)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	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;
+
+	int count = mDatabase->getNumObjects(storageID, format, parent);
+	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;
+	if (mRequest.getParameterCount() < 1)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	MtpObjectHandle handle = mRequest.getParameter(1);
+
+	// FIXME - check for invalid object handle
+	MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
+	if (handles) {
+		mData.putAUInt32(handles);
+		delete handles;
+	} else {
+		mData.putEmptyArray();
+	}
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doSetObjectReferences() {
+	if (!mSessionOpen)
+		return MTP_RESPONSE_SESSION_NOT_OPEN;
+	if (!hasStorage())
+		return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+	if (mRequest.getParameterCount() < 1)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	MtpStorageID handle = mRequest.getParameter(1);
+
+	MtpObjectHandleList* references = mData.getAUInt32();
+	if (!references)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
+	delete references;
+	return result;
+}
+
+MtpResponseCode MtpServer::doGetObjectPropValue() {
+	if (!hasStorage())
+		return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+	if (mRequest.getParameterCount() < 2)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	MtpObjectHandle handle = mRequest.getParameter(1);
+	MtpObjectProperty property = mRequest.getParameter(2);
+	MTPD("GetObjectPropValue %d %s\n", handle,
+			MtpDebug::getObjectPropCodeName(property));
+
+	return mDatabase->getObjectPropertyValue(handle, property, mData);
+}
+
+MtpResponseCode MtpServer::doSetObjectPropValue() {
+	if (!hasStorage())
+		return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+	if (mRequest.getParameterCount() < 2)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	MtpObjectHandle handle = mRequest.getParameter(1);
+	MtpObjectProperty property = mRequest.getParameter(2);
+	MTPD("SetObjectPropValue %d %s\n", handle,
+			MtpDebug::getObjectPropCodeName(property));
+
+	return mDatabase->setObjectPropertyValue(handle, property, mData);
+}
+
+MtpResponseCode MtpServer::doGetDevicePropValue() {
+	if (mRequest.getParameterCount() < 1)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	MtpDeviceProperty property = mRequest.getParameter(1);
+	MTPD("GetDevicePropValue %s\n",
+			MtpDebug::getDevicePropCodeName(property));
+
+	return mDatabase->getDevicePropertyValue(property, mData);
+}
+
+MtpResponseCode MtpServer::doSetDevicePropValue() {
+	if (mRequest.getParameterCount() < 1)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	MtpDeviceProperty property = mRequest.getParameter(1);
+	MTPD("SetDevicePropValue %s\n",
+			MtpDebug::getDevicePropCodeName(property));
+
+	return mDatabase->setDevicePropertyValue(property, mData);
+}
+
+MtpResponseCode MtpServer::doResetDevicePropValue() {
+	if (mRequest.getParameterCount() < 1)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	MtpDeviceProperty property = mRequest.getParameter(1);
+	MTPD("ResetDevicePropValue %s\n",
+			MtpDebug::getDevicePropCodeName(property));
+
+	return mDatabase->resetDeviceProperty(property);
+}
+
+MtpResponseCode MtpServer::doGetObjectPropList() {
+	if (!hasStorage())
+		return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+	if (mRequest.getParameterCount() < 5)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+
+	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: %s group: %d depth: %d\n",
+			handle, MtpDebug::getFormatCodeName(format),
+			MtpDebug::getObjectPropCodeName(property), groupCode, depth);
+
+	return mDatabase->getObjectPropertyList(handle, format, property, groupCode, depth, mData);
+}
+
+MtpResponseCode MtpServer::doGetObjectInfo() {
+	if (!hasStorage())
+		return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+	if (mRequest.getParameterCount() < 1)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	MtpObjectHandle handle = mRequest.getParameter(1);
+	MtpObjectInfo info(handle);
+	MtpResponseCode result = mDatabase->getObjectInfo(handle, info);
+	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);
+		mData.putString(info.mName);
+		formatDateTime(info.mDateCreated, date, sizeof(date));
+		mData.putString(date);	 // 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;
+	if (mRequest.getParameterCount() < 1)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	MtpObjectHandle handle = mRequest.getParameter(1);
+	MtpStringBuffer pathBuf;
+	int64_t fileLength;
+	MtpObjectFormat format;
+	int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
+	if (result != MTP_RESPONSE_OK)
+		return result;
+
+	auto start = std::chrono::steady_clock::now();
+
+	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 = 0;
+	mfr.length = fileLength;
+	mfr.command = mRequest.getOperationCode();
+	mfr.transaction_id = mRequest.getTransactionID();
+
+	// then transfer the file
+	int ret = mHandle->sendFile(mfr);
+	if (ret < 0) {
+		MTPE("Mtp send file got error %s", strerror(errno));
+		if (errno == ECANCELED) {
+			result = MTP_RESPONSE_TRANSACTION_CANCELLED;
+		} else {
+			result = MTP_RESPONSE_GENERAL_ERROR;
+		}
+	} else {
+		result = MTP_RESPONSE_OK;
+	}
+
+	auto end = std::chrono::steady_clock::now();
+	std::chrono::duration<double> diff = end - start;
+	struct stat sstat;
+	fstat(mfr.fd, &sstat);
+	uint64_t finalsize = sstat.st_size;
+	MTPD("Sent a file over MTP. Time: %f s, Size: %" PRIu64 ", Rate: %f bytes/s",
+			diff.count(), finalsize, ((double) finalsize) / diff.count());
+	closeObjFd(mfr.fd, filePath);
+	return result;
+}
+
+MtpResponseCode MtpServer::doGetThumb() {
+	if (mRequest.getParameterCount() < 1)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	MtpObjectHandle handle = mRequest.getParameter(1);
+	size_t thumbSize;
+	void* thumb = mDatabase->getThumbnail(handle, thumbSize);
+	if (thumb) {
+		// send data
+		mData.setOperationCode(mRequest.getOperationCode());
+		mData.setTransactionID(mRequest.getTransactionID());
+		mData.writeData(mHandle, 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) {
+		// MTP_OPERATION_GET_PARTIAL_OBJECT_64 takes 4 arguments
+		if (mRequest.getParameterCount() < 4)
+			return MTP_RESPONSE_INVALID_PARAMETER;
+
+		// android extension with 64 bit offset
+		uint64_t offset2 = mRequest.getParameter(3);
+		offset = offset | (offset2 << 32);
+		length = mRequest.getParameter(4);
+	} else {
+		// MTP_OPERATION_GET_PARTIAL_OBJECT takes 3 arguments
+		if (mRequest.getParameterCount() < 3)
+			return MTP_RESPONSE_INVALID_PARAMETER;
+
+		// standard GetPartialObject
+		length = mRequest.getParameter(3);
+	}
+	MtpStringBuffer pathBuf;
+	int64_t fileLength;
+	MtpObjectFormat format;
+	int result = mDatabase->getObjectFilePath(handle, pathBuf, fileLength, format);
+	if (result != MTP_RESPONSE_OK)
+		return result;
+	if (offset + length > (uint64_t)fileLength)
+		length = fileLength - offset;
+
+	const char* filePath = (const char *)pathBuf;
+	MTPD("sending partial %s\n %" PRIu64 " %" PRIu32, filePath, offset, length);
+	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 = mHandle->sendFile(mfr);
+	MTPD("MTP_SEND_FILE_WITH_HEADER returned %d\n", ret);
+	result = MTP_RESPONSE_OK;
+	if (ret < 0) {
+		if (errno == ECANCELED)
+			result = MTP_RESPONSE_TRANSACTION_CANCELLED;
+		else
+			result = MTP_RESPONSE_GENERAL_ERROR;
+	}
+	closeObjFd(mfr.fd, filePath);
+	return result;
+}
+
+MtpResponseCode MtpServer::doSendObjectInfo() {
+	MtpStringBuffer path;
+	uint16_t temp16;
+	uint32_t temp32;
+
+	if (mRequest.getParameterCount() < 2)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	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) {
+		path.set(storage->getPath());
+		parent = 0;
+	} else {
+		int64_t length;
+		MtpObjectFormat format;
+		int result = mDatabase->getObjectFilePath(parent, path, length, format);
+		if (result != MTP_RESPONSE_OK)
+			return result;
+		if (format != MTP_FORMAT_ASSOCIATION)
+			return MTP_RESPONSE_INVALID_PARENT_OBJECT;
+	}
+
+	// read only the fields we need
+	if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // storage ID
+	if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER;
+	MtpObjectFormat format = temp16;
+	if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER;  // protection status
+	if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;
+	mSendObjectFileSize = temp32;
+	if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER;  // thumb format
+	if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // thumb compressed size
+	if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // thumb pix width
+	if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // thumb pix height
+	if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // image pix width
+	if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // image pix height
+	if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // image bit depth
+	if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // parent
+	if (!mData.getUInt16(temp16)) return MTP_RESPONSE_INVALID_PARAMETER;
+	if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;
+	if (!mData.getUInt32(temp32)) return MTP_RESPONSE_INVALID_PARAMETER;  // sequence number
+	MtpStringBuffer name, created, modified;
+	if (!mData.getString(name)) return MTP_RESPONSE_INVALID_PARAMETER;	  // file name
+	if (name.isEmpty()) {
+		MTPE("empty name");
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	}
+	if (!mData.getString(created)) return MTP_RESPONSE_INVALID_PARAMETER;	   // date created
+	if (!mData.getString(modified)) return MTP_RESPONSE_INVALID_PARAMETER;	   // 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.append("/");
+	path.append(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("path: %s parent: %d storageID: %08X", (const char*)path, parent, storageID);
+	uint64_t size = 0; // TODO: this needs to be implemented
+	time_t modified_time = 0; // TODO: this needs to be implemented
+	MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path, format,
+			parent, storageID, size, modified_time);
+	if (handle == kInvalidObjectHandle) {
+		return MTP_RESPONSE_GENERAL_ERROR;
+	}
+
+	if (format == MTP_FORMAT_ASSOCIATION) {
+		int ret = makeFolder((const char *)path);
+		if (ret)
+			return MTP_RESPONSE_GENERAL_ERROR;
+
+		// SendObject does not get sent for directories, so call endSendObject here instead
+		mDatabase->endSendObject((const char*)path, handle, format, MTP_RESPONSE_OK);
+	}
+	mSendObjectFilePath = path;
+	// save the handle for the SendObject call, which should follow
+	mSendObjectHandle = handle;
+	mSendObjectFormat = format;
+	mSendObjectModifiedTime = modifiedTime;
+
+	mResponse.setParameter(1, storageID);
+	mResponse.setParameter(2, parent);
+	mResponse.setParameter(3, handle);
+
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doMoveObject() {
+	if (!hasStorage())
+		return MTP_RESPONSE_GENERAL_ERROR;
+	if (mRequest.getParameterCount() < 3)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	MtpObjectHandle objectHandle = mRequest.getParameter(1);
+	MtpStorageID storageID = mRequest.getParameter(2);
+	MtpStorage* storage = getStorage(storageID);
+	MtpObjectHandle parent = mRequest.getParameter(3);
+	if (!storage)
+		return MTP_RESPONSE_INVALID_STORAGE_ID;
+	MtpStringBuffer path;
+	MtpResponseCode result;
+
+	MtpStringBuffer fromPath;
+	int64_t fileLength;
+	MtpObjectFormat format;
+	MtpObjectInfo info(objectHandle);
+	result = mDatabase->getObjectInfo(objectHandle, info);
+	if (result != MTP_RESPONSE_OK)
+		return result;
+	result = mDatabase->getObjectFilePath(objectHandle, fromPath, fileLength, format);
+	if (result != MTP_RESPONSE_OK)
+		return result;
+
+	// special case the root
+	if (parent == 0) {
+		path.set(storage->getPath());
+	} else {
+		int64_t parentLength;
+		MtpObjectFormat parentFormat;
+		result = mDatabase->getObjectFilePath(parent, path, parentLength, parentFormat);
+		if (result != MTP_RESPONSE_OK)
+			return result;
+		if (parentFormat != MTP_FORMAT_ASSOCIATION)
+			return MTP_RESPONSE_INVALID_PARENT_OBJECT;
+	}
+
+	if (path[path.size() - 1] != '/')
+		path.append("/");
+	path.append(info.mName);
+
+	result = mDatabase->beginMoveObject(objectHandle, parent, storageID);
+	if (result != MTP_RESPONSE_OK)
+		return result;
+
+	if (info.mStorageID == storageID) {
+		MTPD("Moving file from %s to %s", (const char*)fromPath, (const char*)path);
+		if (renameTo(fromPath, path)) {
+			PLOG(ERROR) << "rename() failed from " << fromPath << " to " << path;
+			result = MTP_RESPONSE_GENERAL_ERROR;
+		}
+	} else {
+		MTPD("Moving across storages from %s to %s", (const char*)fromPath, (const char*)path);
+		if (format == MTP_FORMAT_ASSOCIATION) {
+			int ret = makeFolder((const char *)path);
+			ret += copyRecursive(fromPath, path);
+			if (ret) {
+				result = MTP_RESPONSE_GENERAL_ERROR;
+			} else {
+				deletePath(fromPath);
+			}
+		} else {
+			if (copyFile(fromPath, path)) {
+				result = MTP_RESPONSE_GENERAL_ERROR;
+			} else {
+				deletePath(fromPath);
+			}
+		}
+	}
+
+	// If the move failed, undo the database change
+	mDatabase->endMoveObject(info.mParent, parent, info.mStorageID, storageID, objectHandle,
+			result == MTP_RESPONSE_OK);
+
+	return result;
+}
+
+MtpResponseCode MtpServer::doCopyObject() {
+	if (!hasStorage())
+		return MTP_RESPONSE_GENERAL_ERROR;
+	MtpResponseCode result = MTP_RESPONSE_OK;
+	if (mRequest.getParameterCount() < 3)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	MtpObjectHandle objectHandle = mRequest.getParameter(1);
+	MtpStorageID storageID = mRequest.getParameter(2);
+	MtpStorage* storage = getStorage(storageID);
+	MtpObjectHandle parent = mRequest.getParameter(3);
+	if (!storage)
+		return MTP_RESPONSE_INVALID_STORAGE_ID;
+	MtpStringBuffer path;
+
+	MtpStringBuffer fromPath;
+	int64_t fileLength;
+	MtpObjectFormat format;
+	MtpObjectInfo info(objectHandle);
+	result = mDatabase->getObjectInfo(objectHandle, info);
+	if (result != MTP_RESPONSE_OK)
+		return result;
+	result = mDatabase->getObjectFilePath(objectHandle, fromPath, fileLength, format);
+	if (result != MTP_RESPONSE_OK)
+		return result;
+
+	// special case the root
+	if (parent == 0) {
+		path.set(storage->getPath());
+	} else {
+		int64_t parentLength;
+		MtpObjectFormat parentFormat;
+		result = mDatabase->getObjectFilePath(parent, path, parentLength, parentFormat);
+		if (result != MTP_RESPONSE_OK)
+			return result;
+		if (parentFormat != MTP_FORMAT_ASSOCIATION)
+			return MTP_RESPONSE_INVALID_PARENT_OBJECT;
+	}
+
+	// check space first
+	if ((uint64_t) fileLength > storage->getFreeSpace())
+		return MTP_RESPONSE_STORAGE_FULL;
+
+	if (path[path.size() - 1] != '/')
+		path.append("/");
+	path.append(info.mName);
+
+	MtpObjectHandle handle = mDatabase->beginCopyObject(objectHandle, parent, storageID);
+	if (handle == kInvalidObjectHandle) {
+		return MTP_RESPONSE_GENERAL_ERROR;
+	}
+
+	MTPD("Copying file from %s to %s", (const char*)fromPath, (const char*)path);
+	if (format == MTP_FORMAT_ASSOCIATION) {
+		int ret = makeFolder((const char *)path);
+		ret += copyRecursive(fromPath, path);
+		if (ret) {
+			result = MTP_RESPONSE_GENERAL_ERROR;
+		}
+	} else {
+		if (copyFile(fromPath, path)) {
+			result = MTP_RESPONSE_GENERAL_ERROR;
+		}
+	}
+
+	mDatabase->endCopyObject(handle, result);
+	mResponse.setParameter(1, handle);
+	return result;
+}
+
+MtpResponseCode MtpServer::doSendObject() {
+	if (!hasStorage())
+		return MTP_RESPONSE_GENERAL_ERROR;
+	MtpResponseCode result = MTP_RESPONSE_OK;
+	mode_t mask;
+	int ret, initialData;
+	bool isCanceled = false;
+	struct stat sstat = {};
+
+	auto start = std::chrono::steady_clock::now();
+
+	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(mHandle);
+	if (ret < MTP_CONTAINER_HEADER_SIZE) {
+		result = MTP_RESPONSE_GENERAL_ERROR;
+		goto done;
+	}
+	initialData = ret - MTP_CONTAINER_HEADER_SIZE;
+
+	if (mSendObjectFormat == MTP_FORMAT_ASSOCIATION) {
+		if (initialData != 0)
+			MTPE("Expected folder size to be 0!");
+		mSendObjectHandle = kInvalidObjectHandle;
+		mSendObjectFormat = 0;
+		mSendObjectModifiedTime = 0;
+		return result;
+	}
+
+	mtp_file_range	mfr;
+	mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
+	if (mfr.fd < 0) {
+		result = MTP_RESPONSE_GENERAL_ERROR;
+		goto done;
+	}
+	fchown(mfr.fd, getuid(), FILE_GROUP);
+	// set permissions
+	mask = umask(0);
+	fchmod(mfr.fd, FILE_PERM);
+	umask(mask);
+
+	if (initialData > 0) {
+		ret = write(mfr.fd, mData.getData(), initialData);
+	}
+
+	if (ret < 0) {
+		MTPE("failed to write initial data");
+		result = MTP_RESPONSE_GENERAL_ERROR;
+	} else {
+		mfr.offset = initialData;
+		if (mSendObjectFileSize == 0xFFFFFFFF) {
+			// tell driver to read until it receives a short packet
+			mfr.length = 0xFFFFFFFF;
+		} else {
+			mfr.length = mSendObjectFileSize - initialData;
+		}
+
+		mfr.command = 0;
+		mfr.transaction_id = 0;
+
+		// transfer the file
+		ret = mHandle->receiveFile(mfr, mfr.length == 0 &&
+				initialData == MTP_BUFFER_SIZE - MTP_CONTAINER_HEADER_SIZE);
+		if ((ret < 0) && (errno == ECANCELED)) {
+			isCanceled = true;
+		}
+	}
+
+	if (mSendObjectModifiedTime) {
+		struct timespec newTime[2];
+		newTime[0].tv_nsec = UTIME_NOW;
+		newTime[1].tv_sec = mSendObjectModifiedTime;
+		newTime[1].tv_nsec = 0;
+		if (futimens(mfr.fd, newTime) < 0) {
+			MTPE("changing modified time failed, %s", strerror(errno));
+		}
+	}
+
+	fstat(mfr.fd, &sstat);
+	closeObjFd(mfr.fd, mSendObjectFilePath);
+
+	if (ret < 0) {
+		MTPE("Mtp receive file got error %s", strerror(errno));
+		unlink(mSendObjectFilePath);
+		if (isCanceled)
+			result = MTP_RESPONSE_TRANSACTION_CANCELLED;
+		else
+			result = MTP_RESPONSE_GENERAL_ERROR;
+	}
+
+done:
+	// reset so we don't attempt to send the data back
+	mData.reset();
+
+	mDatabase->endSendObject(mSendObjectFilePath, mSendObjectHandle, mSendObjectFormat, result == MTP_RESPONSE_OK);
+	mSendObjectHandle = kInvalidObjectHandle;
+	mSendObjectFormat = 0;
+	mSendObjectModifiedTime = 0;
+
+	auto end = std::chrono::steady_clock::now();
+	std::chrono::duration<double> diff = end - start;
+	uint64_t finalsize = sstat.st_size;
+	MTPD("Got a file over MTP. Time: %fs, Size: %" PRIu64 ", Rate: %f bytes/s",
+			diff.count(), finalsize, ((double) finalsize) / diff.count());
+	return result;
+}
+
+MtpResponseCode MtpServer::doDeleteObject() {
+	MTPD("In MtpServer::doDeleteObject\n");
+	if (!hasStorage())
+		return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+	if (mRequest.getParameterCount() < 1)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	MtpObjectHandle handle = mRequest.getParameter(1);
+	MtpObjectFormat format;
+	// FIXME - support deleting all objects if handle is 0xFFFFFFFF
+	// FIXME - implement deleting objects by format
+
+	MtpStringBuffer filePath;
+	int64_t fileLength;
+	int result = mDatabase->getObjectFilePath(handle, filePath, fileLength, format);
+	if (result != MTP_RESPONSE_OK)
+		return result;
+
+	// Don't delete the actual files unless the database deletion is allowed
+	result = mDatabase->beginDeleteObject(handle);
+	if (result != MTP_RESPONSE_OK)
+		return result;
+
+	bool success = deletePath((const char *)filePath);
+
+	mDatabase->endDeleteObject(handle, success);
+	return success ? result : MTP_RESPONSE_PARTIAL_DELETION;
+}
+
+MtpResponseCode MtpServer::doGetObjectPropDesc() {
+	if (mRequest.getParameterCount() < 2)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	MtpObjectProperty propCode = mRequest.getParameter(1);
+	MtpObjectFormat format = mRequest.getParameter(2);
+	MTPD("GetObjectPropDesc %s %s\n", MtpDebug::getObjectPropCodeName(propCode),
+										MtpDebug::getFormatCodeName(format));
+	MtpProperty* property = mDatabase->getObjectPropertyDesc(propCode, format);
+	if (!property)
+		return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+	property->write(mData);
+	delete property;
+	return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode MtpServer::doGetDevicePropDesc() {
+	if (mRequest.getParameterCount() < 1)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	MtpDeviceProperty propCode = mRequest.getParameter(1);
+	MTPD("GetDevicePropDesc %s\n", MtpDebug::getDevicePropCodeName(propCode));
+	MtpProperty* property = mDatabase->getDevicePropertyDesc(propCode);
+	if (!property)
+		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;
+	if (mRequest.getParameterCount() < 4)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	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) {
+		MTPD("writing past end of object, offset: %" PRIu64 ", edit->mSize: %" PRIu64,
+			offset, edit->mSize);
+		return MTP_RESPONSE_GENERAL_ERROR;
+	}
+
+	const char* filePath = (const char *)edit->mPath;
+	MTPD("receiving partial %s %" PRIu64 " %" PRIu32, filePath, offset, length);
+
+	// read the header, and possibly some data
+	int ret = mData.read(mHandle);
+	if (ret < MTP_CONTAINER_HEADER_SIZE)
+		return MTP_RESPONSE_GENERAL_ERROR;
+	int initialData = ret - MTP_CONTAINER_HEADER_SIZE;
+
+	if (initialData > 0) {
+		ret = pwrite(edit->mFD, mData.getData(), initialData, offset);
+		offset += initialData;
+		length -= initialData;
+	}
+
+	bool isCanceled = false;
+	if (ret < 0) {
+		MTPE("failed to write initial data");
+	} else {
+		mtp_file_range	mfr;
+		mfr.fd = edit->mFD;
+		mfr.offset = offset;
+		mfr.length = length;
+		mfr.command = 0;
+		mfr.transaction_id = 0;
+
+		// transfer the file
+		ret = mHandle->receiveFile(mfr, mfr.length == 0 &&
+				initialData == MTP_BUFFER_SIZE - MTP_CONTAINER_HEADER_SIZE);
+		if ((ret < 0) && (errno == ECANCELED)) {
+			isCanceled = true;
+		}
+	}
+	if (ret < 0) {
+		mResponse.setParameter(1, 0);
+		if (isCanceled)
+			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() {
+	if (mRequest.getParameterCount() < 3)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	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() {
+	if (mRequest.getParameterCount() < 1)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	MtpObjectHandle handle = mRequest.getParameter(1);
+	if (getEditObject(handle)) {
+		MTPE("object already open for edit in doBeginEditObject");
+		return MTP_RESPONSE_GENERAL_ERROR;
+	}
+
+	MtpStringBuffer path;
+	int64_t fileLength;
+	MtpObjectFormat format;
+	int result = mDatabase->getObjectFilePath(handle, path, fileLength, format);
+	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() {
+	if (mRequest.getParameterCount() < 1)
+		return MTP_RESPONSE_INVALID_PARAMETER;
+	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/ffs/MtpServer.h b/mtp/ffs/MtpServer.h
new file mode 100755
index 0000000..4bc07cd
--- /dev/null
+++ b/mtp/ffs/MtpServer.h
@@ -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.
+ */
+
+#ifndef _MTP_SERVER_H
+#define _MTP_SERVER_H
+
+#include "MtpRequestPacket.h"
+#include "MtpDataPacket.h"
+#include "MtpResponsePacket.h"
+#include "MtpEventPacket.h"
+#include "MtpStringBuffer.h"
+#include "mtp.h"
+#include "MtpUtils.h"
+#include "IMtpHandle.h"
+
+#include <memory>
+#include <mutex>
+#include <queue>
+
+class IMtpDatabase;
+class MtpStorage;
+
+class MtpServer {
+
+private:
+	IMtpDatabase*		mDatabase;
+
+	// appear as a PTP device
+	bool				mPtp;
+
+	// Manufacturer to report in DeviceInfo
+	MtpStringBuffer		mDeviceInfoManufacturer;
+	// Model to report in DeviceInfo
+	MtpStringBuffer		mDeviceInfoModel;
+	// Device version to report in DeviceInfo
+	MtpStringBuffer		mDeviceInfoDeviceVersion;
+	// Serial number to report in DeviceInfo
+	MtpStringBuffer		mDeviceInfoSerialNumber;
+
+	// 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;
+
+	IMtpHandle*			mHandle;
+
+	// handle for new object, set by SendObjectInfo and used by SendObject
+	MtpObjectHandle		mSendObjectHandle;
+	MtpObjectFormat		mSendObjectFormat;
+	MtpStringBuffer		mSendObjectFilePath;
+	size_t				mSendObjectFileSize;
+	time_t				mSendObjectModifiedTime;
+
+	std::mutex			mMutex;
+
+	// 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;
+		MtpStringBuffer			  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);
+		}
+	};
+	std::vector<ObjectEdit*>  mObjectEditList;
+
+public:
+						MtpServer(IMtpDatabase* database, int controlFd, bool ptp,
+									const char *deviceInfoManufacturer,
+									const char *deviceInfoModel,
+									const char *deviceInfoDeviceVersion,
+									const char *deviceInfoSerialNumber);
+	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);
+	void				sendDevicePropertyChanged(MtpDeviceProperty property);
+	void				sendObjectInfoChanged(MtpObjectHandle handle);
+
+
+private:
+	void				sendStoreAdded(MtpStorageID id);
+	void				sendStoreRemoved(MtpStorageID id);
+	void				sendEvent(MtpEventCode code, uint32_t param1);
+
+	void				addEditObject(MtpObjectHandle handle, MtpStringBuffer& 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		doMoveObject();
+	MtpResponseCode		doCopyObject();
+	MtpResponseCode		doGetObjectPropDesc();
+	MtpResponseCode		doGetDevicePropDesc();
+	MtpResponseCode		doSendPartialObject();
+	MtpResponseCode		doTruncateObject();
+	MtpResponseCode		doBeginEditObject();
+	MtpResponseCode		doEndEditObject();
+};
+
+#endif // _MTP_SERVER_H
diff --git a/mtp/ffs/MtpStorage.cpp b/mtp/ffs/MtpStorage.cpp
new file mode 100755
index 0000000..8c67b5b
--- /dev/null
+++ b/mtp/ffs/MtpStorage.cpp
@@ -0,0 +1,810 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "MtpStorage"
+
+#include "MtpDebug.h"
+#include "MtpStorage.h"
+#include "btree.hpp"
+
+#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 <iterator>
+#include <sys/inotify.h>
+
+#define WATCH_FLAGS ( IN_CREATE | IN_DELETE | IN_MOVE | IN_MODIFY )
+
+MtpStorage::MtpStorage(MtpStorageID id, const char* filePath,
+		const char* description, bool removable, uint64_t maxFileSize, MtpServer* refserver)
+	:	mStorageID(id),
+		mFilePath(filePath),
+		mDescription(description),
+		mMaxCapacity(0),
+		mMaxFileSize(maxFileSize),
+		mRemovable(removable),
+	mServer(refserver)
+{
+	MTPD("MtpStorage id: %d path: %s\n", id, filePath);
+	inotify_thread = 0;
+	inotify_fd = -1;
+	// Threading has not started yet so we should be safe to set these directly instead of using atomics
+	inotify_thread_kill.set_value(0);
+	sendEvents = false;
+	handleCurrentlySending = 0;
+	use_mutex = true;
+	if (pthread_mutex_init(&mtpMutex, NULL) != 0) {
+			MTPE("Failed to init mtpMutex\n");
+			use_mutex = false;
+	}
+	if (pthread_mutex_init(&inMutex, NULL) != 0) {
+			MTPE("Failed to init inMutex\n");
+			pthread_mutex_destroy(&mtpMutex);
+			use_mutex = false;
+	}
+}
+
+MtpStorage::~MtpStorage() {
+	if (inotify_thread) {
+			inotify_thread_kill.set_value(1);
+			MTPD("joining inotify_thread after sending the kill notification.\n");
+			pthread_join(inotify_thread, NULL); // There's not much we can do if there's an error here
+			inotify_thread = 0;
+			MTPD("~MtpStorage removing inotify watches and closing inotify_fd\n");
+			for (std::map<int, Tree*>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
+					inotify_rm_watch(inotify_fd, i->first);
+			}
+			close(inotify_fd);
+			inotifymap.clear();
+	}
+		// Deleting the root tree causes a cascade in btree.cpp that ends up
+		// deleting all of the trees and nodes.
+		delete mtpmap[0];
+		mtpmap.clear();
+		if (use_mutex) {
+				use_mutex = false;
+				MTPD("~MtpStorage destroying mutexes\n");
+				pthread_mutex_destroy(&mtpMutex);
+				pthread_mutex_destroy(&inMutex);
+		}
+
+}
+
+int MtpStorage::getType() const {
+	return (mRemovable ? MTP_STORAGE_REMOVABLE_RAM :  MTP_STORAGE_FIXED_RAM);
+}
+
+int MtpStorage::getFileSystemType() const {
+	return MTP_STORAGE_FILESYSTEM_HIERARCHICAL;
+}
+
+int MtpStorage::getAccessCapability() const {
+	return MTP_STORAGE_READ_WRITE;
+}
+
+uint64_t MtpStorage::getMaxCapacity() {
+	if (mMaxCapacity == 0) {
+		struct statfs	stat;
+		if (statfs(getPath(), &stat))
+			return -1;
+		mMaxCapacity = (uint64_t)stat.f_blocks * (uint64_t)stat.f_bsize;
+	}
+	return mMaxCapacity;
+}
+
+uint64_t MtpStorage::getFreeSpace() {
+	struct statfs	stat;
+	if (statfs(getPath(), &stat))
+		return -1;
+	return (uint64_t)stat.f_bavail * (uint64_t)stat.f_bsize;
+}
+
+const char* MtpStorage::getDescription() const {
+	return (const char *)mDescription;
+}
+
+int MtpStorage::renameObject(MtpObjectHandle handle, std::string newName) {
+		MTPD("MtpStorage::renameObject, handle: %u, new name: '%s'\n", handle, newName.c_str());
+		if (handle == MTP_PARENT_ROOT) {
+				MTPE("parent == MTP_PARENT_ROOT, cannot rename root\n");
+				return -1;
+		} else {
+				for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+						Node* node = i->second->findNode(handle);
+						if (node != NULL) {
+				std::string oldName = getNodePath(node);
+								std::string parentdir = oldName.substr(0, oldName.find_last_of('/'));
+								std::string newFullName = parentdir + "/" + newName;
+								MTPD("old: '%s', new: '%s'\n", oldName.c_str(), newFullName.c_str());
+								if (rename(oldName.c_str(), newFullName.c_str()) == 0) {
+										node->rename(newName);
+										return 0;
+								} else {
+										MTPE("MtpStorage::renameObject failed, handle: %u, new name: '%s'\n", handle, newName.c_str());
+										return -1;
+								}
+						}
+				}
+		}
+		// handle not found on this storage
+		return -1;
+}
+
+MtpObjectHandle MtpStorage::beginSendObject(const char* path,
+																						MtpObjectFormat format,
+																						MtpObjectHandle parent,
+																						__attribute__((unused)) uint64_t size,
+																						__attribute__((unused)) time_t modified) {
+		MTPD("MtpStorage::beginSendObject(), path: '%s', parent: %u, format: %04x\n", path, parent, format);
+		iter it = mtpmap.find(parent);
+		if (it == mtpmap.end()) {
+				MTPE("parent node not found, returning error\n");
+				return kInvalidObjectHandle;
+		}
+		Tree* tree = it->second;
+
+		std::string pathstr(path);
+		size_t slashpos = pathstr.find_last_of('/');
+		if (slashpos == std::string::npos) {
+				MTPE("path has no slash, returning error\n");
+				return kInvalidObjectHandle;
+		}
+		std::string parentdir = pathstr.substr(0, slashpos);
+		std::string basename = pathstr.substr(slashpos + 1);
+		if (parent != 0 && parentdir != getNodePath(tree)) {
+				MTPE("beginSendObject into path '%s' but parent tree has path '%s', returning error\n", parentdir.c_str(), getNodePath(tree).c_str());
+				return kInvalidObjectHandle;
+		}
+
+		MTPD("MtpStorage::beginSendObject() parentdir: %s basename: %s\n", parentdir.c_str(), basename.c_str());
+		// note: for directories, the mkdir call is done later in MtpServer, here we just reserve a handle
+		bool isDir = format == MTP_FORMAT_ASSOCIATION;
+		Node* node = addNewNode(isDir, tree, basename);
+		handleCurrentlySending = node->Mtpid(); // suppress inotify for this node while sending
+
+		return node->Mtpid();
+}
+
+int MtpStorage::createDB() {
+		std::string mtpParent = "";
+		mtpstorageparent = getPath();
+		// root directory is special: handle 0, parent 0, and empty path
+		mtpmap[0] = new Tree(0, 0, "");
+		if (use_mutex) {
+				sendEvents = true;
+				MTPD("inotify_init\n");
+				inotify_fd = inotify_init();
+				if (inotify_fd < 0) {
+						MTPE("Can't run inotify_init for mtp server: %s\n", strerror(errno));
+				} else {
+						MTPD("Starting inotify thread\n");
+						inotify_thread = inotify();
+				}
+		} else {
+				MTPD("NOT starting inotify thread\n");
+		}
+		// for debugging and caching purposes, read the root dir already now
+		readDir(mtpstorageparent, mtpmap[0]);
+		// all other dirs are read on demand
+	//
+		MTPD("MtpStorage::createDB DONE\n");
+		return 0;
+}
+
+Node* MtpStorage::findNode(MtpObjectHandle handle) {
+		for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+				Node* node = i->second->findNode(handle);
+				if (node != NULL) {
+						MTPD("findNode: found node %p for handle %u, name: %s\n", node, handle, node->getName().c_str());
+						if (node->Mtpid() != handle)
+						{
+								MTPE("BUG: entry for handle %u points to node with handle %u\n", handle, node->Mtpid());
+						}
+						return node;
+				}
+		}
+		// Item is not on this storage device
+		MTPD("MtpStorage::findNode: no node found for handle %u on storage %u, searched %u trees\n", handle, mStorageID, mtpmap.size());
+		return NULL;
+}
+
+std::string MtpStorage::getNodePath(Node* node) {
+	std::string path;
+		MTPD("getNodePath: node %p, handle %u\n", node, node->Mtpid());
+		while (node)
+		{
+				path = "/" + node->getName() + path;
+				MtpObjectHandle parent = node->getMtpParentId();
+				if (parent == 0)		// root
+						break;
+				node = findNode(parent);
+		}
+		path = mtpstorageparent + path;
+		MTPD("getNodePath: path %s\n", path.c_str());
+		return path;
+}
+
+MtpObjectHandleList* MtpStorage::getObjectList(__attribute__((unused)) MtpStorageID storageID, MtpObjectHandle parent) {
+		MTPD("MtpStorage::getObjectList, parent: %u\n", parent);
+		//append object id	(numerical #s) of database to int array
+		MtpObjectHandleList* list = new MtpObjectHandleList();
+		if (parent == MTP_PARENT_ROOT) {
+				MTPD("parent == MTP_PARENT_ROOT\n");
+				parent = 0;
+		}
+
+		if (mtpmap.find(parent) == mtpmap.end()) {
+				MTPE("parent handle not found, returning empty list\n");
+				return list;
+		}
+
+		Tree* tree = mtpmap[parent];
+		if (!tree->wasAlreadyRead())
+		{
+				std::string path = getNodePath(tree);
+				MTPD("reading directory on demand for tree %p (%u), path: %s\n", tree, tree->Mtpid(), path.c_str());
+				readDir(path, tree);
+		}
+
+		mtpmap[parent]->getmtpids(list);
+		MTPD("returning %u objects in %s.\n", list->size(), tree->getName().c_str());
+		return list;
+}
+
+Node* MtpStorage::addNewNode(bool isDir, Tree* tree, const std::string& name)
+{
+		// global counter for new object handles
+		static MtpObjectHandle mtpid = 0;
+
+		++mtpid;
+		MTPD("adding new %s node for %s, new handle: %u\n", isDir ? "dir" : "file", name.c_str(), mtpid);
+		MtpObjectHandle parent = tree->Mtpid();
+		MTPD("parent tree: %x, handle: %u, name: %s\n", tree, parent, tree->getName().c_str());
+		Node* node;
+		if (isDir)
+				node = mtpmap[mtpid] = new Tree(mtpid, parent, name);
+		else
+				node = new Node(mtpid, parent, name);
+		tree->addEntry(node);
+		return node;
+}
+
+int MtpStorage::readDir(const std::string& path, Tree* tree)
+{
+		struct dirent *de;
+		int storageID = getStorageID();
+		MtpObjectHandle parent = tree->Mtpid();
+
+		DIR *d = opendir(path.c_str());
+		MTPD("reading dir '%s', parent handle %u\n", path.c_str(), parent);
+		if (d == NULL) {
+				MTPE("error opening '%s' -- error: %s\n", path.c_str(), strerror(errno));
+				return -1;
+		}
+		// TODO: for refreshing dirs: capture old entries here
+		while ((de = readdir(d)) != NULL) {
+				// Because exfat-fuse causes issues with dirent, we will use stat
+				// for some things that dirent should be able to do
+				std::string item = path + "/" + de->d_name;
+				struct stat st;
+				if (lstat(item.c_str(), &st)) {
+						MTPE("Error running lstat on '%s'\n", item.c_str());
+						return -1;
+				}
+				// TODO: if we want to use this for refreshing dirs too, first find existing name and overwrite
+				if (strcmp(de->d_name, ".") == 0)
+						continue;
+				if (strcmp(de->d_name, "..") == 0)
+						continue;
+				Node* node = addNewNode(st.st_mode & S_IFDIR, tree, de->d_name);
+				node->addProperties(item, storageID);
+				//if (sendEvents)
+				//		mServer->sendObjectAdded(node->Mtpid());
+				//		sending events here makes simple-mtpfs very slow, and it is probably the wrong thing to do anyway
+		}
+		closedir(d);
+		// TODO: for refreshing dirs: remove entries that no longer exist (with their nodes)
+		tree->setAlreadyRead(true);
+		addInotify(tree);
+		return 0;
+}
+
+int MtpStorage::addInotify(Tree* tree) {
+		if (inotify_fd < 0) {
+				MTPE("inotify_fd not set or error: %i\n", inotify_fd);
+				return -1;
+		}
+		std::string path = getNodePath(tree);
+		MTPD("adding inotify for tree %x, dir: %s\n", tree, path.c_str());
+		int wd = inotify_add_watch(inotify_fd, path.c_str(), WATCH_FLAGS);
+		if (wd < 0) {
+				MTPE("inotify_add_watch failed: %s\n", strerror(errno));
+				return -1;
+		}
+		inotifymap[wd] = tree;
+		return 0;
+}
+
+pthread_t MtpStorage::inotify(void) {
+		pthread_t thread;
+		pthread_attr_t tattr;
+
+		if (pthread_attr_init(&tattr)) {
+				MTPE("Unable to pthread_attr_init\n");
+				return 0;
+		}
+		if (pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_JOINABLE)) {
+				MTPE("Error setting pthread_attr_setdetachstate\n");
+				return 0;
+		}
+		ThreadPtr inotifyptr = &MtpStorage::inotify_t;
+		PThreadPtr p = *(PThreadPtr*)&inotifyptr;
+		pthread_create(&thread, &tattr, p, this);
+		if (pthread_attr_destroy(&tattr)) {
+				MTPE("Failed to pthread_attr_destroy\n");
+		}
+		return thread;
+}
+
+int MtpStorage::inotify_t(void) {
+		#define EVENT_SIZE ( sizeof(struct inotify_event) )
+		#define EVENT_BUF_LEN ( 1024 * ( EVENT_SIZE + 16) )
+		char buf[EVENT_BUF_LEN];
+		fd_set fdset;
+		struct timeval seltmout;
+		int sel_ret;
+
+		MTPD("inotify thread starting.\n");
+
+		while (inotify_thread_kill.get_value() == 0) {
+				FD_ZERO(&fdset);
+				FD_SET(inotify_fd, &fdset);
+				seltmout.tv_sec = 0;
+				seltmout.tv_usec = 25000;
+				sel_ret = select(inotify_fd + 1, &fdset, NULL, NULL, &seltmout);
+				if (sel_ret == 0)
+						continue;
+				int i = 0;
+				int len = read(inotify_fd, buf, EVENT_BUF_LEN);
+
+				if (len < 0) {
+						if (errno == EINTR)
+								continue;
+						MTPE("inotify_t Can't read inotify events\n");
+				}
+
+				while (i < len && inotify_thread_kill.get_value() == 0) {
+						struct inotify_event *event = (struct inotify_event *) &buf[i];
+						if (event->len) {
+								MTPD("inotify event: wd: %i, mask: %x, name: %s\n", event->wd, event->mask, event->name);
+								lockMutex(1);
+								handleInotifyEvent(event);
+								unlockMutex(1);
+						}
+						i += EVENT_SIZE + event->len;
+				}
+		}
+		MTPD("inotify_thread_kill received!\n");
+		// This cleanup is handled in the destructor.
+		/*for (std::map<int, Tree*>::iterator i = inotifymap.begin(); i != inotifymap.end(); i++) {
+				inotify_rm_watch(inotify_fd, i->first);
+		}
+		close(inotify_fd);*/
+		return 0;
+}
+
+void MtpStorage::handleInotifyEvent(struct inotify_event* event)
+{
+		std::map<int, Tree*>::iterator it = inotifymap.find(event->wd);
+		if (it == inotifymap.end()) {
+				MTPE("Unable to locate inotify_wd: %i\n", event->wd);
+				return;
+		}
+		Tree* tree = it->second;
+		MTPD("inotify_t tree: %x '%s'\n", tree, tree->getName().c_str());
+		Node* node = tree->findEntryByName(basename(event->name));
+		if (node && node->Mtpid() == handleCurrentlySending) {
+				MTPD("ignoring inotify event for currently uploading file, handle: %u\n", node->Mtpid());
+				return;
+		}
+		if (event->mask & IN_CREATE || event->mask & IN_MOVED_TO) {
+				if (event->mask & IN_ISDIR) {
+						MTPD("inotify_t create is dir\n");
+				} else {
+						MTPD("inotify_t create is file\n");
+				}
+				if (node == NULL) {
+						node = addNewNode(event->mask & IN_ISDIR, tree, event->name);
+						std::string item = getNodePath(tree) + "/" + event->name;
+						node->addProperties(item, getStorageID());
+						mServer->sendObjectAdded(node->Mtpid());
+				} else {
+						MTPD("inotify_t item already exists.\n");
+				}
+				if (event->mask & IN_ISDIR) {
+						// TODO: do we need to do anything here? probably not until someone reads from the dir...
+				}
+		} else if (event->mask & IN_DELETE || event->mask & IN_MOVED_FROM) {
+				if (event->mask & IN_ISDIR) {
+						MTPD("inotify_t Directory %s deleted\n", event->name);
+				} else {
+						MTPD("inotify_t File %s deleted\n", event->name);
+				}
+				if (node)
+				{
+						if (event->mask & IN_ISDIR) {
+								for (std::map<int, Tree*>::iterator it = inotifymap.begin(); it != inotifymap.end(); ++it) {
+										if (it->second == node) {
+												inotify_rm_watch(inotify_fd, it->first);
+												MTPD("inotify_t removing watch on '%s'\n", getNodePath(it->second).c_str());
+												inotifymap.erase(it->first);
+												break;
+										}
+
+								}
+						}
+						MtpObjectHandle handle = node->Mtpid();
+						deleteFile(handle);
+						mServer->sendObjectRemoved(handle);
+				} else {
+						MTPD("inotify_t already removed.\n");
+				}
+		} else if (event->mask & IN_MODIFY) {
+				MTPD("inotify_t item %s modified.\n", event->name);
+				if (node != NULL) {
+						uint64_t orig_size = node->getProperty(MTP_PROPERTY_OBJECT_SIZE).valueInt;
+						struct stat st;
+						uint64_t new_size = 0;
+						if (lstat(getNodePath(node).c_str(), &st) == 0)
+								new_size = (uint64_t)st.st_size;
+						if (orig_size != new_size) {
+								MTPD("size changed from %llu to %llu on mtpid: %u\n", orig_size, new_size, node->Mtpid());
+								node->updateProperty(MTP_PROPERTY_OBJECT_SIZE, new_size, "", MTP_TYPE_UINT64);
+								mServer->sendObjectUpdated(node->Mtpid());
+						}
+				} else {
+						MTPE("inotify_t modified item not found\n");
+				}
+		} else if (event->mask & IN_DELETE_SELF || event->mask & IN_MOVE_SELF) {
+				// TODO: is this always already handled by IN_DELETE for the parent dir?
+		}
+}
+
+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( __attribute__((unused)) int thread_type) {
+		if (!use_mutex)
+				return; // mutex is disabled
+		pthread_mutex_unlock(&inMutex);
+		pthread_mutex_unlock(&mtpMutex);
+}
+
+int MtpStorage::getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, MtpStorage::PropEntry& pe) {
+		Node *node;
+		for (iter i = mtpmap.begin(); i != mtpmap.end(); i++) {
+				node = i->second->findNode(handle);
+				if (node != NULL) {
+						const Node::mtpProperty& prop = node->getProperty(property);
+						if (prop.property != property) {
+								MTPD("getObjectPropertyValue: unknown property %x for handle %u\n", property, handle);
+								return -1;
+						}
+						pe.datatype = prop.dataType;
+						pe.intvalue = prop.valueInt;
+						pe.strvalue = prop.valueStr;
+						pe.handle = handle;
+						pe.property = property;
+						return 0;
+				}
+		}
+		// handle not found on this storage
+		return -1;
+}
+
+void MtpStorage::endSendObject(const char* path, MtpObjectHandle handle, __attribute__((unused)) MtpObjectFormat format, __attribute__((unused)) bool succeeded)
+{
+		Node* node = findNode(handle);
+		if (!node)
+				return; // just ignore if this is for another storage
+
+		node->addProperties(path, mStorageID);
+		handleCurrentlySending = 0;
+		// TODO: are we supposed to send an event about an upload by the initiator?
+		if (sendEvents)
+				mServer->sendObjectAdded(node->Mtpid());
+}
+
+int MtpStorage::getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, __attribute__((unused)) int depth, MtpDataPacket& packet) {
+		MTPD("MtpStorage::getObjectPropertyList handle: %u, format: %x, property: %x\n", handle, format, property);
+		if (groupCode != 0)
+		{
+				MTPE("getObjectPropertyList: groupCode unsupported\n");
+				return -1; // TODO: RESPONSE_SPECIFICATION_BY_GROUP_UNSUPPORTED
+		}
+		// TODO: support all the special stuff, like:
+		// handle == 0 -> all objects at the root level
+		// handle == 0xffffffff -> all objects (on all storages? how could we support that?)
+		// format == 0 -> all formats, otherwise filter by ObjectFormatCode
+		// property == 0xffffffff -> all properties except those with group code 0xffffffff
+		// if property == 0 then use groupCode
+		//	 groupCode == 0 -> return Specification_By_Group_Unsupported
+		// depth == 0xffffffff -> all objects incl. and below handle
+
+		std::vector<PropEntry> results;
+
+		if (handle == 0xffffffff) {
+				// TODO: all object on all storages (needs a different design, result packet needs to be built by server instead of storage)
+		} else if (handle == 0) {
+				// all objects at the root level
+				Tree* root = mtpmap[0];
+				MtpObjectHandleList list;
+				root->getmtpids(&list);
+				for (MtpObjectHandleList::iterator it = list.begin(); it != list.end(); ++it) {
+						Node* node = root->findNode(*it);
+						if (!node) {
+								MTPE("BUG: node not found for root entry with handle %u\n", *it);
+								break;
+						}
+						queryNodeProperties(results, node, property, groupCode, mStorageID);
+				}
+		} else {
+				// single object
+				Node* node = findNode(handle);
+				if (!node) {
+						// Item is not on this storage device
+						return -1;
+				}
+				queryNodeProperties(results, node, property, groupCode, mStorageID);
+		}
+
+		MTPD("MtpStorage::getObjectPropertyList::count: %u\n", results.size());
+		packet.putUInt32(results.size());
+
+		for (size_t i = 0; i < results.size(); ++i) {
+				PropEntry& p = results[i];
+				MTPD("handle: %u, propertyCode: %x = %s, datatype: %x, value: %llu\n",
+								p.handle, p.property, MtpDebug::getObjectPropCodeName(p.property),
+								p.datatype, p.intvalue);
+				packet.putUInt32(p.handle);
+				packet.putUInt16(p.property);
+				packet.putUInt16(p.datatype);
+				switch (p.datatype) {
+						case MTP_TYPE_INT8:
+								MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_INT8\n");
+								packet.putInt8(p.intvalue);
+								break;
+						case MTP_TYPE_UINT8:
+								MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_UINT8\n");
+								packet.putUInt8(p.intvalue);
+								break;
+						case MTP_TYPE_INT16:
+								MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_INT16\n");
+								packet.putInt16(p.intvalue);
+								break;
+						case MTP_TYPE_UINT16:
+								MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_UINT16\n");
+								packet.putUInt16(p.intvalue);
+								break;
+						case MTP_TYPE_INT32:
+								MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_INT32\n");
+								packet.putInt32(p.intvalue);
+								break;
+						case MTP_TYPE_UINT32:
+								MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_UINT32\n");
+								packet.putUInt32(p.intvalue);
+								break;
+						case MTP_TYPE_INT64:
+								MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_INT64\n");
+								packet.putInt64(p.intvalue);
+								break;
+						case MTP_TYPE_UINT64:
+								MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_UINT64\n");
+								packet.putUInt64(p.intvalue);
+								break;
+						case MTP_TYPE_INT128:
+								MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_INT128\n");
+								packet.putInt128(p.intvalue);
+								break;
+						case MTP_TYPE_UINT128:
+								MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_UINT128\n");
+								packet.putUInt128(p.intvalue);
+								break;
+						case MTP_TYPE_STR:
+								MTPD("MtpStorage::getObjectPropertyList::MTP_TYPE_STR: %s\n", p.strvalue.c_str());
+								packet.putString(p.strvalue.c_str());
+								break;
+						default:
+								MTPE("bad or unsupported data type: %x in MyMtpDatabase::getObjectPropertyList", p.datatype);
+								break;
+				}
+		}
+		return 0;
+}
+
+int MtpStorage::getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info) {
+		struct stat st;
+		uint64_t size = 0;
+		MTPD("MtpStorage::getObjectInfo, handle: %u\n", handle);
+		Node* node = findNode(handle);
+		if (!node) {
+				// Item is not on this storage device
+				return -1;
+		}
+
+		info.mStorageID = getStorageID();
+		MTPD("info.mStorageID: %u\n", info.mStorageID);
+		info.mParent = node->getMtpParentId();
+		MTPD("mParent: %u\n", info.mParent);
+		// TODO: do we want to lstat again here, or read from the node properties?
+		if (lstat(getNodePath(node).c_str(), &st) == 0)
+				size = st.st_size;
+		MTPD("size is: %llu\n", size);
+		info.mCompressedSize = (size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size);
+		info.mDateModified = st.st_mtime;
+		if (S_ISDIR(st.st_mode)) {
+				info.mFormat = MTP_FORMAT_ASSOCIATION;
+		}
+		else {
+				info.mFormat = MTP_FORMAT_UNDEFINED;
+		}
+		info.mName = strdup(node->getName().c_str());
+		MTPD("MtpStorage::getObjectInfo found, Exiting getObjectInfo()\n");
+		return 0;
+}
+
+int MtpStorage::getObjectFilePath(MtpObjectHandle handle, MtpStringBuffer& outFilePath, int64_t& outFileLength, MtpObjectFormat& outFormat) {
+		MTPD("MtpStorage::getObjectFilePath handle: %u\n", handle);
+		Node* node = findNode(handle);
+		if (!node)
+		{
+				// Item is not on this storage device
+				return -1;
+		}
+		// TODO: do we want to lstat here, or just read the info from the node?
+		struct stat st;
+		if (lstat(getNodePath(node).c_str(), &st) == 0)
+				outFileLength = st.st_size;
+		else
+				outFileLength = 0;
+		outFilePath.set(getNodePath(node).c_str());
+		MTPD("outFilePath: %s\n", (const char*) outFilePath);
+		outFormat = node->isDir() ? MTP_FORMAT_ASSOCIATION : MTP_FORMAT_UNDEFINED;
+		return 0;
+}
+
+int MtpStorage::deleteFile(MtpObjectHandle handle) {
+		MTPD("MtpStorage::deleteFile handle: %u\n", handle);
+		Node* node = findNode(handle);
+		if (!node) {
+				// Item is not on this storage device
+				return -1;
+		}
+		MtpObjectHandle parent = node->getMtpParentId();
+		Tree* tree = mtpmap[parent];
+		if (!tree) {
+				MTPE("parent tree for handle %u not found\n", parent);
+				return -1;
+		}
+		if (node->isDir()) {
+				MTPD("deleting tree from mtpmap: %u\n", handle);
+				mtpmap.erase(handle);
+		}
+
+		MTPD("deleting handle: %u\n", handle);
+		tree->deleteNode(handle);
+		MTPD("deleted\n");
+		return 0;
+}
+
+void MtpStorage::queryNodeProperties(std::vector<MtpStorage::PropEntry>& results, Node* node, uint32_t property, __attribute__((unused)) int groupCode, MtpStorageID storageID)
+{
+		MTPD("queryNodeProperties handle %u, path: %s\n", node->Mtpid(), getNodePath(node).c_str());
+		PropEntry pe;
+		pe.handle = node->Mtpid();
+		pe.property = property;
+
+		if (property == 0xffffffff)
+		{
+				// add all properties
+				MTPD("MtpStorage::queryNodeProperties for all properties\n");
+				std::vector<Node::mtpProperty> mtpprop = node->getMtpProps();
+				for (size_t i = 0; i < mtpprop.size(); ++i) {
+						pe.property = mtpprop[i].property;
+						pe.datatype = mtpprop[i].dataType;
+						pe.intvalue = mtpprop[i].valueInt;
+						pe.strvalue = mtpprop[i].valueStr;
+						results.push_back(pe);
+				}
+				return;
+		}
+		else if (property == 0)
+		{
+				// TODO: use groupCode
+		}
+
+		// single property
+		// TODO: this should probably be moved to the Node class and/or merged with getObjectPropertyValue
+		switch (property) {
+//				case MTP_PROPERTY_OBJECT_FORMAT:
+//						pe.datatype = MTP_TYPE_UINT16;
+//						pe.intvalue = node->getIntProperty(MTP_PROPERTY_OBJECT_FORMAT);
+//						break;
+
+				case MTP_PROPERTY_STORAGE_ID:
+						pe.datatype = MTP_TYPE_UINT32;
+						pe.intvalue = storageID;
+						break;
+
+				case MTP_PROPERTY_PROTECTION_STATUS:
+						pe.datatype = MTP_TYPE_UINT16;
+						pe.intvalue = 0;
+						break;
+
+				case MTP_PROPERTY_OBJECT_SIZE:
+				{
+						pe.datatype = MTP_TYPE_UINT64;
+						struct stat st;
+						pe.intvalue = 0;
+						if (lstat(getNodePath(node).c_str(), &st) == 0)
+								pe.intvalue = st.st_size;
+						break;
+				}
+
+				default:
+				{
+						const Node::mtpProperty& prop = node->getProperty(property);
+						if (prop.property != property)
+						{
+								MTPD("queryNodeProperties: unknown property %x\n", property);
+								return;
+						}
+						pe.datatype = prop.dataType;
+						pe.intvalue = prop.valueInt;
+						pe.strvalue = prop.valueStr;
+						// TODO: all the special case stuff in MyMtpDatabase::getObjectPropertyValue is missing here
+				}
+
+		}
+		results.push_back(pe);
+}
+
+
diff --git a/mtp/ffs/MtpStorage.h b/mtp/ffs/MtpStorage.h
new file mode 100755
index 0000000..9d6d291
--- /dev/null
+++ b/mtp/ffs/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.
+ */
+
+#ifndef _MTP_STORAGE_H
+#define _MTP_STORAGE_H
+
+#include "MtpObjectInfo.h"
+#include "MtpServer.h"
+#include "MtpStringBuffer.h"
+#include "MtpTypes.h"
+#include "mtp.h"
+#include "btree.hpp"
+#include "../../tw_atomic.hpp"
+
+class MtpDatabase;
+
+class MtpStorage {
+
+public:
+	struct PropEntry {
+		   MtpObjectHandle handle;
+		   uint16_t property;
+		   uint16_t datatype;
+		   uint64_t intvalue;
+		   std::string strvalue;
+	};
+
+private:
+	MtpStorageID			mStorageID;
+	MtpStringBuffer			mFilePath;
+	MtpStringBuffer			mDescription;
+	uint64_t				mMaxCapacity;
+	uint64_t				mMaxFileSize;
+	bool					mRemovable;
+	typedef					std::map<int, Tree*> maptree;
+	typedef					maptree::iterator iter;
+	maptree					mtpmap;
+	std::string				mtpstorageparent;
+	MtpObjectHandle			handleCurrentlySending;
+	int						inotify_fd;
+	std::map<int, Tree*>	inotifymap;		   // inotify wd -> tree
+	bool					sendEvents;
+	MtpServer*				mServer;
+	typedef					int (MtpStorage::*ThreadPtr)(void);
+	typedef					void* (*PThreadPtr)(void *);
+	bool					use_mutex;
+	pthread_mutex_t			inMutex; // inotify mutex
+	pthread_mutex_t			mtpMutex; // main mtp mutex
+	TWAtomicInt				inotify_thread_kill;
+	pthread_t				inotify_thread;
+	Node*					findNode(MtpObjectHandle handle);
+	std::string				getNodePath(Node* node);
+	Node*					addNewNode(bool isDir, Tree* tree, const std::string& name);
+	void					queryNodeProperties(std::vector<PropEntry>& results, Node* node, uint32_t property, int groupCode, MtpStorageID storageID);
+	int						addInotify(Tree* tree);
+	void					handleInotifyEvent(struct inotify_event* event);
+
+public:
+	MtpStorage(MtpStorageID id, const char* filePath,
+								const char* description,
+								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						renameObject(MtpObjectHandle handle, std::string newName);
+	MtpObjectHandle			beginSendObject(const char* path, MtpObjectFormat format, MtpObjectHandle parent, uint64_t size, time_t modified);
+	MtpObjectHandleList*	getObjectList(MtpStorageID storageID, MtpObjectHandle parent);
+	int						getObjectPropertyList(MtpObjectHandle handle, uint32_t format, uint32_t property, int groupCode, int depth, MtpDataPacket& packet);
+	int						readDir(const std::string& path, Tree* tree);
+	int						getObjectPropertyValue(MtpObjectHandle handle, MtpObjectProperty property, PropEntry& prop);
+	int						getObjectInfo(MtpObjectHandle handle, MtpObjectInfo& info);
+	void					endSendObject(const char* path, MtpObjectHandle handle, MtpObjectFormat format, bool succeeded);
+	int						getObjectFilePath(MtpObjectHandle handle, MtpStringBuffer& outFilePath, int64_t& outFileLength, MtpObjectFormat& outFormat);
+	int						deleteFile(MtpObjectHandle handle);
+	int						createDB();
+	pthread_t				inotify();
+	int						inotify_t();
+	void					lockMutex(int thread_type);
+	void					unlockMutex(int thread_type);
+};
+
+#endif // _MTP_STORAGE_H
diff --git a/mtp/ffs/MtpStorageInfo.cpp b/mtp/ffs/MtpStorageInfo.cpp
new file mode 100644
index 0000000..21a8322
--- /dev/null
+++ b/mtp/ffs/MtpStorageInfo.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "MtpStorageInfo"
+
+#include <inttypes.h>
+
+#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);
+}
+
+bool MtpStorageInfo::read(MtpDataPacket& packet) {
+	MtpStringBuffer string;
+
+	// read the device info
+	if (!packet.getUInt16(mStorageType)) return false;
+	if (!packet.getUInt16(mFileSystemType)) return false;
+	if (!packet.getUInt16(mAccessCapability)) return false;
+	if (!packet.getUInt64(mMaxCapacity)) return false;
+	if (!packet.getUInt64(mFreeSpaceBytes)) return false;
+	if (!packet.getUInt32(mFreeSpaceObjects)) return false;
+
+	if (!packet.getString(string)) return false;
+	mStorageDescription = strdup((const char *)string);
+	if (!mStorageDescription) return false;
+	if (!packet.getString(string)) return false;
+	mVolumeIdentifier = strdup((const char *)string);
+	if (!mVolumeIdentifier) return false;
+
+	return true;
+}
+
+void MtpStorageInfo::print() {
+	MTPD("Storage Info %08X:\n\tmStorageType: %d\n\tmFileSystemType: %d\n\tmAccessCapability: %d\n",
+			mStorageID, mStorageType, mFileSystemType, mAccessCapability);
+	MTPD("\tmMaxCapacity: %" PRIu64 "\n\tmFreeSpaceBytes: %" PRIu64 "\n\tmFreeSpaceObjects: %d\n",
+			mMaxCapacity, mFreeSpaceBytes, mFreeSpaceObjects);
+	MTPD("\tmStorageDescription: %s\n\tmVolumeIdentifier: %s\n",
+			mStorageDescription, mVolumeIdentifier);
+}
diff --git a/mtp/ffs/MtpStorageInfo.h b/mtp/ffs/MtpStorageInfo.h
new file mode 100644
index 0000000..08e0571
--- /dev/null
+++ b/mtp/ffs/MtpStorageInfo.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_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:
+	explicit			MtpStorageInfo(MtpStorageID id);
+	virtual				~MtpStorageInfo();
+
+	bool				read(MtpDataPacket& packet);
+
+	void				print();
+};
+
+#endif // _MTP_STORAGE_INFO_H
diff --git a/mtp/ffs/MtpStringBuffer.cpp b/mtp/ffs/MtpStringBuffer.cpp
new file mode 100644
index 0000000..e2302df
--- /dev/null
+++ b/mtp/ffs/MtpStringBuffer.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "MtpStringBuffer"
+
+#include <codecvt>
+#include <locale>
+#include <string>
+#include <vector>
+
+#include "MtpDataPacket.h"
+#include "MtpStringBuffer.h"
+
+namespace {
+
+std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> gConvert;
+
+static std::string utf16ToUtf8(std::u16string input_str) {
+	return gConvert.to_bytes(input_str);
+}
+
+static std::u16string utf8ToUtf16(std::string input_str) {
+	return gConvert.from_bytes(input_str);
+}
+
+} // namespace
+
+MtpStringBuffer::MtpStringBuffer(const char* src)
+{
+	set(src);
+}
+
+MtpStringBuffer::MtpStringBuffer(const uint16_t* src)
+{
+	set(src);
+}
+
+MtpStringBuffer::MtpStringBuffer(const MtpStringBuffer& src)
+{
+	mString = src.mString;
+}
+
+void MtpStringBuffer::set(const char* src) {
+	mString = std::string(src);
+}
+
+void MtpStringBuffer::set(const uint16_t* src) {
+	mString = utf16ToUtf8(std::u16string((const char16_t*)src));
+}
+
+bool MtpStringBuffer::readFromPacket(MtpDataPacket* packet) {
+	uint8_t count;
+	if (!packet->getUInt8(count))
+		return false;
+	if (count == 0)
+		return true;
+
+	std::vector<char16_t> buffer(count);
+	for (int i = 0; i < count; i++) {
+		uint16_t ch;
+		if (!packet->getUInt16(ch))
+			return false;
+		buffer[i] = ch;
+	}
+	if (buffer[count-1] != '\0') {
+		MTPE("Mtp string not null terminated\n");
+		return false;
+	}
+	mString = utf16ToUtf8(std::u16string(buffer.data()));
+	return true;
+}
+
+void MtpStringBuffer::writeToPacket(MtpDataPacket* packet) const {
+	std::u16string src16 = utf8ToUtf16(mString);
+	int count = src16.length();
+
+	if (count == 0) {
+		packet->putUInt8(0);
+		return;
+	}
+	packet->putUInt8(std::min(count + 1, MTP_STRING_MAX_CHARACTER_NUMBER));
+
+	int i = 0;
+	for (char16_t &c : src16) {
+		if (i == MTP_STRING_MAX_CHARACTER_NUMBER - 1) {
+			// Leave a slot for null termination.
+			MTPD("Mtp truncating long string\n");
+			break;
+		}
+		packet->putUInt16(c);
+		i++;
+	}
+	// only terminate with zero if string is not empty
+	packet->putUInt16(0);
+}
diff --git a/mtp/ffs/MtpStringBuffer.h b/mtp/ffs/MtpStringBuffer.h
new file mode 100644
index 0000000..0006cdb
--- /dev/null
+++ b/mtp/ffs/MtpStringBuffer.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_STRING_BUFFER_H
+#define _MTP_STRING_BUFFER_H
+
+#include <log/log.h>
+#include <stdint.h>
+#include <string>
+
+// Max Character number of a MTP String
+#define MTP_STRING_MAX_CHARACTER_NUMBER				255
+
+class MtpDataPacket;
+
+// Represents a utf8 string, with a maximum of 255 characters
+class MtpStringBuffer {
+
+private:
+	std::string		mString;
+
+public:
+					MtpStringBuffer() {};
+					~MtpStringBuffer() {};
+
+	explicit		MtpStringBuffer(const char* src);
+	explicit		MtpStringBuffer(const uint16_t* src);
+					MtpStringBuffer(const MtpStringBuffer& src);
+
+	void			set(const char* src);
+	void			set(const uint16_t* src);
+
+	inline void		append(const char* other);
+	inline void		append(MtpStringBuffer &other);
+
+	bool			readFromPacket(MtpDataPacket* packet);
+	void			writeToPacket(MtpDataPacket* packet) const;
+
+	inline bool		isEmpty() const { return mString.empty(); }
+	inline int		size() const { return mString.length(); }
+
+	inline operator const char*() const { return mString.c_str(); }
+};
+
+inline void MtpStringBuffer::append(const char* other) {
+	mString += other;
+}
+
+inline void MtpStringBuffer::append(MtpStringBuffer &other) {
+	mString += other.mString;
+}
+
+#endif // _MTP_STRING_BUFFER_H
diff --git a/mtp/ffs/MtpTypes.h b/mtp/ffs/MtpTypes.h
new file mode 100644
index 0000000..9c37b8c
--- /dev/null
+++ b/mtp/ffs/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.
+ */
+
+#ifndef _MTP_TYPES_H
+#define _MTP_TYPES_H
+
+#include <stdint.h>
+#include <vector>
+
+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 std::vector<MtpStorage *> MtpStorageList;
+typedef std::vector<MtpDevice*> MtpDeviceList;
+typedef std::vector<MtpProperty*> MtpPropertyList;
+
+typedef std::vector<uint8_t> UInt8List;
+typedef std::vector<uint16_t> UInt16List;
+typedef std::vector<uint32_t> UInt32List;
+typedef std::vector<uint64_t> UInt64List;
+typedef std::vector<int8_t> Int8List;
+typedef std::vector<int16_t> Int16List;
+typedef std::vector<int32_t> Int32List;
+typedef std::vector<int64_t> Int64List;
+
+typedef UInt16List MtpObjectPropertyList;
+typedef UInt16List MtpDevicePropertyList;
+typedef UInt16List MtpObjectFormatList;
+typedef UInt32List MtpObjectHandleList;
+typedef UInt16List MtpObjectPropertyList;
+typedef UInt32List MtpStorageIDList;
+
+enum UrbPacketDivisionMode {
+	// First packet only contains a header.
+	FIRST_PACKET_ONLY_HEADER,
+	// First packet contains payload much as possible.
+	FIRST_PACKET_HAS_PAYLOAD
+};
+
+#endif // _MTP_TYPES_H
diff --git a/mtp/ffs/MtpUtils.cpp b/mtp/ffs/MtpUtils.cpp
new file mode 100644
index 0000000..80c01bf
--- /dev/null
+++ b/mtp/ffs/MtpUtils.cpp
@@ -0,0 +1,263 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "MtpUtils"
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <string>
+#include <sys/sendfile.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "MtpUtils.h"
+
+using namespace std;
+
+constexpr unsigned long FILE_COPY_SIZE = 262144;
+
+static void access_ok(const char *path) {
+	if (access(path, F_OK) == -1) {
+		// Ignore. Failure could be common in cases of delete where
+		// the metadata was updated through other paths.
+	}
+}
+
+/*
+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.
+This is followed by a UTC offset given as "[+-]zzzz" or the literal "Z", meaning UTC.
+*/
+
+bool parseDateTime(const char* dateTime, time_t& outSeconds) {
+	int year, month, day, hour, minute, second;
+	if (sscanf(dateTime, "%04d%02d%02dT%02d%02d%02d",
+			   &year, &month, &day, &hour, &minute, &second) != 6)
+		return false;
+
+	// skip optional tenth of second
+	const char* tail = dateTime + 15;
+	if (tail[0] == '.' && tail[1]) tail += 2;
+
+	// FIXME: "Z" means UTC, but non-"Z" doesn't mean local time.
+	// It might be that you're in Asia/Seoul on vacation and your Android
+	// device has noticed this via the network, but your camera was set to
+	// America/Los_Angeles once when you bought it and doesn't know where
+	// it is right now, so the camera says "20160106T081700-0800" but we
+	// just ignore the "-0800" and assume local time which is actually "+0900".
+	// I think to support this (without switching to Java or using icu4c)
+	// you'd want to always use timegm(3) and then manually add/subtract
+	// the UTC offset parsed from the string (taking care of wrapping).
+	// mktime(3) ignores the tm_gmtoff field, so you can't let it do the work.
+	bool useUTC = (tail[0] == 'Z');
+
+	struct tm 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_isdst = -1;
+	outSeconds = useUTC ? timegm(&tm) : mktime(&tm);
+
+	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);
+}
+
+int makeFolder(const char *path) {
+	mode_t mask = umask(0);
+	int ret = mkdir((const char *)path, DIR_PERM);
+	umask(mask);
+	if (ret && ret != -EEXIST) {
+		PLOG(ERROR) << "Failed to create folder " << path;
+		ret = -1;
+	} else {
+		chown((const char *)path, getuid(), FILE_GROUP);
+	}
+	access_ok(path);
+	return ret;
+}
+
+/**
+ * Copies target path and all children to destination path.
+ *
+ * Returns 0 on success or a negative value indicating number of failures
+ */
+int copyRecursive(const char *fromPath, const char *toPath) {
+	int ret = 0;
+	string fromPathStr(fromPath);
+	string toPathStr(toPath);
+
+	DIR* dir = opendir(fromPath);
+	if (!dir) {
+		PLOG(ERROR) << "opendir " << fromPath << " failed";
+		return -1;
+	}
+	if (fromPathStr[fromPathStr.size()-1] != '/')
+		fromPathStr += '/';
+	if (toPathStr[toPathStr.size()-1] != '/')
+		toPathStr += '/';
+
+	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;
+		}
+		string oldFile = fromPathStr + name;
+		string newFile = toPathStr + name;
+
+		if (entry->d_type == DT_DIR) {
+			ret += makeFolder(newFile.c_str());
+			ret += copyRecursive(oldFile.c_str(), newFile.c_str());
+		} else {
+			ret += copyFile(oldFile.c_str(), newFile.c_str());
+		}
+	}
+	return ret;
+}
+
+int copyFile(const char *fromPath, const char *toPath) {
+	auto start = std::chrono::steady_clock::now();
+
+	android::base::unique_fd fromFd(open(fromPath, O_RDONLY));
+	if (fromFd == -1) {
+		PLOG(ERROR) << "Failed to open copy from " << fromPath;
+		return -1;
+	}
+	android::base::unique_fd toFd(open(toPath, O_CREAT | O_WRONLY, FILE_PERM));
+	if (toFd == -1) {
+		PLOG(ERROR) << "Failed to open copy to " << toPath;
+		return -1;
+	}
+	off_t offset = 0;
+
+	struct stat sstat = {};
+	if (stat(fromPath, &sstat) == -1)
+		return -1;
+
+	off_t length = sstat.st_size;
+	int ret = 0;
+
+	while (offset < length) {
+		ssize_t transfer_length = std::min(length - offset, (off_t) FILE_COPY_SIZE);
+		ret = sendfile(toFd, fromFd, &offset, transfer_length);
+		if (ret != transfer_length) {
+			ret = -1;
+			PLOG(ERROR) << "Copying failed!";
+			break;
+		}
+	}
+	auto end = std::chrono::steady_clock::now();
+	std::chrono::duration<double> diff = end - start;
+	LOG(DEBUG) << "Copied a file with MTP. Time: " << diff.count() << " s, Size: " << length <<
+		", Rate: " << ((double) length) / diff.count() << " bytes/s";
+	chown(toPath, getuid(), FILE_GROUP);
+	access_ok(toPath);
+	return ret == -1 ? -1 : 0;
+}
+
+void deleteRecursive(const char* path) {
+	string pathStr(path);
+	if (pathStr[pathStr.size()-1] != '/') {
+		pathStr += '/';
+	}
+
+	DIR* dir = opendir(path);
+	if (!dir) {
+		PLOG(ERROR) << "opendir " << path << " failed";
+		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;
+		}
+		string childPath = pathStr + name;
+		int success;
+		if (entry->d_type == DT_DIR) {
+			deleteRecursive(childPath.c_str());
+			success = rmdir(childPath.c_str());
+		} else {
+			success = unlink(childPath.c_str());
+		}
+		access_ok(childPath.c_str());
+		if (success == -1)
+			PLOG(ERROR) << "Deleting path " << childPath << " failed";
+	}
+	closedir(dir);
+}
+
+bool deletePath(const char* path) {
+	struct stat statbuf;
+	int success;
+	if (stat(path, &statbuf) == 0) {
+		if (S_ISDIR(statbuf.st_mode)) {
+			// rmdir will fail if the directory is non empty, so
+			// there is no need to keep errors from deleteRecursive
+			deleteRecursive(path);
+			success = rmdir(path);
+		} else {
+			success = unlink(path);
+		}
+	} else {
+		PLOG(ERROR) << "deletePath stat failed for " << path;
+		return false;
+	}
+	if (success == -1)
+		PLOG(ERROR) << "Deleting path " << path << " failed";
+	access_ok(path);
+	return success == 0;
+}
+
+int renameTo(const char *oldPath, const char *newPath) {
+	int ret = rename(oldPath, newPath);
+	access_ok(oldPath);
+	access_ok(newPath);
+	return ret;
+}
+
+// Calls access(2) on the path to update underlying filesystems,
+// then closes the fd.
+void closeObjFd(int fd, const char *path) {
+	close(fd);
+	access_ok(path);
+}
diff --git a/mtp/ffs/MtpUtils.h b/mtp/ffs/MtpUtils.h
new file mode 100644
index 0000000..4eae95e
--- /dev/null
+++ b/mtp/ffs/MtpUtils.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_UTILS_H
+#define _MTP_UTILS_H
+
+#include "private/android_filesystem_config.h"
+
+#include <stdint.h>
+
+constexpr int FILE_GROUP = AID_MEDIA_RW;
+constexpr int FILE_PERM = 0664;
+constexpr int DIR_PERM = 0775;
+
+bool parseDateTime(const char* dateTime, time_t& outSeconds);
+void formatDateTime(time_t seconds, char* buffer, int bufferLength);
+
+int makeFolder(const char *path);
+int copyRecursive(const char *fromPath, const char *toPath);
+int copyFile(const char *fromPath, const char *toPath);
+bool deletePath(const char* path);
+int renameTo(const char *oldPath, const char *newPath);
+
+void closeObjFd(int fd, const char *path);
+#endif // _MTP_UTILS_H
diff --git a/mtp/ffs/NOTICE b/mtp/ffs/NOTICE
new file mode 100644
index 0000000..c5b1efa
--- /dev/null
+++ b/mtp/ffs/NOTICE
@@ -0,0 +1,190 @@
+
+   Copyright (c) 2005-2008, 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.
+
+   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.
+
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
diff --git a/mtp/ffs/PosixAsyncIO.cpp b/mtp/ffs/PosixAsyncIO.cpp
new file mode 100644
index 0000000..435000a
--- /dev/null
+++ b/mtp/ffs/PosixAsyncIO.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <condition_variable>
+#include <memory>
+#include <mutex>
+#include <queue>
+#include <unistd.h>
+
+#include "PosixAsyncIO.h"
+
+namespace {
+
+void read_func(struct aiocb *aiocbp) {
+	aiocbp->ret = TEMP_FAILURE_RETRY(pread(aiocbp->aio_fildes,
+				aiocbp->aio_buf, aiocbp->aio_nbytes, aiocbp->aio_offset));
+	if (aiocbp->ret == -1) aiocbp->error = errno;
+}
+
+void write_func(struct aiocb *aiocbp) {
+	aiocbp->ret = TEMP_FAILURE_RETRY(pwrite(aiocbp->aio_fildes,
+				aiocbp->aio_buf, aiocbp->aio_nbytes, aiocbp->aio_offset));
+	if (aiocbp->ret == -1) aiocbp->error = errno;
+}
+
+} // end anonymous namespace
+
+aiocb::~aiocb() {
+	CHECK(!thread.joinable());
+}
+
+int aio_read(struct aiocb *aiocbp) {
+	aiocbp->thread = std::thread(read_func, aiocbp);
+	return 0;
+}
+
+int aio_write(struct aiocb *aiocbp) {
+	aiocbp->thread = std::thread(write_func, aiocbp);
+	return 0;
+}
+
+int aio_error(const struct aiocb *aiocbp) {
+	return aiocbp->error;
+}
+
+ssize_t aio_return(struct aiocb *aiocbp) {
+	return aiocbp->ret;
+}
+
+int aio_suspend(struct aiocb *aiocbp[], int n,
+		const struct timespec *) {
+	for (int i = 0; i < n; i++) {
+		aiocbp[i]->thread.join();
+	}
+	return 0;
+}
+
+void aio_prepare(struct aiocb *aiocbp, void* buf, size_t count, off_t offset) {
+	aiocbp->aio_buf = buf;
+	aiocbp->aio_offset = offset;
+	aiocbp->aio_nbytes = count;
+}
diff --git a/mtp/ffs/PosixAsyncIO.h b/mtp/ffs/PosixAsyncIO.h
new file mode 100644
index 0000000..69ab9a5
--- /dev/null
+++ b/mtp/ffs/PosixAsyncIO.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _POSIXASYNCIO_H
+#define _POSIXASYNCIO_H
+
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <time.h>
+#include <thread>
+#include <unistd.h>
+
+/**
+ * Provides a subset of POSIX aio operations.
+ */
+
+struct aiocb {
+	int aio_fildes;
+	void *aio_buf;
+
+	off_t aio_offset;
+	size_t aio_nbytes;
+
+	// Used internally
+	std::thread thread;
+	ssize_t ret;
+	int error;
+
+	~aiocb();
+};
+
+// Submit a request for IO to be completed
+int aio_read(struct aiocb *);
+int aio_write(struct aiocb *);
+
+// Suspend current thread until given IO is complete, at which point
+// its return value and any errors can be accessed
+// All submitted requests must have a corresponding suspend.
+// aiocb->aio_buf must refer to valid memory until after the suspend call
+int aio_suspend(struct aiocb *[], int, const struct timespec *);
+int aio_error(const struct aiocb *);
+ssize_t aio_return(struct aiocb *);
+
+// Helper method for setting aiocb members
+void aio_prepare(struct aiocb *, void*, size_t, off_t);
+
+#endif // POSIXASYNCIO_H
+
diff --git a/mtp/ffs/btree.cpp b/mtp/ffs/btree.cpp
new file mode 100644
index 0000000..78b39a7
--- /dev/null
+++ b/mtp/ffs/btree.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/threads.h>
+#include "btree.hpp"
+#include "MtpDebug.h"
+
+Tree::Tree(MtpObjectHandle handle, MtpObjectHandle parent, const std::string& name)
+	: Node(handle, parent, name), alreadyRead(false) {
+}
+
+Tree::~Tree() {
+	for (std::map<MtpObjectHandle, Node*>::iterator it = entries.begin(); it != entries.end(); ++it)
+		delete it->second;
+	entries.clear();
+}
+
+int Tree::getCount(void) {
+	int count = entries.size();
+	MTPD("Tree::getCount::node count: %d\n", count);
+	return count;
+}
+
+void Tree::addEntry(Node* node) {
+	if (node->Mtpid() == 0) {
+		MTPE("Tree::addEntry: not adding node with 0 handle.\n");
+		return;
+	}
+	if (node->Mtpid() == node->getMtpParentId()) {
+		MTPE("Tree::addEntry: not adding node with handle %u == parent.\n", node->Mtpid());
+		return;
+	}
+	entries[node->Mtpid()] = node;
+}
+
+Node* Tree::findEntryByName(std::string name) {
+	for (std::map<MtpObjectHandle, Node*>::iterator it = entries.begin(); it != entries.end(); ++it)
+	{
+		Node* node = it->second;
+		if (node->getName().compare(name) == 0 && node->Mtpid() > 0)
+			return node;
+	}
+	return NULL;
+}
+
+Node* Tree::findNode(MtpObjectHandle handle) {
+	std::map<MtpObjectHandle, Node*>::iterator it = entries.find(handle);
+	if (it != entries.end())
+		return it->second;
+	return NULL;
+}
+
+void Tree::getmtpids(MtpObjectHandleList* mtpids) {
+	for (std::map<MtpObjectHandle, Node*>::iterator it = entries.begin(); it != entries.end(); ++it)
+		mtpids->push_back(it->second->Mtpid());
+}
+
+void Tree::deleteNode(MtpObjectHandle handle) {
+	std::map<MtpObjectHandle, Node*>::iterator it = entries.find(handle);
+	if (it != entries.end()) {
+		delete it->second;
+		entries.erase(it);
+	}
+}
diff --git a/mtp/btree.hpp b/mtp/ffs/btree.hpp
similarity index 100%
rename from mtp/btree.hpp
rename to mtp/ffs/btree.hpp
diff --git a/mtp/ffs/mtp.h b/mtp/ffs/mtp.h
new file mode 100644
index 0000000..9f6c323
--- /dev/null
+++ b/mtp/ffs/mtp.h
@@ -0,0 +1,616 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_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
+
+// Maximum buffer size for a MTP packet.
+#define MTP_BUFFER_SIZE 16384
+
+// 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_DNG									0x3811	 // Digital Negative
+#define MTP_FORMAT_HEIF									0x3812	 // HEIF images
+#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
+
+// 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
+
+// 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
+
+// MTP class reqeusts
+#define MTP_REQ_CANCEL				0x64
+#define MTP_REQ_GET_EXT_EVENT_DATA	0x65
+#define MTP_REQ_RESET				0x66
+#define MTP_REQ_GET_DEVICE_STATUS	0x67
+
+#endif // _MTP_H
diff --git a/mtp/ffs/mtp_MtpDatabase.cpp b/mtp/ffs/mtp_MtpDatabase.cpp
new file mode 100755
index 0000000..3007698
--- /dev/null
+++ b/mtp/ffs/mtp_MtpDatabase.cpp
@@ -0,0 +1,850 @@
+/*
+ * 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 <assert.h>
+#include <cutils/properties.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <limits.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <map>
+#include <string>
+
+#include "MtpDataPacket.h"
+#include "MtpDatabase.h"
+#include "MtpDebug.h"
+#include "MtpObjectInfo.h"
+#include "MtpProperty.h"
+#include "MtpStorage.h"
+#include "MtpStringBuffer.h"
+#include "MtpUtils.h"
+#include "mtp.h"
+#include "mtp_MtpDatabase.hpp"
+
+IMtpDatabase::IMtpDatabase() {
+  storagenum = 0;
+  count = -1;
+}
+
+IMtpDatabase::~IMtpDatabase() {
+  std::map<int, MtpStorage*>::iterator i;
+  for (i = storagemap.begin(); i != storagemap.end(); i++) {
+	delete i->second;
+  }
+}
+
+int IMtpDatabase::DEVICE_PROPERTIES[3] = { MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER,
+										   MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME,
+										   MTP_DEVICE_PROPERTY_IMAGE_SIZE };
+
+int IMtpDatabase::FILE_PROPERTIES[10] = {
+  // NOTE must match beginning of AUDIO_PROPERTIES, VIDEO_PROPERTIES
+  // and IMAGE_PROPERTIES below
+  MTP_PROPERTY_STORAGE_ID, MTP_PROPERTY_OBJECT_FORMAT, MTP_PROPERTY_PROTECTION_STATUS,
+  MTP_PROPERTY_OBJECT_SIZE, MTP_PROPERTY_OBJECT_FILE_NAME, MTP_PROPERTY_DATE_MODIFIED,
+  MTP_PROPERTY_PARENT_OBJECT, MTP_PROPERTY_PERSISTENT_UID, MTP_PROPERTY_NAME,
+  // TODO: why is DISPLAY_NAME not here?
+  MTP_PROPERTY_DATE_ADDED
+};
+
+int IMtpDatabase::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 IMtpDatabase::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 IMtpDatabase::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 IMtpDatabase::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 IMtpDatabase::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 IMtpDatabase::beginSendObject(const char* path, MtpObjectFormat format,
+											  MtpObjectHandle parent, MtpStorageID storageID,
+											  uint64_t size, time_t modified) {
+  if (storagemap.find(storageID) == storagemap.end()) return kInvalidObjectHandle;
+  return storagemap[storageID]->beginSendObject(path, format, parent, size, modified);
+}
+
+void IMtpDatabase::endSendObject(const char* path, MtpObjectHandle handle, MtpObjectFormat format,
+								 bool succeeded) {
+  MTPD("endSendObject() %s\n", path);
+  if (!succeeded) {
+	MTPE("endSendObject() failed, unlinking %s\n", path);
+	unlink(path);
+  }
+  std::map<int, MtpStorage*>::iterator storit;
+  for (storit = storagemap.begin(); storit != storagemap.end(); storit++)
+	storit->second->endSendObject(path, handle, format, succeeded);
+}
+
+void IMtpDatabase::createDB(MtpStorage* storage, MtpStorageID storageID) {
+  storagemap[storageID] = storage;
+  storage->createDB();
+}
+
+void IMtpDatabase::destroyDB(MtpStorageID storageID) {
+  MtpStorage* storage = storagemap[storageID];
+  storagemap.erase(storageID);
+  delete storage;
+}
+
+MtpObjectHandleList* IMtpDatabase::getObjectList(MtpStorageID storageID,
+												 __attribute__((unused)) MtpObjectFormat format,
+												 MtpObjectHandle parent) {
+  MTPD("IMtpDatabase::getObjectList::storageID: %d\n", storageID);
+  MtpObjectHandleList* list = storagemap[storageID]->getObjectList(storageID, parent);
+  MTPD("IMtpDatabase::getObjectList::list size: %d\n", list->size());
+  return list;
+}
+
+int IMtpDatabase::getNumObjects(MtpStorageID storageID,
+								__attribute__((unused)) MtpObjectFormat format,
+								MtpObjectHandle parent) {
+  MtpObjectHandleList* list = storagemap[storageID]->getObjectList(storageID, parent);
+  int size = list->size();
+  delete list;
+  return size;
+}
+
+MtpObjectFormatList* IMtpDatabase::getSupportedPlaybackFormats() {
+  // This function tells the host PC which file formats the device supports
+  MtpObjectFormatList* list = new MtpObjectFormatList();
+  int length = sizeof(SUPPORTED_PLAYBACK_FORMATS) / sizeof(SUPPORTED_PLAYBACK_FORMATS[0]);
+  MTPD("IMtpDatabase::getSupportedPlaybackFormats length: %i\n", length);
+  for (int i = 0; i < length; i++) {
+	MTPD("supported playback format: %x\n", SUPPORTED_PLAYBACK_FORMATS[i]);
+	list->push_back(SUPPORTED_PLAYBACK_FORMATS[i]);
+  }
+  return list;
+}
+
+MtpObjectFormatList* IMtpDatabase::getSupportedCaptureFormats() {
+  // Android OS implementation of this function returns NULL
+  // so we are not implementing this function either.
+  MTPD(
+	  "IMtpDatabase::getSupportedCaptureFormats returning NULL (This is what Android does as "
+	  "well).\n");
+  return NULL;
+}
+
+MtpObjectPropertyList* IMtpDatabase::getSupportedObjectProperties(MtpObjectFormat format) {
+  int* properties;
+  MtpObjectPropertyList* list = new MtpObjectPropertyList();
+  int length = 0;
+  switch (format) {
+	case MTP_FORMAT_MP3:
+	case MTP_FORMAT_WAV:
+	case MTP_FORMAT_WMA:
+	case MTP_FORMAT_OGG:
+	case MTP_FORMAT_AAC:
+	  properties = AUDIO_PROPERTIES;
+	  length = sizeof(AUDIO_PROPERTIES) / sizeof(AUDIO_PROPERTIES[0]);
+	  break;
+	case MTP_FORMAT_MPEG:
+	case MTP_FORMAT_3GP_CONTAINER:
+	case MTP_FORMAT_WMV:
+	  properties = VIDEO_PROPERTIES;
+	  length = sizeof(VIDEO_PROPERTIES) / sizeof(VIDEO_PROPERTIES[0]);
+	  break;
+	case MTP_FORMAT_EXIF_JPEG:
+	case MTP_FORMAT_GIF:
+	case MTP_FORMAT_PNG:
+	case MTP_FORMAT_BMP:
+	  properties = IMAGE_PROPERTIES;
+	  length = sizeof(IMAGE_PROPERTIES) / sizeof(IMAGE_PROPERTIES[0]);
+	  break;
+	case 0:
+	  properties = ALL_PROPERTIES;
+	  length = sizeof(ALL_PROPERTIES) / sizeof(ALL_PROPERTIES[0]);
+	  break;
+	default:
+	  properties = FILE_PROPERTIES;
+	  length = sizeof(FILE_PROPERTIES) / sizeof(FILE_PROPERTIES[0]);
+  }
+  MTPD("IMtpDatabase::getSupportedObjectProperties length is: %i, format: %x", length, format);
+  for (int i = 0; i < length; i++) {
+	MTPD("supported object property: %x\n", properties[i]);
+	list->push_back(properties[i]);
+  }
+  return list;
+}
+
+MtpDevicePropertyList* IMtpDatabase::getSupportedDeviceProperties() {
+  MtpDevicePropertyList* list = new MtpDevicePropertyList();
+  int length = sizeof(DEVICE_PROPERTIES) / sizeof(DEVICE_PROPERTIES[0]);
+  MTPD("IMtpDatabase::getSupportedDeviceProperties length was: %i\n", length);
+  for (int i = 0; i < length; i++) list->push_back(DEVICE_PROPERTIES[i]);
+  return list;
+}
+
+MtpResponseCode IMtpDatabase::getObjectPropertyValue(MtpObjectHandle handle,
+													 MtpObjectProperty property,
+													 MtpDataPacket& packet) {
+  MTPD("IMtpDatabase::getObjectPropertyValue mtpid: %u, property: %x\n", handle, property);
+  int type;
+  MtpResponseCode result = MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+  MtpStorage::PropEntry prop;
+  if (!getObjectPropertyInfo(property, type)) {
+	MTPE("IMtpDatabase::getObjectPropertyValue returning MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED\n");
+	return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+  }
+  std::map<int, MtpStorage*>::iterator storit;
+  for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+	if (storit->second->getObjectPropertyValue(handle, property, prop) == 0) {
+	  result = MTP_RESPONSE_OK;
+	  break;
+	}
+  }
+
+  if (result != MTP_RESPONSE_OK) {
+	MTPE("IMtpDatabase::getObjectPropertyValue unable to locate handle: %u\n", handle);
+	return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+  }
+
+  uint64_t longValue = prop.intvalue;
+  // special case date properties, which are strings to MTP
+  // but stored internally as a uint64
+  if (property == MTP_PROPERTY_DATE_MODIFIED || property == MTP_PROPERTY_DATE_ADDED) {
+	char date[20];
+	formatDateTime(longValue, date, sizeof(date));
+	packet.putString(date);
+	goto out;
+  }
+
+  switch (type) {
+	case MTP_TYPE_INT8:
+	  packet.putInt8(longValue);
+	  break;
+	case MTP_TYPE_UINT8:
+	  packet.putUInt8(longValue);
+	  break;
+	case MTP_TYPE_INT16:
+	  packet.putInt16(longValue);
+	  break;
+	case MTP_TYPE_UINT16:
+	  packet.putUInt16(longValue);
+	  break;
+	case MTP_TYPE_INT32:
+	  packet.putInt32(longValue);
+	  break;
+	case MTP_TYPE_UINT32:
+	  packet.putUInt32(longValue);
+	  break;
+	case MTP_TYPE_INT64:
+	  packet.putInt64(longValue);
+	  break;
+	case MTP_TYPE_UINT64:
+	  packet.putUInt64(longValue);
+	  break;
+	case MTP_TYPE_INT128:
+	  packet.putInt128(longValue);
+	  break;
+	case MTP_TYPE_UINT128:
+	  packet.putUInt128(longValue);
+	  break;
+	case MTP_TYPE_STR: {
+	  /*std::string stringValue = (string)stringValuesArray[0];
+	  if (stringValue) {
+		const char* str = stringValue.c_str();
+		if (str == NULL) {
+		  return MTP_RESPONSE_GENERAL_ERROR;
+		}
+		packet.putString(str);
+	  } else {
+		packet.putEmptyString();
+	  }*/
+	  packet.putString(prop.strvalue.c_str());
+	  MTPD("MTP_TYPE_STR: %x = %s\n", prop.property, prop.strvalue.c_str());
+	  // MTPE("STRING unsupported type in getObjectPropertyValue\n");
+	  // result = MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
+	  break;
+	}
+	default:
+	  MTPE("unsupported type in getObjectPropertyValue\n");
+	  result = MTP_RESPONSE_INVALID_OBJECT_PROP_FORMAT;
+  }
+out:
+  return result;
+}
+
+MtpResponseCode IMtpDatabase::setObjectPropertyValue(MtpObjectHandle handle,
+													 MtpObjectProperty property,
+													 MtpDataPacket& packet) {
+  int type;
+  MTPD("IMtpDatabase::setObjectPropertyValue start\n");
+  if (!getObjectPropertyInfo(property, type)) {
+	MTPE("IMtpDatabase::setObjectPropertyValue returning MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED\n");
+	return MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+  }
+  MTPD("IMtpDatabase::setObjectPropertyValue continuing\n");
+
+  int8_t int8_t_value;
+  uint8_t uint8_t_value;
+  int16_t int16_t_value;
+  uint16_t uint16_t_value;
+  int32_t int32_t_value;
+  uint32_t uint32_t_value;
+  int64_t int64_t_value;
+  uint64_t uint64_t_value;
+  std::string stringValue;
+
+  switch (type) {
+	case MTP_TYPE_INT8:
+	  MTPD("int8\n");
+	  packet.getInt8(int8_t_value);
+	  break;
+	case MTP_TYPE_UINT8:
+	  MTPD("uint8\n");
+	  packet.getUInt8(uint8_t_value);
+	  break;
+	case MTP_TYPE_INT16:
+	  MTPD("int16\n");
+	  packet.getInt16(int16_t_value);
+	  break;
+	case MTP_TYPE_UINT16:
+	  MTPD("uint16\n");
+	  packet.getUInt16(uint16_t_value);
+	  break;
+	case MTP_TYPE_INT32:
+	  MTPD("int32\n");
+	  packet.getInt32(int32_t_value);
+	  break;
+	case MTP_TYPE_UINT32:
+	  MTPD("uint32\n");
+	  packet.getUInt32(uint32_t_value);
+	  break;
+	case MTP_TYPE_INT64:
+	  MTPD("int64\n");
+	  packet.getInt64(int64_t_value);
+	  break;
+	case MTP_TYPE_UINT64:
+	  MTPD("uint64\n");
+	  packet.getUInt64(uint64_t_value);
+	  break;
+	case MTP_TYPE_STR: {
+	  MTPD("string\n");
+	  MtpStringBuffer buffer;
+	  packet.getString(buffer);
+	  stringValue = buffer;
+	  break;
+	}
+	default:
+	  MTPE("IMtpDatabase::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("IMtpDatabase::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("IMtpDatabase::setObjectPropertyValue property %x not supported.\n", property);
+	  result = MTP_RESPONSE_OBJECT_PROP_NOT_SUPPORTED;
+  }
+  MTPD("IMtpDatabase::setObjectPropertyValue returning %d\n", result);
+  return result;
+}
+
+MtpResponseCode IMtpDatabase::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("IMtpDatabase::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("IMtpDatabase::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("IMtpDatabase::getDevicePropertyValue unsupported type %i in getDevicePropertyValue\n",
+		   type);
+	  return MTP_RESPONSE_INVALID_DEVICE_PROP_FORMAT;
+  }
+
+  return MTP_RESPONSE_OK;
+}
+
+MtpResponseCode IMtpDatabase::setDevicePropertyValue(__attribute__((unused))
+													 MtpDeviceProperty property,
+													 __attribute__((unused))
+													 MtpDataPacket& packet) {
+  MTPE("IMtpDatabase::setDevicePropertyValue not implemented, returning 0\n");
+  return 0;
+}
+
+MtpResponseCode IMtpDatabase::resetDeviceProperty(__attribute__((unused))
+												  MtpDeviceProperty property) {
+  MTPE("IMtpDatabase::resetDeviceProperty not implemented, returning -1\n");
+  return -1;
+}
+
+MtpResponseCode IMtpDatabase::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("IMtpDatabase::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("IMtpDatabase::getObjectPropertyList MTP_RESPOSNE_INVALID_OBJECT_HANDLE %i\n", handle);
+  return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+}
+
+MtpResponseCode IMtpDatabase::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("IMtpDatabase::getObjectInfo MTP_RESPONSE_INVALID_OBJECT_HANDLE %i\n", handle);
+  return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+}
+
+void* IMtpDatabase::getThumbnail(__attribute__((unused)) MtpObjectHandle handle,
+								 __attribute__((unused)) size_t& outThumbSize) {
+  MTPE("IMtpDatabase::getThumbnail not implemented, returning 0\n");
+  return 0;
+}
+
+MtpResponseCode IMtpDatabase::getObjectFilePath(MtpObjectHandle handle,
+												MtpStringBuffer& outFilePath,
+												int64_t& outFileLength,
+												MtpObjectFormat& outFormat) {
+  std::map<int, MtpStorage*>::iterator storit;
+  for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+	MTPD("IMtpDatabase::getObjectFilePath calling getObjectFilePath\n");
+	if (storit->second->getObjectFilePath(handle, outFilePath, outFileLength, outFormat) == 0) {
+	  MTPD("MTP_RESPONSE_OK\n");
+	  return MTP_RESPONSE_OK;
+	}
+  }
+  MTPE("IMtpDatabase::getObjectFilePath MTP_RESPOSNE_INVALID_OBJECT_HANDLE %i\n", handle);
+  return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+}
+
+// MtpResponseCode IMtpDatabase::deleteFile(MtpObjectHandle handle) {
+//	MTPD("IMtpDatabase::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("IMtpDatabase::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 IMtpDatabase::getObjectPropertyInfo(MtpObjectProperty property, int& type) {
+  int count = sizeof(kObjectPropertyTable) / sizeof(kObjectPropertyTable[0]);
+  const PropertyTableEntry* entry = kObjectPropertyTable;
+  MTPD("IMtpDatabase::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 IMtpDatabase::getDevicePropertyInfo(MtpDeviceProperty property, int& type) {
+  int count = sizeof(kDevicePropertyTable) / sizeof(kDevicePropertyTable[0]);
+  const PropertyTableEntry* entry = kDevicePropertyTable;
+  MTPD("IMtpDatabase::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* IMtpDatabase::getObjectReferences(MtpObjectHandle handle) {
+  // call function and place files with associated handles into int array
+  MTPD(
+	  "IMtpDatabase::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 IMtpDatabase::setObjectReferences(__attribute__((unused)) MtpObjectHandle handle,
+												  __attribute__((unused))
+												  MtpObjectHandleList* references) {
+  MTPE("IMtpDatabase::setObjectReferences not implemented, returning 0\n");
+  return 0;
+}
+
+MtpProperty* IMtpDatabase::getObjectPropertyDesc(MtpObjectProperty property,
+												 MtpObjectFormat format) {
+  MTPD("IMtpDatabase::getObjectPropertyDesc start\n");
+  MtpProperty* result = NULL;
+  switch (property) {
+	case MTP_PROPERTY_OBJECT_FORMAT:
+	  // use format as default value
+	  result = new MtpProperty(property, MTP_TYPE_UINT16, false, format);
+	  break;
+	case MTP_PROPERTY_PROTECTION_STATUS:
+	case MTP_PROPERTY_TRACK:
+	  result = new MtpProperty(property, MTP_TYPE_UINT16);
+	  break;
+	case MTP_PROPERTY_STORAGE_ID:
+	case MTP_PROPERTY_PARENT_OBJECT:
+	case MTP_PROPERTY_DURATION:
+	  result = new MtpProperty(property, MTP_TYPE_UINT32);
+	  break;
+	case MTP_PROPERTY_OBJECT_SIZE:
+	  result = new MtpProperty(property, MTP_TYPE_UINT64);
+	  break;
+	case MTP_PROPERTY_PERSISTENT_UID:
+	  result = new MtpProperty(property, MTP_TYPE_UINT128);
+	  break;
+	case MTP_PROPERTY_NAME:
+	case MTP_PROPERTY_DISPLAY_NAME:
+	case MTP_PROPERTY_ARTIST:
+	case MTP_PROPERTY_ALBUM_NAME:
+	case MTP_PROPERTY_ALBUM_ARTIST:
+	case MTP_PROPERTY_GENRE:
+	case MTP_PROPERTY_COMPOSER:
+	case MTP_PROPERTY_DESCRIPTION:
+	  result = new MtpProperty(property, MTP_TYPE_STR);
+	  break;
+	case MTP_PROPERTY_DATE_MODIFIED:
+	case MTP_PROPERTY_DATE_ADDED:
+	case MTP_PROPERTY_ORIGINAL_RELEASE_DATE:
+	  result = new MtpProperty(property, MTP_TYPE_STR);
+	  result->setFormDateTime();
+	  break;
+	case MTP_PROPERTY_OBJECT_FILE_NAME:
+	  // We allow renaming files and folders
+	  result = new MtpProperty(property, MTP_TYPE_STR, true);
+	  break;
+  }
+  return result;
+}
+
+MtpProperty* IMtpDatabase::getDevicePropertyDesc(MtpDeviceProperty property) {
+  MtpProperty* result = NULL;
+  bool writable = false;
+  switch (property) {
+	case MTP_DEVICE_PROPERTY_SYNCHRONIZATION_PARTNER:
+	case MTP_DEVICE_PROPERTY_DEVICE_FRIENDLY_NAME:
+	  writable = true;
+	  // fall through
+	case MTP_DEVICE_PROPERTY_IMAGE_SIZE:
+	  result = new MtpProperty(property, MTP_TYPE_STR, writable);
+
+	  // get current value
+	  // TODO: add actual values
+	  result->setCurrentValue(0);
+	  result->setDefaultValue(0);
+	  break;
+  }
+
+  return result;
+}
+
+void IMtpDatabase::sessionStarted() {
+  MTPD("IMtpDatabase::sessionStarted not implemented or does nothing, returning\n");
+  return;
+}
+
+void IMtpDatabase::sessionEnded() {
+  MTPD("IMtpDatabase::sessionEnded not implemented or does nothing, returning\n");
+  return;
+}
+
+// ----------------------------------------------------------------------------
+
+void IMtpDatabase::lockMutex(void) {
+  std::map<int, MtpStorage*>::iterator storit;
+  for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+	storit->second->lockMutex(0);
+  }
+}
+
+void IMtpDatabase::unlockMutex(void) {
+  std::map<int, MtpStorage*>::iterator storit;
+  for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+	storit->second->unlockMutex(0);
+  }
+}
+
+MtpResponseCode IMtpDatabase::beginDeleteObject(MtpObjectHandle handle) {
+  MTPD("IMtoDatabase::beginDeleteObject handle: %u\n", handle);
+  std::map<int, MtpStorage*>::iterator storit;
+  for (storit = storagemap.begin(); storit != storagemap.end(); storit++) {
+	if (storit->second->deleteFile(handle) == 0) {
+	  MTPD("IMtpDatabase::beginDeleteObject::MTP_RESPONSE_OK\n");
+	  return MTP_RESPONSE_OK;
+	}
+  }
+  return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+}
+
+void IMtpDatabase::endDeleteObject(MtpObjectHandle handle __unused, bool succeeded __unused) {
+  MTPD("IMtpDatabase::endDeleteObject not implemented yet\n");
+}
+
+void IMtpDatabase::rescanFile(const char* path __unused, MtpObjectHandle handle __unused,
+							  MtpObjectFormat format __unused) {
+  MTPD("IMtpDatabase::rescanFile not implemented yet\n");
+}
+
+MtpResponseCode IMtpDatabase::beginMoveObject(MtpObjectHandle handle __unused,
+											  MtpObjectHandle newParent __unused,
+											  MtpStorageID newStorage __unused) {
+  MTPD("IMtpDatabase::beginMoveObject not implemented yet\n");
+  return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+}
+
+void IMtpDatabase::endMoveObject(MtpObjectHandle oldParent __unused,
+								 MtpObjectHandle newParent __unused,
+								 MtpStorageID oldStorage __unused, MtpStorageID newStorage __unused,
+								 MtpObjectHandle handle __unused, bool succeeded __unused) {
+  MTPD("IMtpDatabase::endMoveObject not implemented yet\n");
+}
+
+MtpResponseCode IMtpDatabase::beginCopyObject(MtpObjectHandle handle __unused,
+											  MtpObjectHandle newParent __unused,
+											  MtpStorageID newStorage __unused) {
+  MTPD("IMtpDatabase::beginCopyObject not implemented yet\n");
+  return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+}
+
+void IMtpDatabase::endCopyObject(MtpObjectHandle handle __unused, bool succeeded __unused) {
+  MTPD("IMtpDatabase::endCopyObject not implemented yet\n");
+}
diff --git a/mtp/ffs/mtp_MtpDatabase.hpp b/mtp/ffs/mtp_MtpDatabase.hpp
new file mode 100755
index 0000000..4b18898
--- /dev/null
+++ b/mtp/ffs/mtp_MtpDatabase.hpp
@@ -0,0 +1,163 @@
+/*
+ * 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 IMtpDatabase : public MtpDatabase {
+private:
+	int* getSupportedObjectProperties(int format);
+
+	static int FILE_PROPERTIES[10];
+	static int DEVICE_PROPERTIES[3];
+	static int AUDIO_PROPERTIES[19];
+	static int VIDEO_PROPERTIES[15];
+	static int IMAGE_PROPERTIES[12];
+	static int ALL_PROPERTIES[25];
+	static int SUPPORTED_PLAYBACK_FORMATS[26];
+	int storagenum;
+	int count;
+	std::string lastfile;
+	std::map<int, MtpStorage*> storagemap;
+	void countDirs(std::string path);
+	int readParentDirs(std::string path, int storageID);
+
+public:
+									IMtpDatabase();
+	virtual							~IMtpDatabase();
+
+	virtual void					createDB(MtpStorage* storage, MtpStorageID storageID);
+	virtual void					destroyDB(MtpStorageID storageID);
+	virtual MtpObjectHandle			beginSendObject(const char* path,
+											MtpObjectFormat format,
+											MtpObjectHandle parent,
+											MtpStorageID storageID,
+											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,
+											MtpStringBuffer& 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();
+
+	virtual MtpResponseCode			beginDeleteObject(MtpObjectHandle handle);
+	virtual void					endDeleteObject(MtpObjectHandle handle, bool succeeded);
+	// Called to rescan a file, such as after an edit.
+	virtual void					rescanFile(const char* path,
+											MtpObjectHandle handle,
+											MtpObjectFormat format);
+	virtual MtpResponseCode			beginMoveObject(MtpObjectHandle handle, MtpObjectHandle newParent,
+											MtpStorageID newStorage);
+
+	virtual void					endMoveObject(MtpObjectHandle oldParent, MtpObjectHandle newParent,
+											MtpStorageID oldStorage, MtpStorageID newStorage,
+											MtpObjectHandle handle, bool succeeded);
+
+	virtual MtpResponseCode			beginCopyObject(MtpObjectHandle handle, MtpObjectHandle newParent,
+											MtpStorageID newStorage);
+	virtual void					endCopyObject(MtpObjectHandle handle, bool succeeded);
+};
+#endif
diff --git a/mtp/ffs/mtp_MtpServer.cpp b/mtp/ffs/mtp_MtpServer.cpp
new file mode 100755
index 0000000..ce890d1
--- /dev/null
+++ b/mtp/ffs/mtp_MtpServer.cpp
@@ -0,0 +1,226 @@
+/*
+ * 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.
+ *
+ * Additional Copyright (C) 2018 TeamWin
+ */
+
+#include <utils/Log.h>
+
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <vector>
+#include <utils/threads.h>
+#include <pthread.h>
+#include <cutils/properties.h>
+
+#include "mtp_MtpServer.hpp"
+#include "MtpServer.h"
+#include "MtpStorage.h"
+#include "MtpDebug.h"
+#include "MtpDescriptors.h"
+#include "MtpMessage.hpp"
+#include "mtp_MtpDatabase.hpp"
+
+#include <string>
+
+void twmtp_MtpServer::set_device_info() {
+	char property[512];
+	property_get("ro.build.product", property, "unknown manufacturer");
+	mtpinfo.deviceInfoManufacturer = MtpStringBuffer(property);
+	property_get("ro.product.model", property, "unknown model");
+	mtpinfo.deviceInfoModel = MtpStringBuffer(property);
+	mtpinfo.deviceInfoDeviceVersion = MtpStringBuffer("None");
+	property_get("ro.serialno", property, "unknown serial number");
+	mtpinfo.deviceInfoSerialNumber = MtpStringBuffer(property);
+}
+
+void twmtp_MtpServer::start()
+{
+	int controlFd = 0;
+
+	usePtp =  false;
+	IMtpDatabase* mtpdb = new IMtpDatabase();
+	MTPD("launching server\n");
+		/* Sleep for a bit before we open the MTP USB device because some
+		 * devices are not ready due to the kernel not responding to our
+		 * sysfs requests right away.
+		 */
+		usleep(800000);
+#ifdef USB_MTP_DEVICE
+#define STRINGIFY(x) #x
+#define EXPAND(x) STRINGIFY(x)
+		const char* mtp_device = EXPAND(USB_MTP_DEVICE);
+		MTPI("Using '%s' for MTP device.\n", EXPAND(USB_MTP_DEVICE));
+#else
+		const char* mtp_device = "/dev/mtp_usb";
+#endif
+	bool ffs_ok = access(FFS_MTP_EP0, W_OK) == 0;
+	if (ffs_ok) {
+		MTPD("Opening FFS EPO\n");
+		controlFd = open(FFS_MTP_EP0, O_RDWR);
+	} else {
+		controlFd = open(mtp_device, O_WRONLY);
+	}
+	if (controlFd < 0) {
+		MTPE("could not open MTP driver, errno: %d\n", errno);
+		return;
+	}
+		MTPD("MTP fd: %d\n", controlFd);
+
+	server = new MtpServer(mtpdb,\
+		controlFd,\
+		usePtp,\
+		mtpinfo.deviceInfoManufacturer, \
+		mtpinfo.deviceInfoModel, \
+		mtpinfo.deviceInfoDeviceVersion, \
+		mtpinfo.deviceInfoSerialNumber);
+	refserver = server;
+	MTPI("created new mtpserver object\n");
+	add_storage();
+	MTPD("Starting add / remove mtppipe monitor thread\n");
+	pthread_t thread;
+	ThreadPtr mtpptr = &twmtp_MtpServer::mtppipe_thread;
+	PThreadPtr p = *(PThreadPtr*)&mtpptr;
+	pthread_create(&thread, NULL, p, this);
+	// This loop restarts the MTP process if the device is unplugged and replugged in
+	while (true) {
+		server->run();
+		usleep(800000);
+	}
+}
+
+void twmtp_MtpServer::set_storages(storages* mtpstorages) {
+	stores = mtpstorages;
+}
+
+void twmtp_MtpServer::cleanup()
+{
+	android::Mutex sMutex;
+	android::Mutex::Autolock autoLock(sMutex);
+
+	if (server) {
+		delete server;
+	} else {
+		MTPD("server is null in cleanup");
+	}
+}
+
+void twmtp_MtpServer::send_object_added(int handle)
+{
+	android::Mutex sMutex;
+	android::Mutex::Autolock autoLock(sMutex);
+
+	if (server)
+		server->sendObjectAdded(handle);
+	else
+		MTPD("server is null in send_object_added");
+}
+
+void twmtp_MtpServer::send_object_removed(int handle)
+{
+	android::Mutex sMutex;
+	android::Mutex::Autolock autoLock(sMutex);
+
+	if (server)
+		server->sendObjectRemoved(handle);
+	else
+		MTPD("server is null in send_object_removed");
+}
+
+void twmtp_MtpServer::add_storage()
+{
+	android::Mutex sMutex;
+	android::Mutex::Autolock autoLock(sMutex);
+
+	MTPD("twmtp_MtpServer::add_storage count of storage devices: %i\n", stores->size());
+	for (unsigned int i = 0; i < stores->size(); ++i) {
+			std::string pathStr = stores->at(i)->mount;
+
+			if (!pathStr.empty()) {
+				std::string descriptionStr = stores->at(i)->display;
+				int storageID = stores->at(i)->mtpid;
+				bool removable = false;
+				uint64_t maxFileSize = stores->at(i)->maxFileSize;
+				if (descriptionStr != "") {
+					MtpStorage* storage = new MtpStorage(storageID, &pathStr[0], &descriptionStr[0], removable, maxFileSize, refserver);
+					server->addStorage(storage);
+				}
+		}
+	}
+}
+
+void twmtp_MtpServer::remove_storage(int storageId)
+{
+	android::Mutex sMutex;
+	android::Mutex::Autolock autoLock(sMutex);
+
+	if (server) {
+		MtpStorage* storage = server->getStorage(storageId);
+		if (storage) {
+			MTPD("twmtp_MtpServer::remove_storage calling removeStorage\n");
+			server->removeStorage(storage);
+		}
+	} else
+		MTPD("server is null in remove_storage");
+	MTPD("twmtp_MtpServer::remove_storage DONE\n");
+}
+
+int twmtp_MtpServer::mtppipe_thread(void)
+{
+	if (mtp_read_pipe == -1) {
+		MTPD("mtppipe_thread exiting because mtp_read_pipe not set\n");
+		return 0;
+	}
+	MTPD("Starting twmtp_MtpServer::mtppipe_thread\n");
+	int read_count;
+	struct mtpmsg mtp_message;
+	while (1) {
+		read_count = ::read(mtp_read_pipe, &mtp_message, sizeof(mtp_message));
+		MTPD("read %i from mtppipe\n", read_count);
+		if (read_count == sizeof(mtp_message)) {
+			if (mtp_message.message_type == MTP_MESSAGE_ADD_STORAGE) {
+				MTPI("mtppipe add storage %i '%s'\n", mtp_message.storage_id, mtp_message.path);
+				if (mtp_message.storage_id) {
+					bool removable = false;
+					MtpStorage* storage = new MtpStorage(mtp_message.storage_id, &mtp_message.path[0], &mtp_message.display[0], removable, mtp_message.maxFileSize, refserver);
+					server->addStorage(storage);
+					MTPD("mtppipe done adding storage\n");
+				} else {
+					MTPE("Invalid storage ID %i specified\n", mtp_message.storage_id);
+				}
+			} else if (mtp_message.message_type == MTP_MESSAGE_REMOVE_STORAGE) {
+				MTPI("mtppipe remove storage %i\n", mtp_message.storage_id);
+				remove_storage(mtp_message.storage_id);
+				MTPD("mtppipe done removing storage\n");
+			} else {
+				MTPE("Unknown mtppipe message value: %i\n", mtp_message.message_type);
+			}
+		} else {
+			MTPE("twmtp_MtpServer::mtppipe_thread unexpected read_count %i\n", read_count);
+			close(mtp_read_pipe);
+			break;
+		}
+	}
+	MTPD("twmtp_MtpServer::mtppipe_thread closing\n");
+	return 0;
+}
+
+void twmtp_MtpServer::set_read_pipe(int pipe)
+{
+	mtp_read_pipe = pipe;
+}
diff --git a/mtp/ffs/mtp_MtpServer.hpp b/mtp/ffs/mtp_MtpServer.hpp
new file mode 100644
index 0000000..f2dc4cd
--- /dev/null
+++ b/mtp/ffs/mtp_MtpServer.hpp
@@ -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_MTPSERVER_HPP
+#define MTP_MTPSERVER_HPP
+#include <utils/Log.h>
+
+#include <string>
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <utils/threads.h>
+
+#include "MtpServer.h"
+#include "MtpStorage.h"
+#include "mtp_MtpDatabase.hpp"
+
+typedef struct Storage {
+	std::string display;
+	std::string mount;
+	int mtpid;
+	uint64_t maxFileSize;
+} storage;
+
+typedef std::vector<storage*> storages;
+
+struct mtp_info {
+	MtpStringBuffer deviceInfoManufacturer;
+	MtpStringBuffer deviceInfoModel;
+	MtpStringBuffer deviceInfoDeviceVersion;
+	MtpStringBuffer deviceInfoSerialNumber;
+};
+
+class twmtp_MtpServer {
+	public:
+		void start();
+		void cleanup();
+		void send_object_added(int handle);
+		void send_object_removed(int handle);
+		void add_storage();
+		void remove_storage(int storageId);
+		void set_storages(storages* mtpstorages);
+		void set_read_pipe(int pipe);
+		storages *stores;
+		struct mtp_info mtpinfo;
+		void set_device_info();
+
+	private:
+		typedef int (twmtp_MtpServer::*ThreadPtr)(void);
+		typedef void* (*PThreadPtr)(void *);
+		int mtppipe_thread(void);
+		bool usePtp;
+		MtpServer* server;
+		MtpServer* refserver;
+		int mtp_read_pipe;
+                MtpStringBuffer deviceInfoManufacturer;
+                MtpStringBuffer deviceInfoModel;
+                MtpStringBuffer deviceInfoDeviceVersion;
+                MtpStringBuffer deviceInfoSerialNumber;
+};
+#endif
diff --git a/mtp/ffs/node.cpp b/mtp/ffs/node.cpp
new file mode 100644
index 0000000..7c57dc6
--- /dev/null
+++ b/mtp/ffs/node.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+#include <sys/stat.h>
+#include <sys/statfs.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <limits.h>
+#include <errno.h>
+#include <string.h>
+#include <stdio.h>
+#include <libgen.h>
+
+#include "btree.hpp"
+#include "mtp.h"
+#include "MtpDebug.h"
+
+
+Node::Node()
+	: handle(-1), parent(0), name("")
+{
+}
+
+Node::Node(MtpObjectHandle handle, MtpObjectHandle parent, const std::string& name)
+	: handle(handle), parent(parent), name(name)
+{
+				MTPD("handle: %d\n", handle);
+				MTPD("parent: %d\n", parent);
+				MTPD("name: %s\n", name.c_str());
+}
+
+void Node::rename(const std::string& newName) {
+	name = newName;
+	updateProperty(MTP_PROPERTY_OBJECT_FILE_NAME, 0, name.c_str(), MTP_TYPE_STR);
+	updateProperty(MTP_PROPERTY_NAME, 0, name.c_str(), MTP_TYPE_STR);
+	updateProperty(MTP_PROPERTY_DISPLAY_NAME, 0, name.c_str(), MTP_TYPE_STR);
+}
+
+MtpObjectHandle Node::Mtpid() const { return handle; }
+MtpObjectHandle Node::getMtpParentId() const { return parent; }
+const std::string& Node::getName() const { return name; }
+
+uint64_t Node::getIntProperty(MtpPropertyCode property) {
+	for (unsigned index = 0; index < mtpProp.size(); ++index) {
+		if (mtpProp[index].property == property)
+			return mtpProp[index].valueInt;
+	}
+	MTPE("Node::getIntProperty failed to find property %x, returning -1\n", (unsigned)property);
+	return -1;
+}
+
+const Node::mtpProperty& Node::getProperty(MtpPropertyCode property) {
+	static const mtpProperty dummyProp;
+	for (size_t i = 0; i < mtpProp.size(); ++i) {
+		if (mtpProp[i].property == property)
+			return mtpProp[i];
+	}
+	MTPE("Node::getProperty failed to find property %x, returning dummy property\n", (unsigned)property);
+	return dummyProp;
+}
+
+void Node::addProperty(MtpPropertyCode property, uint64_t valueInt, std::string valueStr, MtpDataType dataType) {
+//	MTPD("adding property: %lld, valueInt: %lld, valueStr: %s, dataType: %d\n", property, valueInt, valueStr.c_str(), dataType);
+	struct mtpProperty prop;
+	prop.property = property;
+	prop.valueInt = valueInt;
+	prop.valueStr = valueStr;
+	prop.dataType = dataType;
+	mtpProp.push_back(prop);
+}
+
+void Node::updateProperty(MtpPropertyCode property, uint64_t valueInt, std::string valueStr, MtpDataType dataType) {
+	for (unsigned i = 0; i < mtpProp.size(); i++) {
+		if (mtpProp[i].property == property) {
+			mtpProp[i].valueInt = valueInt;
+			mtpProp[i].valueStr = valueStr;
+			mtpProp[i].dataType = dataType;
+			return;
+		}
+	}
+	addProperty(property, valueInt, valueStr, dataType);
+}
+
+std::vector<Node::mtpProperty>& Node::getMtpProps() {
+	return mtpProp;
+}
+
+void Node::addProperties(const std::string& path, int storageID) {
+	MTPD("addProperties: handle: %u, filename: '%s'\n", handle, getName().c_str());
+	struct stat st;
+	int mFormat = 0;
+	uint64_t puid = ((uint64_t)storageID << 32) + handle;
+	off_t file_size = 0;
+
+	mFormat = MTP_FORMAT_UNDEFINED;   // file
+	if (lstat(path.c_str(), &st) == 0) {
+		file_size = st.st_size;
+		if (S_ISDIR(st.st_mode))
+			mFormat = MTP_FORMAT_ASSOCIATION; // folder
+	}
+
+	// TODO: don't store properties with constant values at all, add them at query time instead
+	addProperty(MTP_PROPERTY_STORAGE_ID, storageID, "", MTP_TYPE_UINT32);
+	addProperty(MTP_PROPERTY_OBJECT_FORMAT, mFormat, "", MTP_TYPE_UINT16);
+	addProperty(MTP_PROPERTY_PROTECTION_STATUS, 0, "", MTP_TYPE_UINT16);
+	addProperty(MTP_PROPERTY_OBJECT_SIZE, file_size, "", MTP_TYPE_UINT64);
+	addProperty(MTP_PROPERTY_OBJECT_FILE_NAME, 0, getName().c_str(), MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_DATE_MODIFIED, st.st_mtime, "", MTP_TYPE_UINT64);
+	addProperty(MTP_PROPERTY_PARENT_OBJECT, parent, "", MTP_TYPE_UINT32);
+	addProperty(MTP_PROPERTY_PERSISTENT_UID, puid, "", MTP_TYPE_UINT128);
+		// TODO: we can't really support persistent UIDs without a persistent DB.
+		// probably a combination of volume UUID + st_ino would come close.
+		// doesn't help for fs with no native inodes numbers like fat though...
+		// however, Microsoft's own impl (Zune, etc.) does not support persistent UIDs either
+	addProperty(MTP_PROPERTY_NAME, 0, getName().c_str(), MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_DISPLAY_NAME, 0, getName().c_str(), MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_DATE_ADDED, st.st_mtime, "", MTP_TYPE_UINT64);
+	addProperty(MTP_PROPERTY_DESCRIPTION, 0, "", MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_ARTIST, 0, "", MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_ALBUM_NAME, 0, "", MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_ALBUM_ARTIST, 0, "", MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_TRACK, 0, "", MTP_TYPE_UINT16);
+	addProperty(MTP_PROPERTY_ORIGINAL_RELEASE_DATE, 2014, "", MTP_TYPE_UINT64);	// TODO: extract year from st.st_mtime?
+	addProperty(MTP_PROPERTY_DURATION, 0, "", MTP_TYPE_UINT32);
+	addProperty(MTP_PROPERTY_GENRE, 0, "", MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_COMPOSER, 0, "", MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_ARTIST, 0, "", MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_ALBUM_NAME, 0, "", MTP_TYPE_STR);
+	addProperty(MTP_PROPERTY_DURATION, 0, "", MTP_TYPE_UINT32);
+	addProperty(MTP_PROPERTY_DESCRIPTION, 0, "", MTP_TYPE_STR);
+}
diff --git a/mtp/ffs/twrpMtp.cpp b/mtp/ffs/twrpMtp.cpp
new file mode 100644
index 0000000..d636580
--- /dev/null
+++ b/mtp/ffs/twrpMtp.cpp
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *	  http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+#include <string>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <pthread.h>
+#include <utils/Errors.h>
+#include <utils/threads.h>
+#include "MtpTypes.h"
+#include "MtpPacket.h"
+#include "MtpDataPacket.h"
+#include "MtpDatabase.h"
+#include "MtpRequestPacket.h"
+#include "MtpResponsePacket.h"
+#include "mtp_MtpDatabase.hpp"
+#include "mtp_MtpServer.hpp"
+#include "twrpMtp.hpp"
+#include "MtpDebug.h"
+
+#ifdef TWRPMTP
+static void usage(std::string prg) {
+	printf("Usage: %s <OPTIONS>\n", prg.c_str());
+	printf("Options:\n");
+	printf("\t-h, --help\t\tShow Usage\n");
+	printf("\t-s1, --storage1 /path/to/dir\t\tDestination to first storage directory\n");
+	printf("\t-s2, --storage2 /path/to/dir\t\tDestination to first storage directory\n");
+	printf("\t-sN, --storageN /path/to/dir\t\tDestination to first storage directory\n");
+}
+
+int main(int argc, char* argv[]) {
+	printf("argc: %d\n", argc);
+	if (argc < 2) {
+		usage(argv[0]);
+		return 1;
+	}
+
+	std::vector <std::string> storages;
+
+	for (int i = 1; i < argc; ++i) {
+		std::string arg = argv[i];
+		if ((arg == "-h") || (arg == "--help")) {
+			usage(argv[0]);
+		}
+		else {
+			storages.push_back(arg);
+		}
+	}
+	printf("starting\n");
+	twmtp_MtpServer* mtp = new twmtp_MtpServer();
+	mtp->set_storages(storages);
+	mtp->start();
+	return 0;
+}
+#endif //def TWRPMTP
+
+twrpMtp::twrpMtp(int debug_enabled) {
+	if (debug_enabled)
+		MtpDebug::enableDebug();
+	mtpstorages = new storages;
+	mtp_read_pipe = -1;
+}
+
+int twrpMtp::start(void) {
+	MTPI("Starting MTP\n");
+	twmtp_MtpServer *mtp = new twmtp_MtpServer();
+	mtp->set_storages(mtpstorages);
+	mtp->set_device_info();
+	mtp->set_read_pipe(mtp_read_pipe);
+	mtp->start();
+	return 0;
+}
+
+pthread_t twrpMtp::threadserver(void) {
+	pthread_t thread;
+	ThreadPtr mtpptr = &twrpMtp::start;
+	PThreadPtr p = *(PThreadPtr*)&mtpptr;
+	pthread_create(&thread, NULL, p, this);
+	return thread;
+}
+
+pid_t twrpMtp::forkserver(int mtppipe[2]) {
+	pid_t pid;
+	if ((pid = fork()) == -1) {
+		MTPE("MTP fork failed.\n");
+		return 0;
+	}
+	if (pid == 0) {
+		// Child process
+		close(mtppipe[1]); // Child closes write side
+		mtp_read_pipe = mtppipe[0];
+		start();
+		MTPD("MTP child process exited.\n");
+		close(mtppipe[0]);
+		_exit(0);
+	} else {
+		MTPD("MTP child PID: %d\n", pid);
+		return pid;
+	}
+	return 0;
+}
+
+void twrpMtp::addStorage(std::string display, std::string path, int mtpid, uint64_t maxFileSize) {
+	s = new storage;
+	s->display = display;
+	s->mount = path;
+	s->mtpid = mtpid;
+	s->maxFileSize = maxFileSize;
+	MTPD("twrpMtp mtpid: %d\n", s->mtpid);
+	mtpstorages->push_back(s);
+}
diff --git a/mtp/ffs/twrpMtp.hpp b/mtp/ffs/twrpMtp.hpp
new file mode 100644
index 0000000..2e8c2b8
--- /dev/null
+++ b/mtp/ffs/twrpMtp.hpp
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 TeamWin
+ *
+ * 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"
+#include "mtp_MtpServer.hpp"
+
+class twrpMtp {
+	public:
+		twrpMtp(int debug_enabled = 0);
+		pthread_t threadserver(void);
+		pid_t forkserver(int mtppipe[2]);
+		void addStorage(std::string display, std::string path, int mtpid, uint64_t maxFileSize);
+	private:
+		int start(void);
+		typedef int (twrpMtp::*ThreadPtr)(void);
+		typedef void* (*PThreadPtr)(void *);
+		storages *mtpstorages;
+		storage *s;
+		int mtp_read_pipe;
+};
+#endif
diff --git a/mtp/Android.mk b/mtp/legacy/Android.mk
similarity index 60%
rename from mtp/Android.mk
rename to mtp/legacy/Android.mk
index 43649f7..cd04ac0 100644
--- a/mtp/Android.mk
+++ b/mtp/legacy/Android.mk
@@ -3,7 +3,7 @@
 # Build libtwrpmtp library
 
 include $(CLEAR_VARS)
-LOCAL_MODULE := libtwrpmtp
+LOCAL_MODULE := libtwrpmtp-legacy
 LOCAL_MODULE_TAGS := optional
 LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64 -DMTP_DEVICE -DMTP_HOST -fno-strict-aliasing -Wno-unused-variable -Wno-format -Wno-unused-parameter -Wno-unused-private-field
 LOCAL_C_INCLUDES += $(LOCAL_PATH) bionic frameworks/base/include system/core/include bionic/libc/private/
@@ -45,35 +45,3 @@
 endif
 
 include $(BUILD_SHARED_LIBRARY)
-
-# Build twrpmtp binary / executable
-
-include $(CLEAR_VARS)
-
-LOCAL_MODULE := twrpmtp
-LOCAL_MODULE_TAGS := optional
-LOCAL_CFLAGS = -D_FILE_OFFSET_BITS=64 -DMTP_DEVICE -DMTP_HOST -DTWRPMTP
-LOCAL_C_INCLUDES += $(LOCAL_PATH) bionic frameworks/base/include system/core/include bionic/libc/private/
-LOCAL_SRC_FILES = \
-    btree.cpp \
-    MtpDataPacket.cpp \
-    MtpDebug.cpp \
-    MtpDevice.cpp \
-    MtpDeviceInfo.cpp \
-    MtpEventPacket.cpp \
-    MtpObjectInfo.cpp \
-    MtpPacket.cpp \
-    MtpProperty.cpp \
-    MtpRequestPacket.cpp \
-    MtpResponsePacket.cpp \
-    MtpServer.cpp \
-    MtpStorage.cpp \
-    MtpStorageInfo.cpp \
-    MtpStringBuffer.cpp \
-    MtpUtils.cpp \
-    mtp_MtpServer.cpp \
-    twrpMtp.cpp \
-    mtp_MtpDatabase.cpp \
-    node.cpp
-LOCAL_SHARED_LIBRARIES += libz libc libusbhost libstdc++ libdl libcutils libutils libaosprecovery
-include $(BUILD_EXECUTABLE)
diff --git a/mtp/MtpDataPacket.cpp b/mtp/legacy/MtpDataPacket.cpp
similarity index 100%
rename from mtp/MtpDataPacket.cpp
rename to mtp/legacy/MtpDataPacket.cpp
diff --git a/mtp/legacy/MtpDataPacket.h b/mtp/legacy/MtpDataPacket.h
new file mode 100644
index 0000000..5ad440d
--- /dev/null
+++ b/mtp/legacy/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/legacy/MtpDatabase.h b/mtp/legacy/MtpDatabase.h
new file mode 100644
index 0000000..f7f33ff
--- /dev/null
+++ b/mtp/legacy/MtpDatabase.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to c++
+ */
+
+#ifndef _MTP_DATABASE_H
+#define _MTP_DATABASE_H
+
+#include "MtpTypes.h"
+
+class MtpDataPacket;
+class MtpProperty;
+class MtpObjectInfo;
+
+class MtpDatabase {
+public:
+	virtual ~MtpDatabase() {}
+
+	// called from SendObjectInfo to reserve a database entry for the incoming file
+	virtual MtpObjectHandle			beginSendObject(const char* path,
+											MtpObjectFormat format,
+											MtpObjectHandle parent,
+											MtpStorageID storage,
+											uint64_t size,
+											time_t modified) = 0;
+
+	// called to report success or failure of the SendObject file transfer
+	// success should signal a notification of the new object's creation,
+	// failure should remove the database entry created in beginSendObject
+	virtual void					endSendObject(const char* path,
+											MtpObjectHandle handle,
+											MtpObjectFormat format,
+											bool succeeded) = 0;
+
+	virtual MtpObjectHandleList*	getObjectList(MtpStorageID storageID,
+											MtpObjectFormat format,
+											MtpObjectHandle parent) = 0;
+
+	virtual int						getNumObjects(MtpStorageID storageID,
+											MtpObjectFormat format,
+											MtpObjectHandle parent) = 0;
+
+	// callee should delete[] the results from these
+	// results can be NULL
+	virtual MtpObjectFormatList*	getSupportedPlaybackFormats() = 0;
+	virtual MtpObjectFormatList*	getSupportedCaptureFormats() = 0;
+	virtual MtpObjectPropertyList*	getSupportedObjectProperties(MtpObjectFormat format) = 0;
+	virtual MtpDevicePropertyList*	getSupportedDeviceProperties() = 0;
+
+	virtual void					createDB(MtpStorage* storage, MtpStorageID storageID) = 0;
+	virtual void					destroyDB(MtpStorageID storageID) = 0;
+
+	virtual MtpResponseCode			getObjectPropertyValue(MtpObjectHandle handle,
+											MtpObjectProperty property,
+											MtpDataPacket& packet) = 0;
+
+	virtual MtpResponseCode			setObjectPropertyValue(MtpObjectHandle handle,
+											MtpObjectProperty property,
+											MtpDataPacket& packet) = 0;
+
+	virtual MtpResponseCode			getDevicePropertyValue(MtpDeviceProperty property,
+											MtpDataPacket& packet) = 0;
+
+	virtual MtpResponseCode			setDevicePropertyValue(MtpDeviceProperty property,
+											MtpDataPacket& packet) = 0;
+
+	virtual MtpResponseCode			resetDeviceProperty(MtpDeviceProperty property) = 0;
+
+	virtual MtpResponseCode			getObjectPropertyList(MtpObjectHandle handle,
+											uint32_t format, uint32_t property,
+											int groupCode, int depth,
+											MtpDataPacket& packet) = 0;
+
+	virtual MtpResponseCode			getObjectInfo(MtpObjectHandle handle,
+											MtpObjectInfo& info) = 0;
+
+	virtual void*					getThumbnail(MtpObjectHandle handle, size_t& outThumbSize) = 0;
+
+	virtual MtpResponseCode			getObjectFilePath(MtpObjectHandle handle,
+											MtpString& outFilePath,
+											int64_t& outFileLength,
+											MtpObjectFormat& outFormat) = 0;
+
+	virtual MtpResponseCode			deleteFile(MtpObjectHandle handle) = 0;
+
+	virtual MtpObjectHandleList*	getObjectReferences(MtpObjectHandle handle) = 0;
+
+	virtual MtpResponseCode			setObjectReferences(MtpObjectHandle handle,
+											MtpObjectHandleList* references) = 0;
+
+	virtual MtpProperty*			getObjectPropertyDesc(MtpObjectProperty property,
+											MtpObjectFormat format) = 0;
+
+	virtual MtpProperty*			getDevicePropertyDesc(MtpDeviceProperty property) = 0;
+
+	virtual void					sessionStarted() = 0;
+
+	virtual void					sessionEnded() = 0;
+	virtual void					lockMutex() = 0;
+	virtual void					unlockMutex() = 0;
+};
+
+#endif // _MTP_DATABASE_H
diff --git a/mtp/MtpDebug.cpp b/mtp/legacy/MtpDebug.cpp
similarity index 100%
rename from mtp/MtpDebug.cpp
rename to mtp/legacy/MtpDebug.cpp
diff --git a/mtp/MtpDebug.h b/mtp/legacy/MtpDebug.h
similarity index 100%
rename from mtp/MtpDebug.h
rename to mtp/legacy/MtpDebug.h
diff --git a/mtp/MtpDevice.cpp b/mtp/legacy/MtpDevice.cpp
similarity index 100%
rename from mtp/MtpDevice.cpp
rename to mtp/legacy/MtpDevice.cpp
diff --git a/mtp/legacy/MtpDevice.h b/mtp/legacy/MtpDevice.h
new file mode 100644
index 0000000..34b39ec
--- /dev/null
+++ b/mtp/legacy/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/legacy/MtpDeviceInfo.cpp
similarity index 100%
rename from mtp/MtpDeviceInfo.cpp
rename to mtp/legacy/MtpDeviceInfo.cpp
diff --git a/mtp/legacy/MtpDeviceInfo.h b/mtp/legacy/MtpDeviceInfo.h
new file mode 100644
index 0000000..264e199
--- /dev/null
+++ b/mtp/legacy/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/legacy/MtpEventPacket.cpp
similarity index 100%
rename from mtp/MtpEventPacket.cpp
rename to mtp/legacy/MtpEventPacket.cpp
diff --git a/mtp/MtpEventPacket.h b/mtp/legacy/MtpEventPacket.h
similarity index 61%
rename from mtp/MtpEventPacket.h
rename to mtp/legacy/MtpEventPacket.h
index b42abce..6439856 100644
--- a/mtp/MtpEventPacket.h
+++ b/mtp/legacy/MtpEventPacket.h
@@ -5,7 +5,7 @@
  * 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
+ *		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,
@@ -25,22 +25,22 @@
 class MtpEventPacket : public MtpPacket {
 
 public:
-                        MtpEventPacket();
-    virtual             ~MtpEventPacket();
+						MtpEventPacket();
+	virtual				~MtpEventPacket();
 
 #ifdef MTP_DEVICE
-    // write our data to the given file descriptor
-    int                 write(int fd);
+	// 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);
+	// 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); }
+	inline MtpEventCode		getEventCode() const { return getContainerCode(); }
+	inline void				setEventCode(MtpEventCode code)
+													 { return setContainerCode(code); }
 };
 
 #endif // _MTP_EVENT_PACKET_H
diff --git a/mtp/MtpMessage.hpp b/mtp/legacy/MtpMessage.hpp
similarity index 100%
copy from mtp/MtpMessage.hpp
copy to mtp/legacy/MtpMessage.hpp
diff --git a/mtp/MtpObjectInfo.cpp b/mtp/legacy/MtpObjectInfo.cpp
similarity index 100%
rename from mtp/MtpObjectInfo.cpp
rename to mtp/legacy/MtpObjectInfo.cpp
diff --git a/mtp/legacy/MtpObjectInfo.h b/mtp/legacy/MtpObjectInfo.h
new file mode 100644
index 0000000..9b023bc
--- /dev/null
+++ b/mtp/legacy/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/legacy/MtpPacket.cpp
similarity index 100%
rename from mtp/MtpPacket.cpp
rename to mtp/legacy/MtpPacket.cpp
diff --git a/mtp/legacy/MtpPacket.h b/mtp/legacy/MtpPacket.h
new file mode 100644
index 0000000..be3db30
--- /dev/null
+++ b/mtp/legacy/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/legacy/MtpProperty.cpp
similarity index 100%
rename from mtp/MtpProperty.cpp
rename to mtp/legacy/MtpProperty.cpp
diff --git a/mtp/legacy/MtpProperty.h b/mtp/legacy/MtpProperty.h
new file mode 100644
index 0000000..c1f3233
--- /dev/null
+++ b/mtp/legacy/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/legacy/MtpRequestPacket.cpp
similarity index 96%
rename from mtp/MtpRequestPacket.cpp
rename to mtp/legacy/MtpRequestPacket.cpp
index 754e205..e700e3b 100644
--- a/mtp/MtpRequestPacket.cpp
+++ b/mtp/legacy/MtpRequestPacket.cpp
@@ -36,9 +36,7 @@
 
 #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
diff --git a/mtp/MtpRequestPacket.h b/mtp/legacy/MtpRequestPacket.h
similarity index 60%
rename from mtp/MtpRequestPacket.h
rename to mtp/legacy/MtpRequestPacket.h
index 8551dde..dcf00d6 100644
--- a/mtp/MtpRequestPacket.h
+++ b/mtp/legacy/MtpRequestPacket.h
@@ -5,7 +5,7 @@
  * 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
+ *		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,
@@ -28,21 +28,21 @@
 class MtpRequestPacket : public MtpPacket {
 
 public:
-                        MtpRequestPacket();
-    virtual             ~MtpRequestPacket();
+						MtpRequestPacket();
+	virtual				~MtpRequestPacket();
 #ifdef MTP_DEVICE
-    // fill our buffer with data from the given file descriptor
-    int                 read(int fd);
+	// 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);
+	// 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); }
+	inline MtpOperationCode    getOperationCode() const { return getContainerCode(); }
+	inline void				   setOperationCode(MtpOperationCode code)
+													{ return setContainerCode(code); }
 };
 
 
diff --git a/mtp/MtpResponsePacket.cpp b/mtp/legacy/MtpResponsePacket.cpp
similarity index 100%
rename from mtp/MtpResponsePacket.cpp
rename to mtp/legacy/MtpResponsePacket.cpp
diff --git a/mtp/MtpResponsePacket.h b/mtp/legacy/MtpResponsePacket.h
similarity index 60%
rename from mtp/MtpResponsePacket.h
rename to mtp/legacy/MtpResponsePacket.h
index 749b534..f9621aa 100644
--- a/mtp/MtpResponsePacket.h
+++ b/mtp/legacy/MtpResponsePacket.h
@@ -5,7 +5,7 @@
  * 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
+ *		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,
@@ -26,22 +26,22 @@
 class MtpResponsePacket : public MtpPacket {
 
 public:
-                        MtpResponsePacket();
-    virtual             ~MtpResponsePacket();
+						MtpResponsePacket();
+	virtual				~MtpResponsePacket();
 
 #ifdef MTP_DEVICE
-    // write our data to the given file descriptor
-    int                 write(int fd);
+	// 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);
+	// 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); }
+	inline MtpResponseCode		getResponseCode() const { return getContainerCode(); }
+	inline void					setResponseCode(MtpResponseCode code)
+													 { return setContainerCode(code); }
 };
 
 
diff --git a/mtp/MtpServer.cpp b/mtp/legacy/MtpServer.cpp
similarity index 97%
rename from mtp/MtpServer.cpp
rename to mtp/legacy/MtpServer.cpp
index 11eca86..c4e1cd3 100644
--- a/mtp/MtpServer.cpp
+++ b/mtp/legacy/MtpServer.cpp
@@ -25,8 +25,8 @@
 #include <errno.h>
 #include <sys/stat.h>
 #include <dirent.h>
-#include "../twcommon.h"
-#include "../set_metadata.h"
+#include "../../twcommon.h"
+#include "../../set_metadata.h"
 #include <cutils/properties.h>
 
 #include "MtpTypes.h"
@@ -383,7 +383,7 @@
 				MTPD("doGetStorageIDs()\n");
 				response = doGetStorageIDs();
 				break;
-		 	 case MTP_OPERATION_GET_STORAGE_INFO:
+			 case MTP_OPERATION_GET_STORAGE_INFO:
 				MTPD("about to call doGetStorageInfo()\n");
 				response = doGetStorageInfo();
 				break;
@@ -527,7 +527,7 @@
 			sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
 	mData.putAUInt16(deviceProperties); // Device Properties Supported
 	mData.putAUInt16(captureFormats); // Capture Formats
-	mData.putAUInt16(playbackFormats);  // Playback Formats
+	mData.putAUInt16(playbackFormats);	// Playback Formats
 
 	property_get("ro.product.manufacturer", prop_value, "unknown manufacturer");
 	MTPD("prop: %s\n", prop_value);
@@ -824,7 +824,7 @@
 		mData.putString(info.mName);
 		mData.putEmptyString();	// date created
 		formatDateTime(info.mDateModified, date, sizeof(date));
-		mData.putString(date);   // date modified
+		mData.putString(date);	 // date modified
 		mData.putEmptyString();   // keywords
 	}
 	return result;
@@ -846,7 +846,7 @@
 
 	const char* filePath = (const char *)pathBuf;
 	MTPD("filePath: %s\n", filePath);
-	mtp_file_range  mfr;
+	mtp_file_range	mfr;
 	mfr.fd = open(filePath, O_RDONLY);
 	if (mfr.fd < 0) {
 		return MTP_RESPONSE_GENERAL_ERROR;
@@ -918,7 +918,7 @@
 		length = fileLength - offset;
 
 	const char* filePath = (const char *)pathBuf;
-	mtp_file_range  mfr;
+	mtp_file_range	mfr;
 	mfr.fd = open(filePath, O_RDONLY);
 	if (mfr.fd < 0) {
 		return MTP_RESPONSE_GENERAL_ERROR;
@@ -971,21 +971,21 @@
 	}
 
 	// read only the fields we need
-	mData.getUInt32();  // storage ID
+	mData.getUInt32();	// storage ID
 	MtpObjectFormat format = mData.getUInt16();
-	mData.getUInt16();  // protection status
+	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
+	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
+	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
@@ -1077,7 +1077,7 @@
 	}
 	initialData = ret - MTP_CONTAINER_HEADER_SIZE;
 
-	mtp_file_range  mfr;
+	mtp_file_range	mfr;
 	mfr.fd = open(mSendObjectFilePath, O_RDWR | O_CREAT | O_TRUNC, 0640);
 	if (mfr.fd < 0) {
 		result = MTP_RESPONSE_GENERAL_ERROR;
@@ -1114,7 +1114,7 @@
 		if (errno == ECANCELED)
 			result = MTP_RESPONSE_TRANSACTION_CANCELLED;
 		else {
-		    	MTPD("errno: %d\n", errno);
+				MTPD("errno: %d\n", errno);
 			result = MTP_RESPONSE_GENERAL_ERROR;
 		}
 	}
@@ -1292,7 +1292,7 @@
 	}
 
 	if (length > 0) {
-		mtp_file_range  mfr;
+		mtp_file_range	mfr;
 		mfr.fd = edit->mFD;
 		mfr.offset = offset;
 		mfr.length = length;
diff --git a/mtp/legacy/MtpServer.h b/mtp/legacy/MtpServer.h
new file mode 100644
index 0000000..d58cb30
--- /dev/null
+++ b/mtp/legacy/MtpServer.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef _MTP_SERVER_H
+#define _MTP_SERVER_H
+
+#include <utils/threads.h>
+#include <utils/Vector.h>
+#include "MtpRequestPacket.h"
+#include "MtpDatabase.h"
+#include "MtpDataPacket.h"
+#include "MtpResponsePacket.h"
+#include "MtpEventPacket.h"
+#include "mtp.h"
+#include "MtpUtils.h"
+
+
+class MtpDatabase;
+class MtpStorage;
+
+class MtpServer {
+
+private:
+	// file descriptor for MTP kernel driver
+	int					mFD;
+	android::Mutex					 mMutex;
+	MtpDatabase*		mDatabase;
+
+	// appear as a PTP device
+	bool				mPtp;
+
+	// group to own new files and folders
+	int					mFileGroup;
+	// permissions for new files and directories
+	int					mFilePermission;
+	int					mDirectoryPermission;
+
+	// current session ID
+	MtpSessionID		mSessionID;
+	// true if we have an open session and mSessionID is valid
+	bool				mSessionOpen;
+
+	MtpRequestPacket	mRequest;
+	MtpDataPacket		mData;
+	MtpResponsePacket	mResponse;
+	MtpEventPacket		mEvent;
+
+	MtpStorageList		mStorages;
+
+	// handle for new object, set by SendObjectInfo and used by SendObject
+	MtpObjectHandle		mSendObjectHandle;
+	MtpObjectFormat		mSendObjectFormat;
+	MtpString			mSendObjectFilePath;
+	size_t				mSendObjectFileSize;
+
+	pthread_mutex_t mtpMutex;
+
+	// represents an MTP object that is being edited using the android extensions
+	// for direct editing (BeginEditObject, SendPartialObject, TruncateObject and EndEditObject)
+	class ObjectEdit {
+		public:
+		MtpObjectHandle		mHandle;
+		MtpString			mPath;
+		uint64_t			mSize;
+		MtpObjectFormat		mFormat;
+		int					mFD;
+
+		ObjectEdit(MtpObjectHandle handle, const char* path, uint64_t size,
+			MtpObjectFormat format, int fd)
+				: mHandle(handle), mPath(path), mSize(size), mFormat(format), mFD(fd) {
+			}
+
+		virtual ~ObjectEdit() {
+			close(mFD);
+		}
+	};
+	android::Vector<ObjectEdit*>  mObjectEditList;
+
+public:
+						MtpServer(MtpDatabase* database, bool ptp,
+									int fileGroup, int filePerm, int directoryPerm);
+	virtual				~MtpServer();
+
+	MtpStorage*			getStorage(MtpStorageID id);
+	inline bool			hasStorage() { return mStorages.size() > 0; }
+	bool				hasStorage(MtpStorageID id);
+	void				addStorage(MtpStorage* storage);
+	void				removeStorage(MtpStorage* storage);
+
+	void				run(int fd);
+
+	void				sendObjectAdded(MtpObjectHandle handle);
+	void				sendObjectRemoved(MtpObjectHandle handle);
+	void				sendObjectUpdated(MtpObjectHandle handle);
+
+private:
+	void				sendStoreAdded(MtpStorageID id);
+	void				sendStoreRemoved(MtpStorageID id);
+	void				sendEvent(MtpEventCode code, uint32_t param1);
+
+	void				addEditObject(MtpObjectHandle handle, MtpString& path,
+								uint64_t size, MtpObjectFormat format, int fd);
+	ObjectEdit*			getEditObject(MtpObjectHandle handle);
+	void				removeEditObject(MtpObjectHandle handle);
+	void				commitEdit(ObjectEdit* edit);
+
+	bool				handleRequest();
+
+	MtpResponseCode		doGetDeviceInfo();
+	MtpResponseCode		doOpenSession();
+	MtpResponseCode		doCloseSession();
+	MtpResponseCode		doGetStorageIDs();
+	MtpResponseCode		doGetStorageInfo();
+	MtpResponseCode		doGetObjectPropsSupported();
+	MtpResponseCode		doGetObjectHandles();
+	MtpResponseCode		doGetNumObjects();
+	MtpResponseCode		doGetObjectReferences();
+	MtpResponseCode		doSetObjectReferences();
+	MtpResponseCode		doGetObjectPropValue();
+	MtpResponseCode		doSetObjectPropValue();
+	MtpResponseCode		doGetDevicePropValue();
+	MtpResponseCode		doSetDevicePropValue();
+	MtpResponseCode		doResetDevicePropValue();
+	MtpResponseCode		doGetObjectPropList();
+	MtpResponseCode		doGetObjectInfo();
+	MtpResponseCode		doGetObject();
+	MtpResponseCode		doGetThumb();
+	MtpResponseCode		doGetPartialObject(MtpOperationCode operation);
+	MtpResponseCode		doSendObjectInfo();
+	MtpResponseCode		doSendObject();
+	MtpResponseCode		doDeleteObject();
+	MtpResponseCode		doGetObjectPropDesc();
+	MtpResponseCode		doGetDevicePropDesc();
+	MtpResponseCode		doSendPartialObject();
+	MtpResponseCode		doTruncateObject();
+	MtpResponseCode		doBeginEditObject();
+	MtpResponseCode		doEndEditObject();
+};
+
+#endif // _MTP_SERVER_H
diff --git a/mtp/MtpStorage.cpp b/mtp/legacy/MtpStorage.cpp
similarity index 98%
rename from mtp/MtpStorage.cpp
rename to mtp/legacy/MtpStorage.cpp
index 5a69548..b22158a 100644
--- a/mtp/MtpStorage.cpp
+++ b/mtp/legacy/MtpStorage.cpp
@@ -36,7 +36,7 @@
 #include <signal.h>
 #include <sys/inotify.h>
 #include <fcntl.h>
-#include "../tw_atomic.hpp"
+#include "../../tw_atomic.hpp"
 
 #define WATCH_FLAGS ( IN_CREATE | IN_DELETE | IN_MOVE | IN_MODIFY )
 
@@ -110,7 +110,7 @@
 
 uint64_t MtpStorage::getMaxCapacity() {
 	if (mMaxCapacity == 0) {
-		struct statfs   stat;
+		struct statfs	stat;
 		if (statfs(getPath(), &stat))
 			return -1;
 		mMaxCapacity = (uint64_t)stat.f_blocks * (uint64_t)stat.f_bsize;
@@ -119,7 +119,7 @@
 }
 
 uint64_t MtpStorage::getFreeSpace() {
-	struct statfs   stat;
+	struct statfs	stat;
 	if (statfs(getPath(), &stat))
 		return -1;
 	uint64_t freeSpace = (uint64_t)stat.f_bavail * (uint64_t)stat.f_bsize;
@@ -157,7 +157,7 @@
 
 MtpObjectHandleList* MtpStorage::getObjectList(MtpStorageID storageID, MtpObjectHandle parent) {
 	MTPD("MtpStorage::getObjectList, parent: %u\n", parent);
-	//append object id  (numerical #s) of database to int array
+	//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");
@@ -430,7 +430,7 @@
 	// format == 0 -> all formats, otherwise filter by ObjectFormatCode
 	// property == 0xffffffff -> all properties except those with group code 0xffffffff
 	// if property == 0 then use groupCode
-	//   groupCode == 0 -> return Specification_By_Group_Unsupported
+	//	 groupCode == 0 -> return Specification_By_Group_Unsupported
 	// depth == 0xffffffff -> all objects incl. and below handle
 
 	std::vector<PropEntry> results;
diff --git a/mtp/MtpStorage.h b/mtp/legacy/MtpStorage.h
similarity index 67%
rename from mtp/MtpStorage.h
rename to mtp/legacy/MtpStorage.h
index 245debf..d967b4b 100644
--- a/mtp/MtpStorage.h
+++ b/mtp/legacy/MtpStorage.h
@@ -5,7 +5,7 @@
  * 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
+ *		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,
@@ -28,7 +28,7 @@
 #include <pthread.h>
 #include "btree.hpp"
 #include "MtpServer.h"
-#include "../tw_atomic.hpp"
+#include "../../tw_atomic.hpp"
 
 class MtpDatabase;
 struct inotify_event;
@@ -36,37 +36,37 @@
 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;
+	MtpStorageID			mStorageID;
+	MtpString				mFilePath;
+	MtpString				mDescription;
+	uint64_t				mMaxCapacity;
+	uint64_t				mMaxFileSize;
+	// amount of free space to leave unallocated
+	uint64_t				mReserveSpace;
+	bool					mRemovable;
 	MtpServer*				mServer;
-    typedef std::map<int, Tree*> maptree;
-    typedef maptree::iterator iter;
-    maptree mtpmap;
+	typedef std::map<int, Tree*> maptree;
+	typedef maptree::iterator iter;
+	maptree mtpmap;
 	std::string mtpstorageparent;
-	android::Mutex           mMutex;
+	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();
+							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; }
+	inline MtpStorageID		getStorageID() const { return mStorageID; }
+	int						getType() const;
+	int						getFileSystemType() const;
+	int						getAccessCapability() const;
+	uint64_t				getMaxCapacity();
+	uint64_t				getFreeSpace();
+	const char*				getDescription() const;
+	inline const char*		getPath() const { return (const char *)mFilePath; }
+	inline bool				isRemovable() const { return mRemovable; }
+	inline uint64_t			getMaxFileSize() const { return mMaxFileSize; }
 
 	struct PropEntry {
 		MtpObjectHandle handle;
diff --git a/mtp/MtpStorageInfo.cpp b/mtp/legacy/MtpStorageInfo.cpp
similarity index 100%
rename from mtp/MtpStorageInfo.cpp
rename to mtp/legacy/MtpStorageInfo.cpp
diff --git a/mtp/legacy/MtpStorageInfo.h b/mtp/legacy/MtpStorageInfo.h
new file mode 100644
index 0000000..80f8752
--- /dev/null
+++ b/mtp/legacy/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/legacy/MtpStringBuffer.cpp
similarity index 100%
rename from mtp/MtpStringBuffer.cpp
rename to mtp/legacy/MtpStringBuffer.cpp
diff --git a/mtp/legacy/MtpStringBuffer.h b/mtp/legacy/MtpStringBuffer.h
new file mode 100644
index 0000000..68c0a0c
--- /dev/null
+++ b/mtp/legacy/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/legacy/MtpTypes.h
similarity index 100%
rename from mtp/MtpTypes.h
rename to mtp/legacy/MtpTypes.h
diff --git a/mtp/MtpUtils.cpp b/mtp/legacy/MtpUtils.cpp
similarity index 100%
rename from mtp/MtpUtils.cpp
rename to mtp/legacy/MtpUtils.cpp
diff --git a/mtp/MtpUtils.h b/mtp/legacy/MtpUtils.h
similarity index 100%
rename from mtp/MtpUtils.h
rename to mtp/legacy/MtpUtils.h
diff --git a/mtp/btree.cpp b/mtp/legacy/btree.cpp
similarity index 100%
rename from mtp/btree.cpp
rename to mtp/legacy/btree.cpp
diff --git a/mtp/btree.hpp b/mtp/legacy/btree.hpp
similarity index 100%
copy from mtp/btree.hpp
copy to mtp/legacy/btree.hpp
diff --git a/mtp/mtp.h b/mtp/legacy/mtp.h
similarity index 100%
rename from mtp/mtp.h
rename to mtp/legacy/mtp.h
diff --git a/mtp/mtp_MtpDatabase.cpp b/mtp/legacy/mtp_MtpDatabase.cpp
similarity index 100%
rename from mtp/mtp_MtpDatabase.cpp
rename to mtp/legacy/mtp_MtpDatabase.cpp
diff --git a/mtp/legacy/mtp_MtpDatabase.hpp b/mtp/legacy/mtp_MtpDatabase.hpp
new file mode 100644
index 0000000..931ba15
--- /dev/null
+++ b/mtp/legacy/mtp_MtpDatabase.hpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *		http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
+ */
+
+#ifndef MTP_MTPDATABASE_HPP
+#define MTP_MTPDATABASE_HPP
+
+#include <utils/Log.h>
+
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <map>
+#include <string>
+#include <deque>
+
+#include "MtpDatabase.h"
+#include "MtpDataPacket.h"
+#include "MtpObjectInfo.h"
+#include "MtpProperty.h"
+#include "MtpStringBuffer.h"
+#include "MtpUtils.h"
+#include "mtp.h"
+
+class MyMtpDatabase : public MtpDatabase {
+private:
+	int* getSupportedObjectProperties(int format);
+
+	static int FILE_PROPERTIES[10];
+	static int DEVICE_PROPERTIES[3];
+	static int AUDIO_PROPERTIES[19];
+	static int VIDEO_PROPERTIES[15];
+	static int IMAGE_PROPERTIES[12];
+	static int ALL_PROPERTIES[25];
+	static int SUPPORTED_PLAYBACK_FORMATS[26];
+	int storagenum;
+	int count;
+	std::string lastfile;
+	std::map<int, MtpStorage*> storagemap;
+	void countDirs(std::string path);
+	int readParentDirs(std::string path, int storageID);
+
+public:
+									MyMtpDatabase();
+	virtual							~MyMtpDatabase();
+
+	void					createDB(MtpStorage* storage, MtpStorageID storageID);
+	void					destroyDB(MtpStorageID storageID);
+	virtual MtpObjectHandle			beginSendObject(const char* path,
+											MtpObjectFormat format,
+											MtpObjectHandle parent,
+											MtpStorageID storage,
+											uint64_t size,
+											time_t modified);
+
+	virtual void					endSendObject(const char* path,
+											MtpObjectHandle handle,
+											MtpObjectFormat format,
+											bool succeeded);
+
+	virtual MtpObjectHandleList*	getObjectList(MtpStorageID storageID,
+									MtpObjectFormat format,
+									MtpObjectHandle parent);
+
+	virtual int						getNumObjects(MtpStorageID storageID,
+											MtpObjectFormat format,
+											MtpObjectHandle parent);
+
+	// callee should delete[] the results from these
+	// results can be NULL
+	virtual MtpObjectFormatList*	getSupportedPlaybackFormats();
+	virtual MtpObjectFormatList*	getSupportedCaptureFormats();
+	virtual MtpObjectPropertyList*	getSupportedObjectProperties(MtpObjectFormat format);
+	virtual MtpDevicePropertyList*	getSupportedDeviceProperties();
+
+	virtual MtpResponseCode			getObjectPropertyValue(MtpObjectHandle handle,
+											MtpObjectProperty property,
+											MtpDataPacket& packet);
+
+	virtual MtpResponseCode			setObjectPropertyValue(MtpObjectHandle handle,
+											MtpObjectProperty property,
+											MtpDataPacket& packet);
+
+	virtual MtpResponseCode			getDevicePropertyValue(MtpDeviceProperty property,
+											MtpDataPacket& packet);
+
+	virtual MtpResponseCode			setDevicePropertyValue(MtpDeviceProperty property,
+											MtpDataPacket& packet);
+
+	virtual MtpResponseCode			resetDeviceProperty(MtpDeviceProperty property);
+
+	virtual MtpResponseCode			getObjectPropertyList(MtpObjectHandle handle,
+											uint32_t format, uint32_t property,
+											int groupCode, int depth,
+											MtpDataPacket& packet);
+
+	virtual MtpResponseCode			getObjectInfo(MtpObjectHandle handle,
+											MtpObjectInfo& info);
+
+	virtual void*					getThumbnail(MtpObjectHandle handle, size_t& outThumbSize);
+
+	virtual MtpResponseCode			getObjectFilePath(MtpObjectHandle handle,
+											MtpString& outFilePath,
+											int64_t& outFileLength,
+											MtpObjectFormat& outFormat);
+	virtual MtpResponseCode			deleteFile(MtpObjectHandle handle);
+
+	bool							getObjectPropertyInfo(MtpObjectProperty property, int& type);
+	bool							getDevicePropertyInfo(MtpDeviceProperty property, int& type);
+
+	virtual MtpObjectHandleList*	getObjectReferences(MtpObjectHandle handle);
+
+	virtual MtpResponseCode			setObjectReferences(MtpObjectHandle handle,
+											MtpObjectHandleList* references);
+
+	virtual MtpProperty*			getObjectPropertyDesc(MtpObjectProperty property,
+											MtpObjectFormat format);
+
+	virtual MtpProperty*			getDevicePropertyDesc(MtpDeviceProperty property);
+
+	virtual void					sessionStarted();
+
+	virtual void					sessionEnded();
+	virtual void					lockMutex();
+	virtual void					unlockMutex();
+};
+#endif
diff --git a/mtp/mtp_MtpServer.cpp b/mtp/legacy/mtp_MtpServer.cpp
similarity index 100%
rename from mtp/mtp_MtpServer.cpp
rename to mtp/legacy/mtp_MtpServer.cpp
diff --git a/mtp/mtp_MtpServer.hpp b/mtp/legacy/mtp_MtpServer.hpp
similarity index 100%
rename from mtp/mtp_MtpServer.hpp
rename to mtp/legacy/mtp_MtpServer.hpp
diff --git a/mtp/node.cpp b/mtp/legacy/node.cpp
similarity index 100%
rename from mtp/node.cpp
rename to mtp/legacy/node.cpp
diff --git a/mtp/twrpMtp.cpp b/mtp/legacy/twrpMtp.cpp
similarity index 100%
rename from mtp/twrpMtp.cpp
rename to mtp/legacy/twrpMtp.cpp
diff --git a/mtp/twrpMtp.hpp b/mtp/legacy/twrpMtp.hpp
similarity index 100%
rename from mtp/twrpMtp.hpp
rename to mtp/legacy/twrpMtp.hpp
diff --git a/mtp/mtp_MtpDatabase.hpp b/mtp/mtp_MtpDatabase.hpp
deleted file mode 100644
index 49e5913..0000000
--- a/mtp/mtp_MtpDatabase.hpp
+++ /dev/null
@@ -1,146 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- * Copyright (C) 2014 TeamWin - bigbiff and Dees_Troy mtp database conversion to C++
- */
-
-#ifndef MTP_MTPDATABASE_HPP
-#define MTP_MTPDATABASE_HPP
-
-#include <utils/Log.h>
-
-#include <stdio.h>
-#include <assert.h>
-#include <limits.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <errno.h>
-#include <sys/types.h>
-#include <map>
-#include <string>
-#include <deque>
-
-#include "MtpDatabase.h"
-#include "MtpDataPacket.h"
-#include "MtpObjectInfo.h"
-#include "MtpProperty.h"
-#include "MtpStringBuffer.h"
-#include "MtpUtils.h"
-#include "mtp.h"
-
-class MyMtpDatabase : public MtpDatabase {
-private:
-	int* getSupportedObjectProperties(int format);
-
-    static int FILE_PROPERTIES[10];
-	static int DEVICE_PROPERTIES[3];
-    static int AUDIO_PROPERTIES[19];
-    static int VIDEO_PROPERTIES[15];
-    static int IMAGE_PROPERTIES[12];
-    static int ALL_PROPERTIES[25];
-    static int SUPPORTED_PLAYBACK_FORMATS[26];
-	int storagenum;
-	int count;
-	std::string lastfile;
-	std::map<int, MtpStorage*> storagemap;
-	void countDirs(std::string path);
-	int readParentDirs(std::string path, int storageID);
-
-public:
-                                    MyMtpDatabase();
-    virtual                         ~MyMtpDatabase();
-
-	void					createDB(MtpStorage* storage, MtpStorageID storageID);
-	void					destroyDB(MtpStorageID storageID);
-    virtual MtpObjectHandle         beginSendObject(const char* path,
-                                            MtpObjectFormat format,
-                                            MtpObjectHandle parent,
-                                            MtpStorageID storage,
-                                            uint64_t size,
-                                            time_t modified);
-
-    virtual void                    endSendObject(const char* path,
-                                            MtpObjectHandle handle,
-                                            MtpObjectFormat format,
-                                            bool succeeded);
-
-    virtual MtpObjectHandleList*    getObjectList(MtpStorageID storageID,
-                                    MtpObjectFormat format,
-                                    MtpObjectHandle parent);
-
-    virtual int                     getNumObjects(MtpStorageID storageID,
-                                            MtpObjectFormat format,
-                                            MtpObjectHandle parent);
-
-    // callee should delete[] the results from these
-    // results can be NULL
-    virtual MtpObjectFormatList*    getSupportedPlaybackFormats();
-    virtual MtpObjectFormatList*    getSupportedCaptureFormats();
-    virtual MtpObjectPropertyList*  getSupportedObjectProperties(MtpObjectFormat format);
-    virtual MtpDevicePropertyList*  getSupportedDeviceProperties();
-
-    virtual MtpResponseCode         getObjectPropertyValue(MtpObjectHandle handle,
-                                            MtpObjectProperty property,
-                                            MtpDataPacket& packet);
-
-    virtual MtpResponseCode         setObjectPropertyValue(MtpObjectHandle handle,
-                                            MtpObjectProperty property,
-                                            MtpDataPacket& packet);
-
-    virtual MtpResponseCode         getDevicePropertyValue(MtpDeviceProperty property,
-                                            MtpDataPacket& packet);
-
-    virtual MtpResponseCode         setDevicePropertyValue(MtpDeviceProperty property,
-                                            MtpDataPacket& packet);
-
-    virtual MtpResponseCode         resetDeviceProperty(MtpDeviceProperty property);
-
-    virtual MtpResponseCode         getObjectPropertyList(MtpObjectHandle handle,
-                                            uint32_t format, uint32_t property,
-                                            int groupCode, int depth,
-                                            MtpDataPacket& packet);
-
-    virtual MtpResponseCode         getObjectInfo(MtpObjectHandle handle,
-                                            MtpObjectInfo& info);
-
-    virtual void*                   getThumbnail(MtpObjectHandle handle, size_t& outThumbSize);
-
-    virtual MtpResponseCode         getObjectFilePath(MtpObjectHandle handle,
-                                            MtpString& outFilePath,
-                                            int64_t& outFileLength,
-                                            MtpObjectFormat& outFormat);
-    virtual MtpResponseCode         deleteFile(MtpObjectHandle handle);
-
-    bool                            getObjectPropertyInfo(MtpObjectProperty property, int& type);
-    bool                            getDevicePropertyInfo(MtpDeviceProperty property, int& type);
-
-    virtual MtpObjectHandleList*    getObjectReferences(MtpObjectHandle handle);
-
-    virtual MtpResponseCode         setObjectReferences(MtpObjectHandle handle,
-                                            MtpObjectHandleList* references);
-
-    virtual MtpProperty*            getObjectPropertyDesc(MtpObjectProperty property,
-                                            MtpObjectFormat format);
-
-    virtual MtpProperty*            getDevicePropertyDesc(MtpDeviceProperty property);
-
-    virtual void                    sessionStarted();
-
-    virtual void                    sessionEnded();
-    virtual void                    lockMutex();
-    virtual void                    unlockMutex();
-};
-#endif
diff --git a/openrecoveryscript.cpp b/openrecoveryscript.cpp
index d4d4da5..9478cd7 100755
--- a/openrecoveryscript.cpp
+++ b/openrecoveryscript.cpp
@@ -343,6 +343,8 @@
 					TWFunc::tw_reboot(rb_bootloader);
 				else if (strlen(value) && strcmp(value, "download") == 0)
 					TWFunc::tw_reboot(rb_download);
+				else if (strlen(value) && strcmp(value, "edl") == 0)
+					TWFunc::tw_reboot(rb_edl);
 				else
 					TWFunc::tw_reboot(rb_system);
 			} else if (strcmp(command, "cmd") == 0) {
diff --git a/partitionmanager.cpp b/partitionmanager.cpp
index 5b749bb..4ebe742 100755
--- a/partitionmanager.cpp
+++ b/partitionmanager.cpp
@@ -55,9 +55,15 @@
 #include "adbbu/libtwadbbu.hpp"
 
 #ifdef TW_HAS_MTP
-#include "mtp/mtp_MtpServer.hpp"
-#include "mtp/twrpMtp.hpp"
-#include "mtp/MtpMessage.hpp"
+#ifdef TW_HAS_LEGACY_MTP
+#include "mtp/legacy/mtp_MtpServer.hpp"
+#include "mtp/legacy/twrpMtp.hpp"
+#include "mtp/legacy/MtpMessage.hpp"
+#else
+#include "mtp/ffs/mtp_MtpServer.hpp"
+#include "mtp/ffs/twrpMtp.hpp"
+#include "mtp/ffs/MtpMessage.hpp"
+#endif
 #endif
 
 extern "C" {
@@ -2315,6 +2321,7 @@
 
 bool TWPartitionManager::Disable_MTP(void) {
 	char old_value[PROPERTY_VALUE_MAX];
+	property_set("sys.usb.ffs.mtp.ready", "0");
 	property_get("sys.usb.config", old_value, "");
 	if (strcmp(old_value, "adb") != 0) {
 		char vendor[PROPERTY_VALUE_MAX];
diff --git a/prebuilt/Android.mk b/prebuilt/Android.mk
index 3418f1d..7696aff 100644
--- a/prebuilt/Android.mk
+++ b/prebuilt/Android.mk
@@ -137,14 +137,24 @@
     RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libmincrypttwrp.so
 endif
 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
+
+ifeq ($(shell test $(PLATFORM_SDK_VERSION) -ge 28; echo $$?),0)
 ifneq ($(TW_EXCLUDE_MTP), true)
-    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libtwrpmtp.so
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libtwrpmtp-ffs.so
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libasyncio.so
 endif
+else
+ifneq ($(TW_EXCLUDE_MTP), true)
+    RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libtwrpmtp-legacy.so
+endif
+endif
+
 RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libext4_utils.so
 RELINK_SOURCE_FILES += $(TARGET_OUT_SHARED_LIBRARIES)/libaosprecovery.so
 ifneq ($(TW_INCLUDE_JPEG),)
diff --git a/twrp-functions.cpp b/twrp-functions.cpp
index 37dd0df..ff34828 100755
--- a/twrp-functions.cpp
+++ b/twrp-functions.cpp
@@ -626,6 +626,13 @@
 #else
 			return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, (void*) "download");
 #endif
+		case rb_edl:
+			check_and_run_script("/sbin/rebootedl.sh", "reboot edl");
+#ifdef ANDROID_RB_PROPERTY
+			return property_set(ANDROID_RB_PROPERTY, "reboot,edl");
+#else
+			return __reboot(LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_RESTART2, (void*) "edl");
+#endif
 		default:
 			return -1;
 	}
diff --git a/twrp-functions.hpp b/twrp-functions.hpp
index 7847aed..efaa460 100755
--- a/twrp-functions.hpp
+++ b/twrp-functions.hpp
@@ -37,6 +37,7 @@
 	rb_poweroff,
 	rb_bootloader,     // May also be fastboot
 	rb_download,
+	rb_edl,
 } RebootCommand;
 
 enum Archive_Type {
diff --git a/twrp.cpp b/twrp.cpp
index 6b9cd75..95e376b 100755
--- a/twrp.cpp
+++ b/twrp.cpp
@@ -345,6 +345,8 @@
 		TWFunc::tw_reboot(rb_bootloader);
 	else if (Reboot_Arg == "download")
 		TWFunc::tw_reboot(rb_download);
+	else if (Reboot_Arg == "edl")
+		TWFunc::tw_reboot(rb_edl);
 	else
 		TWFunc::tw_reboot(rb_system);
 
diff --git a/variables.h b/variables.h
index 22eb5c9..3b61432 100644
--- a/variables.h
+++ b/variables.h
@@ -129,6 +129,7 @@
 #define TW_MIN_SYSTEM_SIZE          "50" // minimum system size to allow a reboot
 #define TW_MIN_SYSTEM_VAR           "tw_min_system"
 #define TW_DOWNLOAD_MODE            "tw_download_mode"
+#define TW_EDL_MODE                 "tw_edl_mode"
 #define TW_IS_ENCRYPTED             "tw_is_encrypted"
 #define TW_IS_DECRYPTED             "tw_is_decrypted"
 #define TW_CRYPTO_PWTYPE            "tw_crypto_pwtype"