Merge "Remove mkvparser code from /frameworks/base"
diff --git a/include/media/stagefright/foundation/ABitReader.h b/include/media/stagefright/foundation/ABitReader.h
index 5135211..5510b12 100644
--- a/include/media/stagefright/foundation/ABitReader.h
+++ b/include/media/stagefright/foundation/ABitReader.h
@@ -31,6 +31,8 @@
     uint32_t getBits(size_t n);
     void skipBits(size_t n);
 
+    void putBits(uint32_t x, size_t n);
+
     size_t numBitsLeft() const;
 
     const uint8_t *data() const;
@@ -43,7 +45,6 @@
     size_t mNumBitsLeft;
 
     void fillReservoir();
-    void putBits(uint32_t x, size_t n);
 
     DISALLOW_EVIL_CONSTRUCTORS(ABitReader);
 };
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index fd575fe..0100a17 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -298,6 +298,17 @@
         return INVALID_OPERATION;
     }
 
+    // It appears that if an invalid file descriptor is passed through
+    // binder calls, the server-side of the inter-process function call
+    // is skipped. As a result, the check at the server-side to catch
+    // the invalid file descritpor never gets invoked. This is to workaround
+    // this issue by checking the file descriptor first before passing
+    // it through binder call.
+    if (fd < 0) {
+        LOGE("Invalid file descriptor: %d", fd);
+        return BAD_VALUE;
+    }
+
     status_t ret = mMediaRecorder->setOutputFile(fd, offset, length);
     if (OK != ret) {
         LOGV("setOutputFile failed: %d", ret);
diff --git a/media/libstagefright/foundation/ABitReader.cpp b/media/libstagefright/foundation/ABitReader.cpp
index 24c8df8..f07dd4f 100644
--- a/media/libstagefright/foundation/ABitReader.cpp
+++ b/media/libstagefright/foundation/ABitReader.cpp
@@ -90,9 +90,7 @@
 }
 
 const uint8_t *ABitReader::data() const {
-    CHECK_EQ(mNumBitsLeft % 8, 0u);
-
-    return mData - mNumBitsLeft / 8;
+    return mData - (mNumBitsLeft + 7) / 8;
 }
 
 }  // namespace android
diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
index 8bfe285..11d9c22 100644
--- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
@@ -14,6 +14,9 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "AMPEG4AudioAssembler"
+
 #include "AMPEG4AudioAssembler.h"
 
 #include "ARTPSource.h"
@@ -139,7 +142,10 @@
     return OK;
 }
 
-static status_t parseAudioSpecificConfig(ABitReader *bits) {
+static status_t parseAudioSpecificConfig(ABitReader *bits, sp<ABuffer> *asc) {
+    const uint8_t *dataStart = bits->data();
+    size_t totalNumBits = bits->numBitsLeft();
+
     unsigned audioObjectType;
     CHECK_EQ(parseAudioObjectType(bits, &audioObjectType), (status_t)OK);
 
@@ -185,13 +191,13 @@
         }
     }
 
-#if 0
-    // This is not supported here as the upper layers did not explicitly
-    // signal the length of AudioSpecificConfig.
-
     if (extensionAudioObjectType != 5 && bits->numBitsLeft() >= 16) {
+        size_t numBitsLeftAtStart = bits->numBitsLeft();
+
         unsigned syncExtensionType = bits->getBits(11);
         if (syncExtensionType == 0x2b7) {
+            LOGI("found syncExtension");
+
             CHECK_EQ(parseAudioObjectType(bits, &extensionAudioObjectType),
                      (status_t)OK);
 
@@ -203,9 +209,45 @@
                     /* unsigned extensionSamplingFrequency = */bits->getBits(24);
                 }
             }
+
+            size_t numBitsInExtension =
+                numBitsLeftAtStart - bits->numBitsLeft();
+
+            if (numBitsInExtension & 7) {
+                // Apparently an extension is always considered an even
+                // multiple of 8 bits long.
+
+                LOGI("Skipping %d bits after sync extension",
+                     8 - (numBitsInExtension & 7));
+
+                bits->skipBits(8 - (numBitsInExtension & 7));
+            }
+        } else {
+            bits->putBits(syncExtensionType, 11);
         }
     }
-#endif
+
+    if (asc != NULL) {
+        size_t bitpos = totalNumBits & 7;
+
+        ABitReader bs(dataStart, (totalNumBits + 7) / 8);
+
+        totalNumBits -= bits->numBitsLeft();
+
+        size_t numBytes = (totalNumBits + 7) / 8;
+
+        *asc = new ABuffer(numBytes);
+
+        if (bitpos & 7) {
+            bs.skipBits(8 - (bitpos & 7));
+        }
+
+        uint8_t *dstPtr = (*asc)->data();
+        while (numBytes > 0) {
+            *dstPtr++ = bs.getBits(8);
+            --numBytes;
+        }
+    }
 
     return OK;
 }
@@ -214,6 +256,7 @@
         ABitReader *bits,
         unsigned *numSubFrames,
         unsigned *frameLengthType,
