MTP: Add support for dynamically adding and removing storage units

BUG: 3402847

Change-Id: I7da266061d949abcb6bb11c6faaa47b5e4a2a977
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index 853a5af..37e02a3 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -84,6 +84,8 @@
 static const MtpEventCode kSupportedEventCodes[] = {
     MTP_EVENT_OBJECT_ADDED,
     MTP_EVENT_OBJECT_REMOVED,
+    MTP_EVENT_STORE_ADDED,
+    MTP_EVENT_STORE_REMOVED,
 };
 
 MtpServer::MtpServer(int fd, MtpDatabase* database,
@@ -104,11 +106,23 @@
 MtpServer::~MtpServer() {
 }
 
-void MtpServer::addStorage(const char* filePath, uint64_t reserveSpace) {
-    int index = mStorages.size() + 1;
-    index |= index << 16;   // set high and low part to our index
-    MtpStorage* storage = new MtpStorage(index, filePath, reserveSpace);
-    addStorage(storage);
+void MtpServer::addStorage(MtpStorage* storage) {
+    Mutex::Autolock autoLock(mMutex);
+
+    mStorages.push(storage);
+    sendStoreAdded(storage->getStorageID());
+}
+
+void MtpServer::removeStorage(MtpStorage* storage) {
+    Mutex::Autolock autoLock(mMutex);
+
+    for (int i = 0; i < mStorages.size(); i++) {
+        if (mStorages[i] == storage) {
+            mStorages.removeAt(i);
+            sendStoreRemoved(storage->getStorageID());
+            break;
+        }
+    }
 }
 
 MtpStorage* MtpServer::getStorage(MtpStorageID id) {
@@ -122,6 +136,12 @@
     return NULL;
 }
 
+bool MtpServer::hasStorage(MtpStorageID id) {
+    if (id == 0 || id == 0xFFFFFFFF)
+        return mStorages.size() > 0;
+    return (getStorage(id) != NULL);
+}
+
 void MtpServer::run() {
     int fd = mFD;
 
@@ -203,28 +223,38 @@
 }
 
 void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
-    if (mSessionOpen) {
-        LOGV("sendObjectAdded %d\n", handle);
-        mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED);
-        mEvent.setTransactionID(mRequest.getTransactionID());
-        mEvent.setParameter(1, handle);
-        int ret = mEvent.write(mFD);
-        LOGV("mEvent.write returned %d\n", ret);
-    }
+    LOGV("sendObjectAdded %d\n", handle);
+    sendEvent(MTP_EVENT_OBJECT_ADDED, handle);
 }
 
 void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
