Merge "MTP: Remove an unnecessary thread from the MtpClient class."
diff --git a/media/java/android/media/MtpClient.java b/media/java/android/media/MtpClient.java
index ad6640f..0fe9bb4 100644
--- a/media/java/android/media/MtpClient.java
+++ b/media/java/android/media/MtpClient.java
@@ -44,9 +44,12 @@
         native_finalize();
     }
 
-    public void start() {
-        mEventThread = new MtpEventThread();
-        mEventThread.start();
+    public boolean start() {
+        return native_start();
+    }
+
+    public void stop() {
+        native_stop();
     }
 
     public boolean deleteObject(int deviceID, int objectID) {
@@ -61,24 +64,6 @@
         return native_get_storage_id(deviceID, objectID);
     }
 
-    private class MtpEventThread extends Thread {
-
-        private boolean mDone;
-
-        public MtpEventThread() {
-            super("MtpEventThread");
-        }
-
-        public void run() {
-            Log.d(TAG, "MtpEventThread starting");
-            while (!mDone) {
-                // this will wait for an event from an MTP device
-                native_wait_for_event();
-            }
-            Log.d(TAG, "MtpEventThread exiting");
-        }
-    }
-
     public interface Listener {
         // called when a new MTP device has been discovered
         void deviceAdded(int id);
@@ -99,14 +84,13 @@
         mListener.deviceRemoved(id);
     }
 
-    private MtpEventThread mEventThread;
-
     // used by the JNI code
     private int mNativeContext;
 
     private native final void native_setup();
     private native final void native_finalize();
-    private native void native_wait_for_event();
+    private native boolean native_start();
+    private native void native_stop();
     private native boolean native_delete_object(int deviceID, int objectID);
     private native int native_get_parent(int deviceID, int objectID);
     private native int native_get_storage_id(int deviceID, int objectID);
diff --git a/media/jni/android_media_MtpClient.cpp b/media/jni/android_media_MtpClient.cpp
index 2328889..5c397a6 100644
--- a/media/jni/android_media_MtpClient.cpp
+++ b/media/jni/android_media_MtpClient.cpp
@@ -22,7 +22,6 @@
 #include <limits.h>
 #include <unistd.h>
 #include <fcntl.h>
-#include <utils/threads.h>
 
 #include "jni.h"
 #include "JNIHelp.h"
@@ -39,34 +38,25 @@
 static jmethodID method_deviceRemoved;
 static jfieldID field_context;
 
+static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
+    if (env->ExceptionCheck()) {
+        LOGE("An exception was thrown by callback '%s'.", methodName);
+        LOGE_EX(env);
+        env->ExceptionClear();
+    }
+}
+
 class MyClient : public MtpClient {
 private:
-
-    enum {
-        kDeviceAdded = 1,
-        kDeviceRemoved,
-    };
-
     virtual void    deviceAdded(MtpDevice *device);
     virtual void    deviceRemoved(MtpDevice *device);
 
-    bool            reportDeviceAdded(JNIEnv *env, MtpDevice *device);
-    bool            reportDeviceRemoved(JNIEnv *env, MtpDevice *device);
-
-    JNIEnv*         mEnv;
     jobject         mClient;
-    Mutex           mEventLock;
-    Condition       mEventCondition;
-    Mutex           mAckLock;
-    Condition       mAckCondition;
-    int             mEvent;
     MtpDevice*      mEventDevice;
 
 public:
                     MyClient(JNIEnv *env, jobject client);
-    virtual         ~MyClient();
-    void            waitForEvent(JNIEnv *env);
-
+    void            cleanup(JNIEnv *env);
 };
 
 MtpClient* get_client_from_object(JNIEnv* env, jobject javaClient)
@@ -76,87 +66,36 @@
 
 
 MyClient::MyClient(JNIEnv *env, jobject client)
-    :   mEnv(env),
-        mClient(env->NewGlobalRef(client)),
-        mEvent(0),
-        mEventDevice(NULL)
+    :   mClient(env->NewGlobalRef(client))
 {
 }
 
-MyClient::~MyClient() {
-    mEnv->DeleteGlobalRef(mClient);
+void MyClient::cleanup(JNIEnv *env) {
+    env->DeleteGlobalRef(mClient);
 }
 
-
 void MyClient::deviceAdded(MtpDevice *device) {
-    LOGD("MyClient::deviceAdded\n");
-    mAckLock.lock();
-    mEventLock.lock();
-    mEvent = kDeviceAdded;
-    mEventDevice = device;
-    mEventCondition.signal();
-    mEventLock.unlock();
-    mAckCondition.wait(mAckLock);
-    mAckLock.unlock();
-}
-
-void MyClient::deviceRemoved(MtpDevice *device) {
-    LOGD("MyClient::deviceRemoved\n");
-    mAckLock.lock();
-    mEventLock.lock();
-    mEvent = kDeviceRemoved;
-    mEventDevice = device;
-    mEventCondition.signal();
-    mEventLock.unlock();
-    mAckCondition.wait(mAckLock);
-    mAckLock.unlock();
-}
-
-bool MyClient::reportDeviceAdded(JNIEnv *env, MtpDevice *device) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
     const char* name = device->getDeviceName();
-    LOGD("MyClient::reportDeviceAdded %s\n", name);
+    LOGD("MyClient::deviceAdded %s\n", name);
 
     env->CallVoidMethod(mClient, method_deviceAdded, device->getID());
 
-    return (!env->ExceptionCheck());
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
 }
 
