MTP: Add support for sending events to the host when objects are added and removed

Change-Id: Ia1d5232b919c644c670ff9ca651eca92b3f9ad42
Signed-off-by: Mike Lockwood <lockwood@android.com>
diff --git a/media/java/android/media/MtpServer.java b/media/java/android/media/MtpServer.java
index 766a86a..b0945a5 100644
--- a/media/java/android/media/MtpServer.java
+++ b/media/java/android/media/MtpServer.java
@@ -47,6 +47,14 @@
         native_stop();
     }
 
+    public void sendObjectAdded(int handle) {
+        native_send_object_added(handle);
+    }
+
+    public void sendObjectRemoved(int handle) {
+        native_send_object_removed(handle);
+    }
+
     // used by the JNI code
     private int mNativeContext;
 
@@ -54,4 +62,6 @@
     private native final void native_finalize();
     private native final void native_start();
     private native final void native_stop();
+    private native final void native_send_object_added(int handle);
+    private native final void native_send_object_removed(int handle);
 }
diff --git a/media/jni/android_media_MtpServer.cpp b/media/jni/android_media_MtpServer.cpp
index 17e85f8..0883527 100644
--- a/media/jni/android_media_MtpServer.cpp
+++ b/media/jni/android_media_MtpServer.cpp
@@ -50,12 +50,14 @@
 class MtpThread : public Thread {
 private:
     MtpDatabase*    mDatabase;
+    MtpServer*      mServer;
     String8 mStoragePath;
     bool mDone;
+    Mutex           mMutex;
 
 public:
     MtpThread(MtpDatabase* database, const char* storagePath)
-        : mDatabase(database), mStoragePath(storagePath), mDone(false)
+        : mDatabase(database), mServer(NULL), mStoragePath(storagePath), mDone(false)
     {
     }
 
@@ -67,14 +69,19 @@
             return false;
         }
 
-        MtpServer* server = new MtpServer(fd, mDatabase, AID_SDCARD_RW, 0664, 0775);
-        server->addStorage(mStoragePath);
+        mMutex.lock();
+        mServer = new MtpServer(fd, mDatabase, AID_SDCARD_RW, 0664, 0775);
+        mServer->addStorage(mStoragePath);
+        mMutex.unlock();
 
-        // temporary
-        LOGD("MtpThread server->run");
-        server->run();
+        LOGD("MtpThread mServer->run");
+        mServer->run();
         close(fd);
-        delete server;
+
+        mMutex.lock();
+        delete mServer;
+        mServer = NULL;
+        mMutex.unlock();
 
         bool done = mDone;
         if (done)
@@ -84,6 +91,24 @@
     }
 
     void setDone() { mDone = true; }
+
+    void sendObjectAdded(MtpObjectHandle handle) {
+        mMutex.lock();
+        if (mServer)
+            mServer->sendObjectAdded(handle);
+        else
+            LOGE("sendObjectAdded called while disconnected\n");
+        mMutex.unlock();
+    }
+
+    void sendObjectRemoved(MtpObjectHandle handle) {
+        mMutex.lock();
+        if (mServer)
+            mServer->sendObjectRemoved(handle);
+        else
+            LOGE("sendObjectRemoved called while disconnected\n");
+        mMutex.unlock();
+    }
 };
 
 static void
@@ -126,14 +151,38 @@
     }
 }
 