+    LOGV("sendObjectRemoved %d\n", handle);
+    sendEvent(MTP_EVENT_OBJECT_REMOVED, handle);
+}
+
+void MtpServer::sendStoreAdded(MtpStorageID id) {
+    LOGV("sendStoreAdded %08X\n", id);
+    sendEvent(MTP_EVENT_STORE_ADDED, id);
+}
+
+void MtpServer::sendStoreRemoved(MtpStorageID id) {
+    LOGV("sendStoreRemoved %08X\n", id);
+    sendEvent(MTP_EVENT_STORE_REMOVED, id);
+}
+
+void MtpServer::sendEvent(MtpEventCode code, uint32_t param1) {
     if (mSessionOpen) {
-        LOGV("sendObjectRemoved %d\n", handle);
-        mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED);
+        mEvent.setEventCode(code);
         mEvent.setTransactionID(mRequest.getTransactionID());
-        mEvent.setParameter(1, handle);
+        mEvent.setParameter(1, param1);
         int ret = mEvent.write(mFD);
         LOGV("mEvent.write returned %d\n", ret);
     }
 }
 
 bool MtpServer::handleRequest() {
+    Mutex::Autolock autoLock(mMutex);
+
     MtpOperationCode operation = mRequest.getOperationCode();
     MtpResponseCode response;
 
@@ -439,6 +469,9 @@
     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;
     if (parent == 0xFFFFFFFF)
         parent = 0;
 
@@ -455,6 +488,8 @@
     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;
     if (parent == 0xFFFFFFFF)
         parent = 0;
 
@@ -471,7 +506,9 @@
 MtpResponseCode MtpServer::doGetObjectReferences() {
     if (!mSessionOpen)
         return MTP_RESPONSE_SESSION_NOT_OPEN;
-    MtpStorageID handle = mRequest.getParameter(1);
+    if (!hasStorage())
+        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+    MtpObjectHandle handle = mRequest.getParameter(1);
 
     // FIXME - check for invalid object handle
     MtpObjectHandleList* handles = mDatabase->getObjectReferences(handle);
@@ -487,7 +524,10 @@
 MtpResponseCode MtpServer::doSetObjectReferences() {
     if (!mSessionOpen)
         return MTP_RESPONSE_SESSION_NOT_OPEN;
+    if (!hasStorage())
+        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
     MtpStorageID handle = mRequest.getParameter(1);
+
     MtpObjectHandleList* references = mData.getAUInt32();
     MtpResponseCode result = mDatabase->setObjectReferences(handle, references);
     delete references;
@@ -495,6 +535,8 @@
 }
 
 MtpResponseCode MtpServer::doGetObjectPropValue() {
+    if (!hasStorage())
+        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
     MtpObjectHandle handle = mRequest.getParameter(1);
     MtpObjectProperty property = mRequest.getParameter(2);
     LOGV("GetObjectPropValue %d %s\n", handle,
@@ -504,6 +546,8 @@
 }
 
 MtpResponseCode MtpServer::doSetObjectPropValue() {
+    if (!hasStorage())
+        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
     MtpObjectHandle handle = mRequest.getParameter(1);
     MtpObjectProperty property = mRequest.getParameter(2);
     LOGV("SetObjectPropValue %d %s\n", handle,
@@ -537,6 +581,8 @@
 }
 
 MtpResponseCode MtpServer::doGetObjectPropList() {
+    if (!hasStorage())
+        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
 
     MtpObjectHandle handle = mRequest.getParameter(1);
     // use uint32_t so we can support 0xFFFFFFFF
@@ -552,11 +598,15 @@
 }
 
 MtpResponseCode MtpServer::doGetObjectInfo() {
+    if (!hasStorage())
+        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
     MtpObjectHandle handle = mRequest.getParameter(1);
     return mDatabase->getObjectInfo(handle, mData);
 }
 
 MtpResponseCode MtpServer::doGetObject() {
+    if (!hasStorage())
+        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
     MtpObjectHandle handle = mRequest.getParameter(1);
     MtpString pathBuf;
     int64_t fileLength;
@@ -592,6 +642,8 @@
 }
 
 MtpResponseCode MtpServer::doGetPartialObject() {
+    if (!hasStorage())
+        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
     MtpObjectHandle handle = mRequest.getParameter(1);
     uint32_t offset = mRequest.getParameter(2);
     uint32_t length = mRequest.getParameter(3);
@@ -688,6 +740,7 @@
     if (mSendObjectFileSize > storage->getFreeSpace())
         return MTP_RESPONSE_STORAGE_FULL;
 
+LOGD("path: %s parent: %d storageID: %08X", (const char*)path, parent, storageID);
     MtpObjectHandle handle = mDatabase->beginSendObject((const char*)path,
             format, parent, storageID, mSendObjectFileSize, modifiedTime);
     if (handle == kInvalidObjectHandle) {
@@ -719,6 +772,8 @@
 }
 
 MtpResponseCode MtpServer::doSendObject() {
+    if (!hasStorage())
+        return MTP_RESPONSE_GENERAL_ERROR;
     MtpResponseCode result = MTP_RESPONSE_OK;
     mode_t mask;
     int ret;
@@ -835,6 +890,8 @@
 }
 
 MtpResponseCode MtpServer::doDeleteObject() {
+    if (!hasStorage())
+        return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
     MtpObjectHandle handle = mRequest.getParameter(1);
     MtpObjectFormat format = mRequest.getParameter(2);
     // FIXME - support deleting all objects if handle is 0xFFFFFFFF
diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h
index 605d5a2..1efa715 100644
--- a/media/mtp/MtpServer.h
+++ b/media/mtp/MtpServer.h
@@ -22,9 +22,10 @@
 #include "MtpResponsePacket.h"
 #include "MtpEventPacket.h"
 #include "mtp.h"
-
 #include "MtpUtils.h"
 
+#include <utils/threads.h>
+
 namespace android {
 
 class MtpDatabase;
@@ -62,20 +63,29 @@
     MtpString           mSendObjectFilePath;
     size_t              mSendObjectFileSize;
 
+    Mutex               mMutex;
+
 public:
                         MtpServer(int fd, MtpDatabase* database,
                                     int fileGroup, int filePerm, int directoryPerm);
     virtual             ~MtpServer();
 
-    void                addStorage(const char* filePath, uint64_t reserveSpace);
-    inline void         addStorage(MtpStorage* storage) { mStorages.push(storage); }
-    MtpStorage*         getStorage(MtpStorageID id);
+    void                addStorage(MtpStorage* storage);
+    void                removeStorage(MtpStorage* storage);
+
     void                run();
 
     void                sendObjectAdded(MtpObjectHandle handle);
     void                sendObjectRemoved(MtpObjectHandle handle);
 
 private:
+    MtpStorage*         getStorage(MtpStorageID id);
+    inline bool         hasStorage() { return mStorages.size() > 0; }
+    bool                hasStorage(MtpStorageID id);
+    void                sendStoreAdded(MtpStorageID id);
+    void                sendStoreRemoved(MtpStorageID id);
+    void                sendEvent(MtpEventCode code, uint32_t param1);
+
     bool                handleRequest();
 
     MtpResponseCode     doGetDeviceInfo();
diff --git a/media/mtp/MtpStorage.cpp b/media/mtp/MtpStorage.cpp
index 2fbbc51..6cb88b3 100644
--- a/media/mtp/MtpStorage.cpp
+++ b/media/mtp/MtpStorage.cpp
@@ -59,7 +59,7 @@
 uint64_t MtpStorage::getMaxCapacity() {
     if (mMaxCapacity == 0) {
         struct statfs   stat;
-        if (statfs(mFilePath, &stat))
+        if (statfs(getPath(), &stat))
             return -1;
         mMaxCapacity = (uint64_t)stat.f_blocks * (uint64_t)stat.f_bsize;
     }
@@ -68,7 +68,7 @@
 
 uint64_t MtpStorage::getFreeSpace() {
     struct statfs   stat;
-    if (statfs(mFilePath, &stat))
+    if (statfs(getPath(), &stat))
         return -1;
     uint64_t freeSpace = (uint64_t)stat.f_bavail * (uint64_t)stat.f_bsize;
     return (freeSpace > mReserveSpace ? freeSpace - mReserveSpace : 0);
diff --git a/media/mtp/MtpStorage.h b/media/mtp/MtpStorage.h
index ace720b..858c9d3 100644
--- a/media/mtp/MtpStorage.h
+++ b/media/mtp/MtpStorage.h
@@ -17,6 +17,7 @@
 #ifndef _MTP_STORAGE_H
 #define _MTP_STORAGE_H
 
+#include "MtpTypes.h"
 #include "mtp.h"
 
 namespace android {
@@ -27,7 +28,7 @@
 
 private:
     MtpStorageID            mStorageID;
-    const char*             mFilePath;
+    MtpString               mFilePath;
     uint64_t                mMaxCapacity;
     // amount of free space to leave unallocated
     uint64_t                mReserveSpace;
@@ -44,7 +45,7 @@
     uint64_t                getMaxCapacity();
     uint64_t                getFreeSpace();
     const char*             getDescription() const;
-    inline const char*      getPath() const { return mFilePath; }
+    inline const char*      getPath() const { return (const char *)mFilePath; }
 };
 
 }; // namespace android
diff --git a/media/mtp/mtp.h b/media/mtp/mtp.h
index 8bc2e22..6fedc16 100644
--- a/media/mtp/mtp.h
+++ b/media/mtp/mtp.h
@@ -22,6 +22,8 @@
 
 #define MTP_STANDARD_VERSION            100
 
+#define MTP_FIRST_STORAGE_ID            0x00010001
+
 // Container Types
 #define MTP_CONTAINER_TYPE_UNDEFINED    0
 #define MTP_CONTAINER_TYPE_COMMAND      1