+        ssize_t *fixedFrameLength,
         bool *otherDataPresent,
         unsigned *otherDataLenBits) {
     unsigned audioMuxVersion = bits->getBits(1);
@@ -242,12 +285,14 @@
 
     if (audioMuxVersion == 0) {
         // AudioSpecificConfig
-        CHECK_EQ(parseAudioSpecificConfig(bits), (status_t)OK);
+        CHECK_EQ(parseAudioSpecificConfig(bits, NULL /* asc */), (status_t)OK);
     } else {
         TRESPASS();  // XXX to be implemented
     }
 
     *frameLengthType = bits->getBits(3);
+    *fixedFrameLength = -1;
+
     switch (*frameLengthType) {
         case 0:
         {
@@ -260,7 +305,14 @@
 
         case 1:
         {
-            /* unsigned frameLength = */bits->getBits(9);
+            *fixedFrameLength = bits->getBits(9);
+            break;
+        }
+
+        case 2:
+        {
+            // reserved
+            TRESPASS();
             break;
         }
 
@@ -338,9 +390,21 @@
                 break;
             }
 
-            default:
-                TRESPASS();  // XXX to be implemented
+            case 2:
+            {
+                // reserved
+
+                TRESPASS();
                 break;
+            }
+
+            default:
+            {
+                CHECK_GE(mFixedFrameLength, 0);
+
+                payloadLength = mFixedFrameLength;
+                break;
+            }
         }
 
         CHECK_LE(offset + payloadLength, buffer->size());
@@ -393,6 +457,7 @@
     ABitReader bits(config->data(), config->size());
     status_t err = parseStreamMuxConfig(
             &bits, &mNumSubFrames, &mFrameLengthType,
+            &mFixedFrameLength,
             &mOtherDataPresent, &mOtherDataLenBits);
 
     CHECK_EQ(err, (status_t)NO_ERROR);
diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.h b/media/libstagefright/rtsp/AMPEG4AudioAssembler.h
index 9cef94c..1361cd2 100644
--- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.h
+++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.h
@@ -46,6 +46,7 @@
     bool mMuxConfigPresent;
     unsigned mNumSubFrames;
     unsigned mFrameLengthType;
+    ssize_t mFixedFrameLength;
     bool mOtherDataPresent;
     unsigned mOtherDataLenBits;
 
diff --git a/media/libstagefright/rtsp/APacketSource.cpp b/media/libstagefright/rtsp/APacketSource.cpp
index 679dcab..6819fef 100644
--- a/media/libstagefright/rtsp/APacketSource.cpp
+++ b/media/libstagefright/rtsp/APacketSource.cpp
@@ -20,6 +20,7 @@
 
 #include "APacketSource.h"
 
+#include "ARawAudioAssembler.h"
 #include "ASessionDescription.h"
 
 #include "avc_utils.h"
@@ -661,6 +662,8 @@
         mFormat->setData(
                 kKeyESDS, 0,
                 codecSpecificData->data(), codecSpecificData->size());
+    } else if (ARawAudioAssembler::Supports(desc.c_str())) {
+        ARawAudioAssembler::MakeFormat(desc.c_str(), mFormat);
     } else {
         mInitCheck = ERROR_UNSUPPORTED;
     }
diff --git a/media/libstagefright/rtsp/ARTPSource.cpp b/media/libstagefright/rtsp/ARTPSource.cpp
index 84c666f..3aa07ce 100644
--- a/media/libstagefright/rtsp/ARTPSource.cpp
+++ b/media/libstagefright/rtsp/ARTPSource.cpp
@@ -25,6 +25,7 @@
 #include "AH263Assembler.h"
 #include "AMPEG4AudioAssembler.h"
 #include "AMPEG4ElementaryAssembler.h"
+#include "ARawAudioAssembler.h"
 #include "ASessionDescription.h"
 
 #include <media/stagefright/foundation/ABuffer.h>
@@ -70,6 +71,8 @@
             || !strncasecmp(desc.c_str(), "mpeg4-generic/", 14)) {
         mAssembler = new AMPEG4ElementaryAssembler(notify, desc, params);
         mIssueFIRRequests = true;
+    } else if (ARawAudioAssembler::Supports(desc.c_str())) {
+        mAssembler = new ARawAudioAssembler(notify, desc.c_str(), params);
     } else {
         TRESPASS();
     }