-bool MyClient::reportDeviceRemoved(JNIEnv *env, MtpDevice *device) {
-   const char* name = device->getDeviceName();
-    LOGD("MyClient::reportDeviceRemoved %s\n", name);
+void MyClient::deviceRemoved(MtpDevice *device) {
+    JNIEnv* env = AndroidRuntime::getJNIEnv();
+    const char* name = device->getDeviceName();
+    LOGD("MyClient::deviceRemoved %s\n", name);
 
     env->CallVoidMethod(mClient, method_deviceRemoved, device->getID());
 
-    return (!env->ExceptionCheck());
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
 }
 
-void MyClient::waitForEvent(JNIEnv *env) {
-    mEventLock.lock();
-    mEventCondition.wait(mEventLock);
-    mEventLock.unlock();
-
-    switch (mEvent) {
-        case kDeviceAdded:
-            reportDeviceAdded(env, mEventDevice);
-            break;
-        case kDeviceRemoved:
-            reportDeviceRemoved(env, mEventDevice);
-            break;
-    }
-
-    mAckLock.lock();
-    mAckCondition.signal();
-    mAckLock.unlock();
-}
-
-
 // ----------------------------------------------------------------------------
 
-static bool ExceptionCheck(void* env)
-{
-    return ((JNIEnv *)env)->ExceptionCheck();
-}
-
 static void
 android_media_MtpClient_setup(JNIEnv *env, jobject thiz)
 {
@@ -171,15 +110,25 @@
 {
     LOGD("finalize\n");
     MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
+    client->cleanup(env);
     delete client;
+    env->SetIntField(thiz, field_context, 0);
+}
+
+static jboolean
+android_media_MtpClient_start(JNIEnv *env, jobject thiz)
+{
+    LOGD("start\n");
+    MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
+    return client->start();
 }
 
 static void
-android_media_MtpClient_wait_for_event(JNIEnv *env, jobject thiz)
+android_media_MtpClient_stop(JNIEnv *env, jobject thiz)
 {
-    LOGD("wait_for_event\n");
+    LOGD("stop\n");
     MyClient *client = (MyClient *)env->GetIntField(thiz, field_context);
-    client->waitForEvent(env);
+    client->stop();
 }
 
 static jboolean
@@ -223,7 +172,8 @@
 static JNINativeMethod gMethods[] = {
     {"native_setup",            "()V",  (void *)android_media_MtpClient_setup},
     {"native_finalize",         "()V",  (void *)android_media_MtpClient_finalize},
-    {"native_wait_for_event",   "()V",  (void *)android_media_MtpClient_wait_for_event},
+    {"native_start",            "()Z",  (void *)android_media_MtpClient_start},
+    {"native_stop",             "()V",  (void *)android_media_MtpClient_stop},
     {"native_delete_object",   "(II)Z", (void *)android_media_MtpClient_delete_object},
     {"native_get_parent",      "(II)I", (void *)android_media_MtpClient_get_parent},
     {"native_get_storage_id",  "(II)I", (void *)android_media_MtpClient_get_storage_id},
diff --git a/media/mtp/MtpClient.cpp b/media/mtp/MtpClient.cpp
index fbf1c77..b4c47af8 100644
--- a/media/mtp/MtpClient.cpp
+++ b/media/mtp/MtpClient.cpp
@@ -27,6 +27,7 @@
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <errno.h>
+#include <utils/threads.h>
 
 #include <usbhost/usbhost.h>
 #include <linux/version.h>
@@ -38,34 +39,59 @@
 
 namespace android {
 
+class MtpClientThread : public Thread {
+private:
+    MtpClient*   mClient;
+
+public:
+    MtpClientThread(MtpClient* client)
+        : mClient(client)
+    {
+    }
+
+    virtual bool threadLoop() {
+        return mClient->threadLoop();
+    }
+};
+
+
 MtpClient::MtpClient()
-    :   mStarted(false)
+    :   mThread(NULL),
+        mUsbHostContext(NULL),
+        mDone(false)
 {
 }
 
 MtpClient::~MtpClient() {
+    usb_host_cleanup(mUsbHostContext);
 }
 
 bool MtpClient::start() {
-    if (mStarted)
+    if (mThread)
         return true;
 
-    if (usb_host_init(usb_device_added, usb_device_removed, this)) {
-        LOGE("MtpClient::start failed\n");
+    mUsbHostContext = usb_host_init();
+    if (!mUsbHostContext)
         return false;
-    }
-    mStarted = true;
+
+    mThread = new MtpClientThread(this);
+    mThread->run("MtpClientThread");
+
     return true;
 }
 
-void MtpClient::usbDeviceAdded(const char *devname) {
+void MtpClient::stop() {
+    mDone = true;
+}
+
+bool MtpClient::usbDeviceAdded(const char *devname) {
     struct usb_descriptor_header* desc;
     struct usb_descriptor_iter iter;
 
     struct usb_device *device = usb_device_open(devname);
     if (!device) {
         LOGE("usb_device_open failed\n");
-        return;
+        return mDone;
     }
 
     usb_descriptor_iter_init(device, &iter);
@@ -90,7 +116,7 @@
                     ep = (struct usb_endpoint_descriptor *)usb_descriptor_iter_next(&iter);
                     if (!ep || ep->bDescriptorType != USB_DT_ENDPOINT) {
                         LOGE("endpoints not found\n");
-                        return;
+                        return mDone;
                     }
                     if (ep->bmAttributes == USB_ENDPOINT_XFER_BULK) {
                         if (ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
@@ -104,7 +130,7 @@
                 }
                 if (!ep_in_desc || !ep_out_desc || !ep_intr_desc) {
                     LOGE("endpoints not found\n");
-                    return;
+                    return mDone;
                 }
 
                 struct usb_endpoint *ep_in = usb_endpoint_open(device, ep_in_desc);
@@ -116,7 +142,7 @@
                     usb_endpoint_close(ep_in);
                     usb_endpoint_close(ep_out);
                     usb_endpoint_close(ep_intr);
-                    return;
+                    return mDone;
                 }
 
                 MtpDevice* mtpDevice = new MtpDevice(device, interface->bInterfaceNumber,
@@ -124,12 +150,13 @@
                 mDeviceList.add(mtpDevice);
                 mtpDevice->initialize();
                 deviceAdded(mtpDevice);
-                return;
+                return mDone;
             }
         }
     }
 
     usb_device_close(device);
+    return mDone;
 }
 
 MtpDevice* MtpClient::getDevice(int id) {
@@ -141,7 +168,7 @@
     return NULL;
 }
 
-void MtpClient::usbDeviceRemoved(const char *devname) {
+bool MtpClient::usbDeviceRemoved(const char *devname) {
     for (int i = 0; i < mDeviceList.size(); i++) {
         MtpDevice* device = mDeviceList[i];
         if (!strcmp(devname, device->getDeviceName())) {
@@ -152,16 +179,22 @@
             break;
         }
     }
+    return mDone;
 }
 
-void MtpClient::usb_device_added(const char *devname, void* client_data) {
+bool MtpClient::threadLoop() {
+    usb_host_run(mUsbHostContext, usb_device_added, usb_device_removed, this);
+    return false;
+}
+
+int MtpClient::usb_device_added(const char *devname, void* client_data) {
     LOGD("usb_device_added %s\n", devname);
-    ((MtpClient *)client_data)->usbDeviceAdded(devname);
+    return ((MtpClient *)client_data)->usbDeviceAdded(devname);
 }
 
-void MtpClient::usb_device_removed(const char *devname, void* client_data) {
+int MtpClient::usb_device_removed(const char *devname, void* client_data) {
     LOGD("usb_device_removed %s\n", devname);
-    ((MtpClient *)client_data)->usbDeviceRemoved(devname);
+    return ((MtpClient *)client_data)->usbDeviceRemoved(devname);
 }
 
 }  // namespace android
diff --git a/media/mtp/MtpClient.h b/media/mtp/MtpClient.h
index d87c226..907a80b 100644
--- a/media/mtp/MtpClient.h
+++ b/media/mtp/MtpClient.h
@@ -19,18 +19,25 @@
 
 #include "MtpTypes.h"
 
+struct usb_host_context;
+
 namespace android {
 
+class MtpClientThread;
+
 class MtpClient {
 private:
-    MtpDeviceList           mDeviceList;
-    bool                    mStarted;
+    MtpDeviceList               mDeviceList;
+    MtpClientThread*            mThread;
+    struct usb_host_context*    mUsbHostContext;
+    bool                        mDone;
 
 public:
                             MtpClient();
     virtual                 ~MtpClient();
 
     bool                    start();
+    void                    stop();
 
     inline MtpDeviceList&   getDeviceList() { return mDeviceList; }
     MtpDevice*              getDevice(int id);
@@ -40,10 +47,14 @@
     virtual void            deviceRemoved(MtpDevice *device) = 0;
 
 private:
-    void                    usbDeviceAdded(const char *devname);
-    void                    usbDeviceRemoved(const char *devname);
-    static void             usb_device_added(const char *devname, void* client_data);
-    static void             usb_device_removed(const char *devname, void* client_data);
+    // these return true if we should stop monitoring USB and clean up
+    bool                    usbDeviceAdded(const char *devname);
+    bool                    usbDeviceRemoved(const char *devname);
+
+    friend class MtpClientThread;
+    bool                    threadLoop();
+    static int              usb_device_added(const char *devname, void* client_data);
+    static int              usb_device_removed(const char *devname, void* client_data);
 };
 
 }; // namespace android