AI 144392: am: CL 144241 Stop polling dbus.
  Original author: rgreenwalt

Automated import of CL 144392
diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java
index 11c297c..8cc229b 100644
--- a/core/java/android/server/BluetoothEventLoop.java
+++ b/core/java/android/server/BluetoothEventLoop.java
@@ -111,57 +111,27 @@
         return mPasskeyAgentRequestData;
     }
 
-    private synchronized boolean waitForAndDispatchEvent(int timeout_ms) {
-        return waitForAndDispatchEventNative(timeout_ms);
-    }
-    private native boolean waitForAndDispatchEventNative(int timeout_ms);
+    private native void startEventLoopNative();
+    private native void stopEventLoopNative();
+    private native boolean isEventLoopRunningNative();
 
-    /* package */ synchronized void start() {
+    /* package */ void start() {
 
-        if (mThread != null) {
-            // Already running.
-            return;
-        }
-        mThread = new Thread("Bluetooth Event Loop") {
-                @Override
-                public void run() {
-                    try {
-                        if (setUpEventLoopNative()) {
-                            mStarted = true;
-                            while (!mInterrupted) {
-                                waitForAndDispatchEvent(0);
-                                sleep(500);
-                            }
-                        }
-                        // tear down even in the error case to clean
-                        // up anything we started to setup
-                        tearDownEventLoopNative();
-                    } catch (InterruptedException e) { }
-                    if (DBG) log("Event Loop thread finished");
-                    mThread = null;
-                }
-            };
-        if (DBG) log("Starting Event Loop thread");
-        mInterrupted = false;
-        mThread.start();
-    }
-    private native boolean setUpEventLoopNative();
-    private native void tearDownEventLoopNative();
-
-    public synchronized void stop() {
-        if (mThread != null) {
-            mInterrupted = true;
-            try {
-                mThread.join();
-                mThread = null;
-            } catch (InterruptedException e) {
-                Log.i(TAG, "Interrupted waiting for Event Loop thread to join");
-            }
+        if (!isEventLoopRunningNative()) {
+            if (DBG) log("Starting Event Loop thread");
+            startEventLoopNative();
         }
     }
 
-    public synchronized boolean isEventLoopRunning() {
-        return mThread != null && mStarted;
+    public void stop() {
+        if (isEventLoopRunningNative()) {
+            if (DBG) log("Stopping Event Loop thread");
+            stopEventLoopNative();
+        }
+    }
+
+    public boolean isEventLoopRunning() {
+        return isEventLoopRunningNative();
     }
 
     /*package*/ void onModeChanged(String bluezMode) {
diff --git a/core/jni/android_bluetooth_common.cpp b/core/jni/android_bluetooth_common.cpp
index c81af1ce..0b8a604 100644
--- a/core/jni/android_bluetooth_common.cpp
+++ b/core/jni/android_bluetooth_common.cpp
@@ -46,8 +46,9 @@
 }
 
 typedef struct {
-    void (*user_cb)(DBusMessage *, void *);
+    void (*user_cb)(DBusMessage *, void *, void *);
     void *user;
+    void *nat;
     JNIEnv *env;
 } dbus_async_call_t;
 
@@ -63,7 +64,7 @@
     if (msg) {
         if (req->user_cb) {
             // The user may not deref the message object.
-            req->user_cb(msg, req->user);
+            req->user_cb(msg, req->user, req->nat);
         }
         dbus_message_unref(msg);
     }
@@ -74,11 +75,14 @@
     free(req);
 }
 
-dbus_bool_t dbus_func_args_async_valist(JNIEnv *env,
+static dbus_bool_t dbus_func_args_async_valist(JNIEnv *env,
                                         DBusConnection *conn,
                                         int timeout_ms,
-                                        void (*user_cb)(DBusMessage *, void *),
+                                        void (*user_cb)(DBusMessage *,
+                                                        void *,
+                                                        void*),
                                         void *user,
+                                        void *nat,
                                         const char *path,
                                         const char *ifc,
                                         const char *func,
@@ -111,6 +115,7 @@
         pending->env = env;
         pending->user_cb = user_cb;
         pending->user = user;
+        pending->nat = nat;
         //pending->method = msg;
 
         reply = dbus_connection_send_with_reply(conn, msg,
@@ -132,8 +137,9 @@
 dbus_bool_t dbus_func_args_async(JNIEnv *env,
                                  DBusConnection *conn,
                                  int timeout_ms,
-                                 void (*reply)(DBusMessage *, void *),
+                                 void (*reply)(DBusMessage *, void *, void*),
                                  void *user,
+                                 void *nat,
                                  const char *path,
                                  const char *ifc,
                                  const char *func,
@@ -144,7 +150,7 @@
     va_start(lst, first_arg_type);
     ret = dbus_func_args_async_valist(env, conn,
                                       timeout_ms,
-                                      reply, user,
+                                      reply, user, nat,
                                       path, ifc, func,
                                       first_arg_type, lst);
     va_end(lst);
diff --git a/core/jni/android_bluetooth_common.h b/core/jni/android_bluetooth_common.h
index c30ba22..69092dd 100644
--- a/core/jni/android_bluetooth_common.h
+++ b/core/jni/android_bluetooth_common.h
@@ -24,7 +24,9 @@
 #include "utils/Log.h"
 
 #include <errno.h>
+#include <pthread.h>
 #include <stdint.h>
+#include <sys/poll.h>
 
 #ifdef HAVE_BLUETOOTH
 #include <dbus/dbus.h>
@@ -45,6 +47,9 @@
 
 #define BTADDR_SIZE 18   // size of BT address character array (including null)
 
+// size of the dbus event loops pollfd structure, hopefully never to be grown
+#define DEFAULT_INITIAL_POLLFD_COUNT 8
+
 jfieldID get_field(JNIEnv *env,
                    jclass clazz,
                    const char *member,
@@ -63,29 +68,33 @@
 
 struct event_loop_native_data_t {
     DBusConnection *conn;
-    /* These variables are set in waitForAndDispatchEventNative() and are
-       valid only within the scope of this function.  At any other time, they
-       are NULL. */
-    jobject me;
-    JNIEnv *env;
-};
 
-dbus_bool_t dbus_func_args_async_valist(JNIEnv *env,
-                                        DBusConnection *conn,
-                                        int timeout_ms,
-                                        void (*reply)(DBusMessage *, void *),
-                                        void *user,
-                                        const char *path,
-                                        const char *ifc,
-                                        const char *func,
-                                        int first_arg_type,
-                                        va_list args);
+    /* protects the thread */
+    pthread_mutex_t thread_mutex;
+    pthread_t thread;
+    /* our comms socket */
+    /* mem for the list of sockets to listen to */
+    struct pollfd *pollData;
+    int pollMemberCount;
+    int pollDataSize;
+    /* mem for matching set of dbus watch ptrs */
+    DBusWatch **watchData;
+    /* pair of sockets for event loop control, Reader and Writer */
+    int controlFdR;
+    int controlFdW;
+    /* our vm and env Version for future env generation */
+    JavaVM *vm;
+    int envVer;
+    /* reference to our java self */
+    jobject me;
+};
 
 dbus_bool_t dbus_func_args_async(JNIEnv *env,
                                  DBusConnection *conn,
                                  int timeout_ms,
-                                 void (*reply)(DBusMessage *, void *),
+                                 void (*reply)(DBusMessage *, void *, void *),
                                  void *user,
+                                 void *nat,
                                  const char *path,
                                  const char *ifc,
                                  const char *func,
diff --git a/core/jni/android_server_BluetoothA2dpService.cpp b/core/jni/android_server_BluetoothA2dpService.cpp
index 062f893..b320c09 100644
--- a/core/jni/android_server_BluetoothA2dpService.cpp
+++ b/core/jni/android_server_BluetoothA2dpService.cpp
@@ -52,12 +52,11 @@
 
 static native_data_t *nat = NULL;  // global native data
 
-extern event_loop_native_data_t *event_loop_nat;  // for the event loop JNIEnv
 #endif
 
 #ifdef HAVE_BLUETOOTH
-static void onConnectSinkResult(DBusMessage *msg, void *user);
-static void onDisconnectSinkResult(DBusMessage *msg, void *user);
+static void onConnectSinkResult(DBusMessage *msg, void *user, void *nat);
+static void onDisconnectSinkResult(DBusMessage *msg, void *user, void *nat);
 #endif
 
 /* Returns true on success (even if adapter is present but disabled).
@@ -175,7 +174,8 @@
 
         bool ret =
             dbus_func_args_async(env, nat->conn, -1,
-                           onConnectSinkResult, (void *)c_path_copy, c_path,
+                           onConnectSinkResult, (void *)c_path_copy, nat,
+                           c_path,
                            "org.bluez.audio.Sink", "Connect",
                            DBUS_TYPE_INVALID);
 
@@ -202,7 +202,8 @@
 
         bool ret =
             dbus_func_args_async(env, nat->conn, -1,
-                           onDisconnectSinkResult, (void *)c_path_copy, c_path,
+                           onDisconnectSinkResult, (void *)c_path_copy, nat,
+                           c_path,
                            "org.bluez.audio.Sink", "Disconnect",
                            DBUS_TYPE_INVALID);
         env->ReleaseStringUTFChars(path, c_path);
@@ -233,13 +234,13 @@
 }
 
 #ifdef HAVE_BLUETOOTH
-static void onConnectSinkResult(DBusMessage *msg, void *user) {
+static void onConnectSinkResult(DBusMessage *msg, void *user, void *natData) {
     LOGV(__FUNCTION__);
 
     char *c_path = (char *)user;
     DBusError err;
     dbus_error_init(&err);
-    JNIEnv *env = event_loop_nat->env;
+    JNIEnv *env = nat->env;
 
     LOGV("... path = %s", c_path);
     if (dbus_set_error_from_message(&err, msg)) {
@@ -258,13 +259,13 @@
     free(c_path);
 }
 
-static void onDisconnectSinkResult(DBusMessage *msg, void *user) {
+static void onDisconnectSinkResult(DBusMessage *msg, void *user, void *natData) {
     LOGV(__FUNCTION__);
 
     char *c_path = (char *)user;
     DBusError err;
     dbus_error_init(&err);
-    JNIEnv *env = event_loop_nat->env;
+    JNIEnv *env = nat->env;
 
     LOGV("... path = %s", c_path);
     if (dbus_set_error_from_message(&err, msg)) {
diff --git a/core/jni/android_server_BluetoothDeviceService.cpp b/core/jni/android_server_BluetoothDeviceService.cpp
index a0e0b84..b6e9798 100644
--- a/core/jni/android_server_BluetoothDeviceService.cpp
+++ b/core/jni/android_server_BluetoothDeviceService.cpp
@@ -50,6 +50,7 @@
 // We initialize these variables when we load class
 // android.server.BluetoothDeviceService
 static jfieldID field_mNativeData;
+static jfieldID field_mEventLoop;
 
 typedef struct {
     JNIEnv *env;
@@ -57,8 +58,10 @@
     const char *adapter;  // dbus object name of the local adapter
 } native_data_t;
 
-void onCreateBondingResult(DBusMessage *msg, void *user);
-void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user);
+extern event_loop_native_data_t *get_EventLoop_native_data(JNIEnv *,
+                                                           jobject);
+void onCreateBondingResult(DBusMessage *msg, void *user, void *nat);
+void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user, void *nat);
 
 /** Get native data stored in the opaque (Java code maintained) pointer mNativeData
  *  Perform quick sanity check, if there are any problems return NULL
@@ -78,6 +81,8 @@
     LOGV(__FUNCTION__);
 #ifdef HAVE_BLUETOOTH
     field_mNativeData = get_field(env, clazz, "mNativeData", "I");
+    field_mEventLoop = get_field(env, clazz, "mEventLoop",
+            "Landroid/server/BluetoothEventLoop;");
 #endif
 }
 
@@ -472,14 +477,19 @@
     LOGV(__FUNCTION__);
 #ifdef HAVE_BLUETOOTH
     native_data_t *nat = get_native_data(env, object);
-    if (nat) {
+    jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
+    struct event_loop_native_data_t *eventLoopNat =
+            get_EventLoop_native_data(env, eventLoop);
+
+    if (nat && eventLoopNat) {
         const char *c_address = env->GetStringUTFChars(address, NULL);
         LOGV("... address = %s", c_address);
         char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char));
         strlcpy(context_address, c_address, BTADDR_SIZE);  // for callback
         bool ret = dbus_func_args_async(env, nat->conn, (int)timeout_ms,
                                         onCreateBondingResult, // callback
-                                        context_address, // user data
+                                        context_address,
+                                        eventLoopNat,
                                         nat->adapter,
                                         DBUS_CLASS_NAME, "CreateBonding",
                                         DBUS_TYPE_STRING, &c_address,
@@ -856,7 +866,10 @@
 #ifdef HAVE_BLUETOOTH
     LOGV(__FUNCTION__);
     native_data_t *nat = get_native_data(env, object);
-    if (nat) {
+    jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
+    struct event_loop_native_data_t *eventLoopNat =
+            get_EventLoop_native_data(env, eventLoop);
+    if (nat && eventLoopNat) {
         const char *c_address = env->GetStringUTFChars(address, NULL);
         char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char));
         strlcpy(context_address, c_address, BTADDR_SIZE);
@@ -866,6 +879,7 @@
 
         bool ret = dbus_func_args_async(env, nat->conn, 20000,  // ms
                            onGetRemoteServiceChannelResult, context_address,
+                           eventLoopNat,
                            nat->adapter,
                            DBUS_CLASS_NAME, "GetRemoteServiceChannel",
                            DBUS_TYPE_STRING, &c_address,
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp
index c03bab6..7c5da5b 100644
--- a/core/jni/android_server_BluetoothEventLoop.cpp
+++ b/core/jni/android_server_BluetoothEventLoop.cpp
@@ -18,6 +18,7 @@
 
 #include "android_bluetooth_common.h"
 #include "android_runtime/AndroidRuntime.h"
+#include "cutils/sockets.h"
 #include "JNIHelp.h"
 #include "jni.h"
 #include "utils/Log.h"
@@ -65,14 +66,15 @@
 
 typedef event_loop_native_data_t native_data_t;
 
-// Only valid during waitForAndDispatchEventNative()
-native_data_t *event_loop_nat;
-
 static inline native_data_t * get_native_data(JNIEnv *env, jobject object) {
     return (native_data_t *)(env->GetIntField(object,
                                                  field_mNativeData));
 }
 
+native_data_t *get_EventLoop_native_data(JNIEnv *env, jobject object) {
+    return get_native_data(env, object);
+}
+
 #endif
 static void classInitNative(JNIEnv* env, jclass clazz) {
     LOGV(__FUNCTION__);
@@ -115,6 +117,10 @@
         LOGE("%s: out of memory!", __FUNCTION__);
         return;
     }
+    memset(nat, 0, sizeof(native_data_t));
+
+    pthread_mutex_init(&(nat->thread_mutex), NULL);
+
     env->SetIntField(object, field_mNativeData, (jint)nat);
 
     {
@@ -135,6 +141,9 @@
 #ifdef HAVE_BLUETOOTH
     native_data_t *nat =
             (native_data_t *)env->GetIntField(object, field_mNativeData);
+
+    pthread_mutex_destroy(&(nat->thread_mutex));
+
     if (nat) {
         free(nat);
     }
@@ -151,13 +160,11 @@
 static const DBusObjectPathVTable agent_vtable = {
     NULL, agent_event_filter, NULL, NULL, NULL, NULL
 };
-#endif
 
-static jboolean setUpEventLoopNative(JNIEnv *env, jobject object) {
-#ifdef HAVE_BLUETOOTH
+
+static jboolean setUpEventLoop(native_data_t *nat) {
     LOGV(__FUNCTION__);
     dbus_threads_init_default();
-    native_data_t *nat = get_native_data(env, object);
     DBusError err;
     dbus_error_init(&err);
 
@@ -207,7 +214,7 @@
         // Add an object handler for passkey agent method calls
         const char *path = "/android/bluetooth/Agent";
         if (!dbus_connection_register_object_path(nat->conn, path,
-                &agent_vtable, NULL)) {
+                &agent_vtable, nat)) {
             LOGE("%s: Can't register object path %s for agent!",
                  __FUNCTION__, path);
             return JNI_FALSE;
@@ -217,7 +224,7 @@
         // trying for 10 seconds.
         int attempt;
         for (attempt = 0; attempt < 1000; attempt++) {
-            DBusMessage *reply = dbus_func_args_error(env, nat->conn, &err,
+            DBusMessage *reply = dbus_func_args_error(NULL, nat->conn, &err,
                     BLUEZ_DBUS_BASE_PATH,
                     "org.bluez.Security", "RegisterDefaultPasskeyAgent",
                     DBUS_TYPE_STRING, &path,
@@ -245,7 +252,7 @@
         }
 
         // Now register the Auth agent
-        DBusMessage *reply = dbus_func_args_error(env, nat->conn, &err,
+        DBusMessage *reply = dbus_func_args_error(NULL, nat->conn, &err,
                 BLUEZ_DBUS_BASE_PATH,
                 "org.bluez.Security", "RegisterDefaultAuthorizationAgent",
                 DBUS_TYPE_STRING, &path,
@@ -259,14 +266,11 @@
         return JNI_TRUE;
     }
 
-#endif
     return JNI_FALSE;
 }
 
-static void tearDownEventLoopNative(JNIEnv *env, jobject object) {
+static void tearDownEventLoop(native_data_t *nat) {
     LOGV(__FUNCTION__);
-#ifdef HAVE_BLUETOOTH
-    native_data_t *nat = get_native_data(env, object);
     if (nat != NULL && nat->conn != NULL) {
 
         DBusError err;
@@ -274,14 +278,14 @@
 
         const char *path = "/android/bluetooth/Agent";
         DBusMessage *reply =
-            dbus_func_args(env, nat->conn, BLUEZ_DBUS_BASE_PATH,
+            dbus_func_args(NULL, nat->conn, BLUEZ_DBUS_BASE_PATH,
                     "org.bluez.Security", "UnregisterDefaultPasskeyAgent",
                     DBUS_TYPE_STRING, &path,
                     DBUS_TYPE_INVALID);
         if (reply) dbus_message_unref(reply);
 
         reply =
-            dbus_func_args(env, nat->conn, BLUEZ_DBUS_BASE_PATH,
+            dbus_func_args(NULL, nat->conn, BLUEZ_DBUS_BASE_PATH,
                     "org.bluez.Security", "UnregisterDefaultAuthorizationAgent",
                     DBUS_TYPE_STRING, &path,
                     DBUS_TYPE_INVALID);
@@ -322,7 +326,308 @@
 
         dbus_connection_remove_filter(nat->conn, event_filter, nat);
     }
-#endif
+}
+
+
+#define EVENT_LOOP_EXIT 1
+#define EVENT_LOOP_ADD  2
+#define EVENT_LOOP_REMOVE 3
+
+dbus_bool_t dbusAddWatch(DBusWatch *watch, void *data) {
+    native_data_t *nat = (native_data_t *)data;
+
+    if (dbus_watch_get_enabled(watch)) {
+        // note that we can't just send the watch and inspect it later
+        // because we may get a removeWatch call before this data is reacted
+        // to by our eventloop and remove this watch..  reading the add first
+        // and then inspecting the recently deceased watch would be bad.
+        char control = EVENT_LOOP_ADD;
+        write(nat->controlFdW, &control, sizeof(char));
+
+        int fd = dbus_watch_get_fd(watch);
+        write(nat->controlFdW, &fd, sizeof(int));
+
+        unsigned int flags = dbus_watch_get_flags(watch);
+        write(nat->controlFdW, &flags, sizeof(unsigned int));
+
+        write(nat->controlFdW, &watch, sizeof(DBusWatch*));
+    }
+    return true;
+}
+
+void dbusRemoveWatch(DBusWatch *watch, void *data) {
+    native_data_t *nat = (native_data_t *)data;
+
+    char control = EVENT_LOOP_REMOVE;
+    write(nat->controlFdW, &control, sizeof(char));
+
+    int fd = dbus_watch_get_fd(watch);
+    write(nat->controlFdW, &fd, sizeof(int));
+
+    unsigned int flags = dbus_watch_get_flags(watch);
+    write(nat->controlFdW, &flags, sizeof(unsigned int));
+}
+
+void dbusToggleWatch(DBusWatch *watch, void *data) {
+    if (dbus_watch_get_enabled(watch)) {
+        dbusAddWatch(watch, data);
+    } else {
+        dbusRemoveWatch(watch, data);
+    }
+}
+
+static void handleWatchAdd(native_data_t *nat) {
+    DBusWatch *watch;
+    int newFD;
+    unsigned int flags;
+
+    read(nat->controlFdR, &newFD, sizeof(int));
+    read(nat->controlFdR, &flags, sizeof(unsigned int));
+    read(nat->controlFdR, &watch, sizeof(DBusWatch *));
+    int events = (flags & DBUS_WATCH_READABLE ? POLLIN : 0)
+            | (flags & DBUS_WATCH_WRITABLE ? POLLOUT : 0);
+
+    for (int y = 0; y<nat->pollMemberCount; y++) {
+        if ((nat->pollData[y].fd == newFD) &&
+                (nat->pollData[y].events == events)) {
+            LOGV("DBusWatch duplicate add");
+            return;
+        }
+    }
+    if (nat->pollMemberCount == nat->pollDataSize) {
+        LOGV("Bluetooth EventLoop poll struct growing");
+        struct pollfd *temp = (struct pollfd *)malloc(
+                sizeof(struct pollfd) * (nat->pollMemberCount+1));
+        if (!temp) {
+            return;
+        }
+        memcpy(temp, nat->pollData, sizeof(struct pollfd) *
+                nat->pollMemberCount);
+        free(nat->pollData);
+        nat->pollData = temp;
+        DBusWatch **temp2 = (DBusWatch **)malloc(sizeof(DBusWatch *) *
+                (nat->pollMemberCount+1));
+        if (!temp2) {
+            return;
+        }
+        memcpy(temp2, nat->watchData, sizeof(DBusWatch *) *
+                nat->pollMemberCount);
+        free(nat->watchData);
+        nat->watchData = temp2;
+        nat->pollDataSize++;
+    }
+    nat->pollData[nat->pollMemberCount].fd = newFD;
+    nat->pollData[nat->pollMemberCount].revents = 0;
+    nat->pollData[nat->pollMemberCount].events = events;
+    nat->watchData[nat->pollMemberCount] = watch;
+    nat->pollMemberCount++;
+}
+
+static void handleWatchRemove(native_data_t *nat) {
+    int removeFD;
+    unsigned int flags;
+
+    read(nat->controlFdR, &removeFD, sizeof(int));
+    read(nat->controlFdR, &flags, sizeof(unsigned int));
+    int events = (flags & DBUS_WATCH_READABLE ? POLLIN : 0)
+            | (flags & DBUS_WATCH_WRITABLE ? POLLOUT : 0);
+
+    for (int y = 0; y < nat->pollMemberCount; y++) {
+        if ((nat->pollData[y].fd == removeFD) &&
+                (nat->pollData[y].events == events)) {
+            int newCount = --nat->pollMemberCount;
+            // copy the last live member over this one
+            nat->pollData[y].fd = nat->pollData[newCount].fd;
+            nat->pollData[y].events = nat->pollData[newCount].events;
+            nat->pollData[y].revents = nat->pollData[newCount].revents;
+            nat->watchData[y] = nat->watchData[newCount];
+            return;
+        }
+    }
+    LOGW("WatchRemove given with unknown watch");
+}
+
+static void *eventLoopMain(void *ptr) {
+    native_data_t *nat = (native_data_t *)ptr;
+    JNIEnv *env;
+
+    JavaVMAttachArgs args;
+    char name[] = "BT EventLoop";
+    args.version = nat->envVer;
+    args.name = name;
+    args.group = NULL;
+
+    nat->vm->AttachCurrentThread(&env, &args);
+
+    dbus_connection_set_watch_functions(nat->conn, dbusAddWatch,
+            dbusRemoveWatch, dbusToggleWatch, ptr, NULL);
+
+    while (1) {
+        for (int i = 0; i < nat->pollMemberCount; i++) {
+            if (!nat->pollData[i].revents) {
+                continue;
+            }
+            if (nat->pollData[i].fd == nat->controlFdR) {
+                char data;
+                while (recv(nat->controlFdR, &data, sizeof(char), MSG_DONTWAIT)
+                        != -1) {
+                    switch (data) {
+                    case EVENT_LOOP_EXIT:
+                    {
+                        dbus_connection_set_watch_functions(nat->conn,
+                                NULL, NULL, NULL, NULL, NULL);
+                        tearDownEventLoop(nat);
+                        nat->vm->DetachCurrentThread();
+                        shutdown(nat->controlFdR,SHUT_RDWR);
+                        return NULL;
+                    }
+                    case EVENT_LOOP_ADD:
+                    {
+                        handleWatchAdd(nat);
+                        break;
+                    }
+                    case EVENT_LOOP_REMOVE:
+                    {
+                        handleWatchRemove(nat);
+                        break;
+                    }
+                    }
+                }
+            } else {
+                  int event = nat->pollData[i].revents;
+                  int flags = (event & POLLIN ? DBUS_WATCH_READABLE : 0) |
+                              (event & POLLOUT ? DBUS_WATCH_WRITABLE : 0);
+                  dbus_watch_handle(nat->watchData[i], event);
+                  nat->pollData[i].revents = 0;
+                  // can only do one - it may have caused a 'remove'
+                  break;
+            }
+        }
+        while (dbus_connection_dispatch(nat->conn) == 
+                DBUS_DISPATCH_DATA_REMAINS) {
+        }
+
+        poll(nat->pollData, nat->pollMemberCount, -1);
+    }
+}
+#endif // HAVE_BLUETOOTH
+
+static jboolean startEventLoopNative(JNIEnv *env, jobject object) {
+    jboolean result = JNI_FALSE;
+#ifdef HAVE_BLUETOOTH
+    event_loop_native_data_t *nat = get_native_data(env, object);
+
+    pthread_mutex_lock(&(nat->thread_mutex));
+
+    if (nat->pollData) {
+        LOGW("trying to start EventLoop a second time!");
+        pthread_mutex_unlock( &(nat->thread_mutex) );
+        return JNI_FALSE;
+    }
+
+    nat->pollData = (struct pollfd *)malloc(sizeof(struct pollfd) *
+            DEFAULT_INITIAL_POLLFD_COUNT);
+    if (!nat->pollData) {
+        LOGE("out of memory error starting EventLoop!");
+        goto done;
+    }
+
+    nat->watchData = (DBusWatch **)malloc(sizeof(DBusWatch *) *
+            DEFAULT_INITIAL_POLLFD_COUNT);
+    if (!nat->watchData) {
+        LOGE("out of memory error starting EventLoop!");
+        goto done;
+    }
+
+    memset(nat->pollData, 0, sizeof(struct pollfd) *
+            DEFAULT_INITIAL_POLLFD_COUNT);
+    memset(nat->watchData, 0, sizeof(DBusWatch *) *
+            DEFAULT_INITIAL_POLLFD_COUNT);
+    nat->pollDataSize = DEFAULT_INITIAL_POLLFD_COUNT;
+    nat->pollMemberCount = 1;
+
+    if (socketpair(AF_LOCAL, SOCK_STREAM, 0, &(nat->controlFdR))) {
+        LOGE("Error getting BT control socket");
+        goto done;
+    }
+    nat->pollData[0].fd = nat->controlFdR;
+    nat->pollData[0].events = POLLIN;
+
+    env->GetJavaVM( &(nat->vm) );
+    nat->envVer = env->GetVersion();
+
+    nat->me = env->NewGlobalRef(object);
+
+    if (setUpEventLoop(nat) != JNI_TRUE) {
+        LOGE("failure setting up Event Loop!");
+        goto done;
+    }
+
+    pthread_create(&(nat->thread), NULL, eventLoopMain, nat);
+    result = JNI_TRUE;
+
+done:
+    if (JNI_FALSE == result) {
+        if (nat->controlFdW || nat->controlFdR) {
+            shutdown(nat->controlFdW, SHUT_RDWR);
+            nat->controlFdW = 0;
+            nat->controlFdR = 0;
+        }
+        if (nat->me) env->DeleteGlobalRef(nat->me);
+        nat->me = NULL;
+        if (nat->pollData) free(nat->pollData);
+        nat->pollData = NULL;
+        if (nat->watchData) free(nat->watchData);
+        nat->watchData = NULL;
+        nat->pollDataSize = 0;
+        nat->pollMemberCount = 0;
+    }
+
+    pthread_mutex_unlock(&(nat->thread_mutex));
+#endif // HAVE_BLUETOOTH
+    return result;
+}
+
+static void stopEventLoopNative(JNIEnv *env, jobject object) {
+#ifdef HAVE_BLUETOOTH
+    native_data_t *nat = get_native_data(env, object);
+
+    pthread_mutex_lock(&(nat->thread_mutex));
+    if (nat->pollData) {
+        char data = EVENT_LOOP_EXIT;
+        ssize_t t = write(nat->controlFdW, &data, sizeof(char));
+        void *ret;
+        pthread_join(nat->thread, &ret);
+
+        env->DeleteGlobalRef(nat->me);
+        nat->me = NULL;
+        free(nat->pollData);
+        nat->pollData = NULL;
+        free(nat->watchData);
+        nat->watchData = NULL;
+        nat->pollDataSize = 0;
+        nat->pollMemberCount = 0;
+        shutdown(nat->controlFdW, SHUT_RDWR);
+        nat->controlFdW = 0;
+        nat->controlFdR = 0;
+    }
+    pthread_mutex_unlock(&(nat->thread_mutex));
+#endif // HAVE_BLUETOOTH
+}
+
+static jboolean isEventLoopRunningNative(JNIEnv *env, jobject object) {
+    jboolean result = JNI_FALSE;
+#ifdef HAVE_BLUETOOTH
+    native_data_t *nat = get_native_data(env, object);
+
+    pthread_mutex_lock(&(nat->thread_mutex));
+    if (nat->pollData) {
+        result = JNI_TRUE;
+    }
+    pthread_mutex_unlock(&(nat->thread_mutex));
+
+#endif // HAVE_BLUETOOTH
+    return result;
 }
 
 #ifdef HAVE_BLUETOOTH
@@ -338,7 +643,7 @@
     dbus_error_init(&err);
 
     nat = (native_data_t *)data;
-    env = nat->env;
+    nat->vm->GetEnv((void**)&env, nat->envVer);
     if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL) {
         LOGV("%s: not interested (not a signal).", __FUNCTION__);
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@@ -556,11 +861,9 @@
 // Called by dbus during WaitForAndDispatchEventNative()
 static DBusHandlerResult agent_event_filter(DBusConnection *conn,
                                             DBusMessage *msg, void *data) {
-    native_data_t *nat = event_loop_nat;
+    native_data_t *nat = (native_data_t *)data;
     JNIEnv *env;
-
-
-    env = nat->env;
+    nat->vm->GetEnv((void**)&env, nat->envVer);
     if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_METHOD_CALL) {
         LOGV("%s: not interested (not a method call).", __FUNCTION__);
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@@ -730,27 +1033,6 @@
 }
 #endif
 
-static jboolean waitForAndDispatchEventNative(JNIEnv *env, jobject object,
-                                               jint timeout_ms) {
-#ifdef HAVE_BLUETOOTH
-    //LOGV("%s: %8d (pid %d tid %d)",__FUNCTION__, time(NULL), getpid(), gettid()); // too chatty
-    native_data_t *nat = get_native_data(env, object);
-    if (nat != NULL && nat->conn != NULL) {
-        jboolean ret;
-        nat->me = object;
-        nat->env = env;
-        event_loop_nat = nat;
-        ret = dbus_connection_read_write_dispatch_greedy(nat->conn,
-                                                         timeout_ms) == TRUE ?
-              JNI_TRUE : JNI_FALSE;
-        event_loop_nat = NULL;
-        nat->me = NULL;
-        nat->env = NULL;
-        return ret;
-    }
-#endif
-    return JNI_FALSE;
-}
 
 #ifdef HAVE_BLUETOOTH
 //TODO: Unify result codes in a header
@@ -761,13 +1043,16 @@
 #define BOND_RESULT_AUTH_CANCELED 3
 #define BOND_RESULT_REMOTE_DEVICE_DOWN 4
 #define BOND_RESULT_DISCOVERY_IN_PROGRESS 5
-void onCreateBondingResult(DBusMessage *msg, void *user) {
+
+void onCreateBondingResult(DBusMessage *msg, void *user, void *n) {
     LOGV(__FUNCTION__);
 
+    native_data_t *nat = (native_data_t *)n;
     const char *address = (const char *)user;
     DBusError err;
     dbus_error_init(&err);
-    JNIEnv *env = event_loop_nat->env;
+    JNIEnv *env;
+    nat->vm->GetEnv((void**)&env, nat->envVer);
 
     LOGV("... address = %s", address);
 
@@ -809,7 +1094,7 @@
         }
     }
 
-    env->CallVoidMethod(event_loop_nat->me,
+    env->CallVoidMethod(nat->me,
                         method_onCreateBondingResult,
                         env->NewStringUTF(address),
                         result);
@@ -818,13 +1103,17 @@
     free(user);
 }
 
-void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user) {
+void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user, void *n) {
     LOGV(__FUNCTION__);
 
     const char *address = (const char *) user;
+    native_data_t *nat = (native_data_t *) n;
+
     DBusError err;
     dbus_error_init(&err);
-    JNIEnv *env = event_loop_nat->env;
+    JNIEnv *env;
+    nat->vm->GetEnv((void**)&env, nat->envVer);
+
     jint channel = -2;
 
     LOGV("... address = %s", address);
@@ -839,7 +1128,7 @@
     }
 
 done:
-    env->CallVoidMethod(event_loop_nat->me,
+    env->CallVoidMethod(nat->me,
                         method_onGetRemoteServiceChannelResult,
                         env->NewStringUTF(address),
                         channel);
@@ -852,9 +1141,9 @@
     {"classInitNative", "()V", (void *)classInitNative},
     {"initializeNativeDataNative", "()V", (void *)initializeNativeDataNative},
     {"cleanupNativeDataNative", "()V", (void *)cleanupNativeDataNative},
-    {"setUpEventLoopNative", "()Z", (void *)setUpEventLoopNative},
-    {"tearDownEventLoopNative", "()V", (void *)tearDownEventLoopNative},
-    {"waitForAndDispatchEventNative", "(I)Z", (void *)waitForAndDispatchEventNative}
+    {"startEventLoopNative", "()V", (void *)startEventLoopNative},
+    {"stopEventLoopNative", "()V", (void *)stopEventLoopNative},
+    {"isEventLoopRunningNative", "()Z", (void *)isEventLoopRunningNative}
 };
 
 int register_android_server_BluetoothEventLoop(JNIEnv *env) {