+static void
+android_media_MtpServer_send_object_added(JNIEnv *env, jobject thiz, jint handle)
+{
+    LOGD("send_object_added %d\n", handle);
+    MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
+    if (thread)
+        thread->sendObjectAdded(handle);
+    else
+        LOGE("sendObjectAdded called while disconnected\n");
+}
+
+static void
+android_media_MtpServer_send_object_removed(JNIEnv *env, jobject thiz, jint handle)
+{
+    LOGD("send_object_removed %d\n", handle);
+    MtpThread *thread = (MtpThread *)env->GetIntField(thiz, field_context);
+    if (thread)
+        thread->sendObjectRemoved(handle);
+    else
+        LOGE("sendObjectRemoved called while disconnected\n");
+}
+
 // ----------------------------------------------------------------------------
 
 static JNINativeMethod gMethods[] = {
-    {"native_setup",            "(Landroid/media/MtpDatabase;Ljava/lang/String;)V",
+    {"native_setup",                "(Landroid/media/MtpDatabase;Ljava/lang/String;)V",
                                             (void *)android_media_MtpServer_setup},
-    {"native_finalize",         "()V",  (void *)android_media_MtpServer_finalize},
-    {"native_start",            "()V",  (void *)android_media_MtpServer_start},
-    {"native_stop",             "()V",  (void *)android_media_MtpServer_stop},
+    {"native_finalize",             "()V",  (void *)android_media_MtpServer_finalize},
+    {"native_start",                "()V",  (void *)android_media_MtpServer_start},
+    {"native_stop",                 "()V",  (void *)android_media_MtpServer_stop},
+    {"native_send_object_added",    "(I)V", (void *)android_media_MtpServer_send_object_added},
+    {"native_send_object_removed",  "(I)V", (void *)android_media_MtpServer_send_object_removed},
 };
 
 static const char* const kClassPathName = "android/media/MtpServer";
diff --git a/media/mtp/Android.mk b/media/mtp/Android.mk
index 174ea36..4659709 100644
--- a/media/mtp/Android.mk
+++ b/media/mtp/Android.mk
@@ -23,6 +23,7 @@
                   MtpDataPacket.cpp                     \
                   MtpDebug.cpp                          \
                   MtpDevice.cpp                         \
+                  MtpEventPacket.cpp                    \
                   MtpDeviceInfo.cpp                     \
                   MtpObjectInfo.cpp                     \
                   MtpPacket.cpp                         \
diff --git a/media/mtp/MtpEventPacket.cpp b/media/mtp/MtpEventPacket.cpp
new file mode 100644
index 0000000..651761e
--- /dev/null
+++ b/media/mtp/MtpEventPacket.cpp
@@ -0,0 +1,65 @@
+/*
+ * 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 <sys/ioctl.h>
+
+#include <f_mtp.h>
+
+#include "MtpEventPacket.h"
+
+namespace android {
+
+MtpEventPacket::MtpEventPacket()
+    :   MtpPacket(512)
+{
+}
+
+MtpEventPacket::~MtpEventPacket() {
+}
+
+#ifdef MTP_DEVICE
+int MtpEventPacket::write(int fd) {
+    struct mtp_event    event;
+
+    putUInt32(MTP_CONTAINER_LENGTH_OFFSET, mPacketSize);
+    putUInt16(MTP_CONTAINER_TYPE_OFFSET, MTP_CONTAINER_TYPE_EVENT);
+
+    event.data = mBuffer;
+    event.length = mPacketSize;
+    int ret = ::ioctl(fd, MTP_SEND_EVENT, (unsigned long)&event);
+    return (ret < 0 ? ret : 0);
+}
+#endif
+
+#ifdef MTP_HOST
+    // read our buffer from the given endpoint
+int MtpEventPacket::read(struct usb_endpoint *ep) {
+    int ret = transfer(ep, mBuffer, mBufferSize);
+     if (ret >= 0)
+        mPacketSize = ret;
+    else
+        mPacketSize = 0;
+    return ret;
+}
+#endif
+
+}  // namespace android
+
diff --git a/media/mtp/MtpEventPacket.h b/media/mtp/MtpEventPacket.h
new file mode 100644
index 0000000..30ae869
--- /dev/null
+++ b/media/mtp/MtpEventPacket.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _MTP_EVENT_PACKET_H
+#define _MTP_EVENT_PACKET_H
+
+#include "MtpPacket.h"
+#include "mtp.h"
+
+namespace android {
+
+class MtpEventPacket : public MtpPacket {
+
+public:
+                        MtpEventPacket();
+    virtual             ~MtpEventPacket();
+
+#ifdef MTP_DEVICE
+    // write our data to the given file descriptor
+    int                 write(int fd);
+#endif
+
+#ifdef MTP_HOST
+    // read our buffer from the given endpoint
+    int                 read(struct usb_endpoint *ep);
+#endif
+
+    inline MtpEventCode     getEventCode() const { return getContainerCode(); }
+    inline void             setEventCode(MtpEventCode code)
+                                                     { return setContainerCode(code); }
+};
+
+}; // namespace android
+
+#endif // _MTP_EVENT_PACKET_H
diff --git a/media/mtp/MtpServer.cpp b/media/mtp/MtpServer.cpp
index e8f09fa..fda5f8f 100644
--- a/media/mtp/MtpServer.cpp
+++ b/media/mtp/MtpServer.cpp
@@ -73,6 +73,11 @@
 //    MTP_OPERATION_SKIP,
 };
 
+static const MtpEventCode kSupportedEventCodes[] = {
+    MTP_EVENT_OBJECT_ADDED,
+    MTP_EVENT_OBJECT_REMOVED,
+};
+
 static const MtpObjectProperty kSupportedObjectProperties[] = {
     MTP_PROPERTY_STORAGE_ID,
     MTP_PROPERTY_OBJECT_FORMAT,
@@ -239,6 +244,24 @@
     return NULL;
 }
 
+void MtpServer::sendObjectAdded(MtpObjectHandle handle) {
+    LOGD("sendObjectAdded %d\n", handle);
+    mEvent.setEventCode(MTP_EVENT_OBJECT_ADDED);
+    mEvent.setTransactionID(mRequest.getTransactionID());
+    mEvent.setParameter(1, handle);
+    int ret = mEvent.write(mFD);
+    LOGD("mEvent.write returned %d\n", ret);
+}
+
+void MtpServer::sendObjectRemoved(MtpObjectHandle handle) {
+    LOGD("sendObjectRemoved %d\n", handle);
+    mEvent.setEventCode(MTP_EVENT_OBJECT_REMOVED);
+    mEvent.setTransactionID(mRequest.getTransactionID());
+    mEvent.setParameter(1, handle);
+    int ret = mEvent.write(mFD);
+    LOGD("mEvent.write returned %d\n", ret);
+}
+
 void MtpServer::initObjectProperties() {
     mObjectProperties.push(new MtpProperty(MTP_PROPERTY_STORAGE_ID, MTP_TYPE_UINT16));
     mObjectProperties.push(new MtpProperty(MTP_PROPERTY_OBJECT_FORMAT, MTP_TYPE_UINT16));
@@ -326,7 +349,8 @@
     mData.putUInt16(0); //Functional Mode
     mData.putAUInt16(kSupportedOperationCodes,
             sizeof(kSupportedOperationCodes) / sizeof(uint16_t)); // Operations Supported
-    mData.putEmptyArray(); // Events Supported
+    mData.putAUInt16(kSupportedEventCodes,
+            sizeof(kSupportedEventCodes) / sizeof(uint16_t)); // Events Supported
     mData.putEmptyArray(); // Device Properties Supported
     mData.putEmptyArray(); // Capture Formats
     mData.putAUInt16(kSupportedPlaybackFormats,
diff --git a/media/mtp/MtpServer.h b/media/mtp/MtpServer.h
index 37b1cbe..aff973a 100644
--- a/media/mtp/MtpServer.h
+++ b/media/mtp/MtpServer.h
@@ -20,6 +20,7 @@
 #include "MtpRequestPacket.h"
 #include "MtpDataPacket.h"
 #include "MtpResponsePacket.h"
+#include "MtpEventPacket.h"
 #include "mtp.h"
 
 #include "MtpUtils.h"
@@ -52,6 +53,7 @@
     MtpRequestPacket    mRequest;
     MtpDataPacket       mData;
     MtpResponsePacket   mResponse;
+    MtpEventPacket      mEvent;
 
     MtpStorageList      mStorages;
 
@@ -77,6 +79,9 @@
     MtpProperty*        getObjectProperty(MtpPropertyCode propCode);
     MtpProperty*        getDeviceProperty(MtpPropertyCode propCode);
 
+    void                sendObjectAdded(MtpObjectHandle handle);
+    void                sendObjectRemoved(MtpObjectHandle handle);
+
 private:
     void                initObjectProperties();
 
diff --git a/media/mtp/MtpTypes.h b/media/mtp/MtpTypes.h
index ec0f867..2a895a7 100644
--- a/media/mtp/MtpTypes.h
+++ b/media/mtp/MtpTypes.h
@@ -28,6 +28,7 @@
 
 typedef uint16_t MtpOperationCode;
 typedef uint16_t MtpResponseCode;
+typedef uint16_t MtpEventCode;
 typedef uint32_t MtpSessionID;
 typedef uint32_t MtpStorageID;
 typedef uint32_t MtpTransactionID;
diff --git a/media/mtp/f_mtp.h b/media/mtp/f_mtp.h
index c1c9aef..426c6b5 100644
--- a/media/mtp/f_mtp.h
+++ b/media/mtp/f_mtp.h
@@ -32,6 +32,13 @@
 	size_t		length;
 };
 
+struct mtp_event {
+	/* size of the event */
+	size_t		length;
+	/* event data to send */
+	void  		*data;
+};
+
 /* Sends the specified file range to the host */
 #define MTP_SEND_FILE              _IOW('M', 0, struct mtp_file_range)
 /* Receives data from the host and writes it to a file.
@@ -40,5 +47,7 @@
 #define MTP_RECEIVE_FILE           _IOW('M', 1, struct mtp_file_range)
 /* Sets the driver mode to either MTP or PTP */
 #define MTP_SET_INTERFACE_MODE     _IOW('M', 2, int)
+/* Sends an event to the host via the interrupt endpoint */
+#define MTP_SEND_EVENT             _IOW('M', 3, struct mtp_event)
 
 #endif /* __LINUX_USB_F_MTP_H */