MTP FFS updates:

This update splits old MTP code and new MTP code from Google
into two trees, legacy and ffs. Depending on the SDK level,
the build system will select the correct version. The reason
for separating the versions out are due to older android trees
not supporting the updated MTP code from Google.

Most MTP code is from Google, with additions needed from
implementing the Java functions in C++ for TWRP and FFS.

We assume if you are in android-9.0 or above, your kernel
has support for FFS over MTP. Verify that your init.rc
is mounting the MTP FFS driver to the proper location.

Change-Id: I4b107b239bd9bc5699527f9c8c77d9079f264a7e
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