diff --git a/media/libstagefright/rtsp/ARawAudioAssembler.cpp b/media/libstagefright/rtsp/ARawAudioAssembler.cpp
new file mode 100644
index 0000000..dd47ea3
--- /dev/null
+++ b/media/libstagefright/rtsp/ARawAudioAssembler.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2011 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_NDEBUG 0
+#define LOG_TAG "ARawAudioAssembler"
+#include <utils/Log.h>
+
+#include "ARawAudioAssembler.h"
+
+#include "ARTPSource.h"
+#include "ASessionDescription.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+ARawAudioAssembler::ARawAudioAssembler(
+        const sp<AMessage> &notify, const char *desc, const AString &params)
+    : mNotifyMsg(notify),
+      mNextExpectedSeqNoValid(false),
+      mNextExpectedSeqNo(0) {
+}
+
+ARawAudioAssembler::~ARawAudioAssembler() {
+}
+
+ARTPAssembler::AssemblyStatus ARawAudioAssembler::assembleMore(
+        const sp<ARTPSource> &source) {
+    return addPacket(source);
+}
+
+ARTPAssembler::AssemblyStatus ARawAudioAssembler::addPacket(
+        const sp<ARTPSource> &source) {
+    List<sp<ABuffer> > *queue = source->queue();
+
+    if (queue->empty()) {
+        return NOT_ENOUGH_DATA;
+    }
+
+    if (mNextExpectedSeqNoValid) {
+        List<sp<ABuffer> >::iterator it = queue->begin();
+        while (it != queue->end()) {
+            if ((uint32_t)(*it)->int32Data() >= mNextExpectedSeqNo) {
+                break;
+            }
+
+            it = queue->erase(it);
+        }
+
+        if (queue->empty()) {
+            return NOT_ENOUGH_DATA;
+        }
+    }
+
+    sp<ABuffer> buffer = *queue->begin();
+
+    if (!mNextExpectedSeqNoValid) {
+        mNextExpectedSeqNoValid = true;
+        mNextExpectedSeqNo = (uint32_t)buffer->int32Data();
+    } else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) {
+        LOGV("Not the sequence number I expected");
+
+        return WRONG_SEQUENCE_NUMBER;
+    }
+
+    // hexdump(buffer->data(), buffer->size());
+
+    if (buffer->size() < 1) {
+        queue->erase(queue->begin());
+        ++mNextExpectedSeqNo;
+
+        LOGV("raw audio packet too short.");
+
+        return MALFORMED_PACKET;
+    }
+
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setObject("access-unit", buffer);
+    msg->post();
+
+    queue->erase(queue->begin());
+    ++mNextExpectedSeqNo;
+
+    return OK;
+}
+
+void ARawAudioAssembler::packetLost() {
+    CHECK(mNextExpectedSeqNoValid);
+    ++mNextExpectedSeqNo;
+}
+
+void ARawAudioAssembler::onByeReceived() {
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setInt32("eos", true);
+    msg->post();
+}
+
+// static
+bool ARawAudioAssembler::Supports(const char *desc) {
+    return !strncmp(desc, "PCMU/", 5)
+        || !strncmp(desc, "PCMA/", 5);
+}
+
+// static
+void ARawAudioAssembler::MakeFormat(
+        const char *desc, const sp<MetaData> &format) {
+    if (!strncmp(desc, "PCMU/", 5)) {
+        format->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_MLAW);
+    } else if (!strncmp(desc, "PCMA/", 5)) {
+        format->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_ALAW);
+    } else {
+        TRESPASS();
+    }
+
+    int32_t sampleRate, numChannels;
+    ASessionDescription::ParseFormatDesc(
+            desc, &sampleRate, &numChannels);
+
+    format->setInt32(kKeySampleRate, sampleRate);
+    format->setInt32(kKeyChannelCount, numChannels);
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/rtsp/ARawAudioAssembler.h b/media/libstagefright/rtsp/ARawAudioAssembler.h
new file mode 100644
index 0000000..ed7af08
--- /dev/null
+++ b/media/libstagefright/rtsp/ARawAudioAssembler.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2011 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 A_RAW_AUDIO_ASSEMBLER_H_
+
+#define A_RAW_AUDIO_ASSEMBLER_H_
+
+#include "ARTPAssembler.h"
+
+namespace android {
+
+struct AMessage;
+struct AString;
+struct MetaData;
+
+struct ARawAudioAssembler : public ARTPAssembler {
+    ARawAudioAssembler(
+            const sp<AMessage> &notify,
+            const char *desc, const AString &params);
+
+    static bool Supports(const char *desc);
+
+    static void MakeFormat(
+            const char *desc, const sp<MetaData> &format);
+
+protected:
+    virtual ~ARawAudioAssembler();
+
+    virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source);
+    virtual void onByeReceived();
+    virtual void packetLost();
+
+private:
+    bool mIsWide;
+
+    sp<AMessage> mNotifyMsg;
+    bool mNextExpectedSeqNoValid;
+    uint32_t mNextExpectedSeqNo;
+
+    AssemblyStatus addPacket(const sp<ARTPSource> &source);
+
+    DISALLOW_EVIL_CONSTRUCTORS(ARawAudioAssembler);
+};
+
+}  // namespace android
+
+#endif  // A_RAW_AUDIO_ASSEMBLER_H_
diff --git a/media/libstagefright/rtsp/Android.mk b/media/libstagefright/rtsp/Android.mk
index fb42de8..8530ff3 100644
--- a/media/libstagefright/rtsp/Android.mk
+++ b/media/libstagefright/rtsp/Android.mk
@@ -9,6 +9,7 @@
         AMPEG4AudioAssembler.cpp    \
         AMPEG4ElementaryAssembler.cpp \
         APacketSource.cpp           \
+        ARawAudioAssembler.cpp      \
         ARTPAssembler.cpp           \
         ARTPConnection.cpp          \
         ARTPSource.cpp              \
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