/* ** Copyright 2008, 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 "BluetoothEventLoop.cpp" #include "android_bluetooth_common.h" #include "android_runtime/AndroidRuntime.h" #include "JNIHelp.h" #include "jni.h" #include "utils/Log.h" #include "utils/misc.h" #include #include #include #include #include #ifdef HAVE_BLUETOOTH #include #endif namespace android { #ifdef HAVE_BLUETOOTH static jfieldID field_mNativeData; static jmethodID method_onModeChanged; static jmethodID method_onNameChanged; static jmethodID method_onDiscoveryStarted; static jmethodID method_onDiscoveryCompleted; static jmethodID method_onRemoteDeviceFound; static jmethodID method_onRemoteDeviceDisappeared; static jmethodID method_onRemoteClassUpdated; static jmethodID method_onRemoteNameUpdated; static jmethodID method_onRemoteNameFailed; static jmethodID method_onRemoteDeviceConnected; static jmethodID method_onRemoteDeviceDisconnectRequested; static jmethodID method_onRemoteDeviceDisconnected; static jmethodID method_onBondingCreated; static jmethodID method_onBondingRemoved; static jmethodID method_onCreateBondingResult; static jmethodID method_onGetRemoteServiceChannelResult; static jmethodID method_onPasskeyAgentRequest; static jmethodID method_onPasskeyAgentCancel; static jmethodID method_onAuthAgentAuthorize; static jmethodID method_onAuthAgentCancel; 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)); } #endif static void classInitNative(JNIEnv* env, jclass clazz) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH method_onModeChanged = env->GetMethodID(clazz, "onModeChanged", "(Ljava/lang/String;)V"); method_onNameChanged = env->GetMethodID(clazz, "onNameChanged", "(Ljava/lang/String;)V"); method_onDiscoveryStarted = env->GetMethodID(clazz, "onDiscoveryStarted", "()V"); method_onDiscoveryCompleted = env->GetMethodID(clazz, "onDiscoveryCompleted", "()V"); method_onRemoteDeviceFound = env->GetMethodID(clazz, "onRemoteDeviceFound", "(Ljava/lang/String;IS)V"); method_onRemoteDeviceDisappeared = env->GetMethodID(clazz, "onRemoteDeviceDisappeared", "(Ljava/lang/String;)V"); method_onRemoteClassUpdated = env->GetMethodID(clazz, "onRemoteClassUpdated", "(Ljava/lang/String;I)V"); method_onRemoteNameUpdated = env->GetMethodID(clazz, "onRemoteNameUpdated", "(Ljava/lang/String;Ljava/lang/String;)V"); method_onRemoteNameFailed = env->GetMethodID(clazz, "onRemoteNameFailed", "(Ljava/lang/String;)V"); method_onRemoteDeviceConnected = env->GetMethodID(clazz, "onRemoteDeviceConnected", "(Ljava/lang/String;)V"); method_onRemoteDeviceDisconnectRequested = env->GetMethodID(clazz, "onRemoteDeviceDisconnectRequested", "(Ljava/lang/String;)V"); method_onRemoteDeviceDisconnected = env->GetMethodID(clazz, "onRemoteDeviceDisconnected", "(Ljava/lang/String;)V"); method_onBondingCreated = env->GetMethodID(clazz, "onBondingCreated", "(Ljava/lang/String;)V"); method_onBondingRemoved = env->GetMethodID(clazz, "onBondingRemoved", "(Ljava/lang/String;)V"); method_onCreateBondingResult = env->GetMethodID(clazz, "onCreateBondingResult", "(Ljava/lang/String;I)V"); method_onPasskeyAgentRequest = env->GetMethodID(clazz, "onPasskeyAgentRequest", "(Ljava/lang/String;I)V"); method_onPasskeyAgentCancel = env->GetMethodID(clazz, "onPasskeyAgentCancel", "(Ljava/lang/String;)V"); method_onAuthAgentAuthorize = env->GetMethodID(clazz, "onAuthAgentAuthorize", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z"); method_onAuthAgentCancel = env->GetMethodID(clazz, "onAuthAgentCancel", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V"); method_onGetRemoteServiceChannelResult = env->GetMethodID(clazz, "onGetRemoteServiceChannelResult", "(Ljava/lang/String;I)V"); field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I"); #endif } static void initializeNativeDataNative(JNIEnv* env, jobject object) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH native_data_t *nat = (native_data_t *)calloc(1, sizeof(native_data_t)); if (NULL == nat) { LOGE("%s: out of memory!", __FUNCTION__); return; } env->SetIntField(object, field_mNativeData, (jint)nat); { DBusError err; dbus_error_init(&err); dbus_threads_init_default(); nat->conn = dbus_bus_get(DBUS_BUS_SYSTEM, &err); if (dbus_error_is_set(&err)) { LOGE("%s: Could not get onto the system bus!", __FUNCTION__); dbus_error_free(&err); } } #endif } static void cleanupNativeDataNative(JNIEnv* env, jobject object) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH native_data_t *nat = (native_data_t *)env->GetIntField(object, field_mNativeData); if (nat) { free(nat); } #endif } #ifdef HAVE_BLUETOOTH static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, void *data); static DBusHandlerResult agent_event_filter(DBusConnection *conn, DBusMessage *msg, void *data); static const DBusObjectPathVTable agent_vtable = { NULL, agent_event_filter, NULL, NULL, NULL, NULL }; #endif static jboolean setUpEventLoopNative(JNIEnv *env, jobject object) { #ifdef HAVE_BLUETOOTH LOGV(__FUNCTION__); dbus_threads_init_default(); native_data_t *nat = get_native_data(env, object); DBusError err; dbus_error_init(&err); if (nat != NULL && nat->conn != NULL) { // Add a filter for all incoming messages if (!dbus_connection_add_filter(nat->conn, event_filter, nat, NULL)){ return JNI_FALSE; } // Set which messages will be processed by this dbus connection dbus_bus_add_match(nat->conn, "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Adapter'", &err); if (dbus_error_is_set(&err)) { LOG_AND_FREE_DBUS_ERROR(&err); return JNI_FALSE; } dbus_bus_add_match(nat->conn, "type='signal',interface='org.bluez.audio.Manager'", &err); if (dbus_error_is_set(&err)) { LOG_AND_FREE_DBUS_ERROR(&err); return JNI_FALSE; } dbus_bus_add_match(nat->conn, "type='signal',interface='org.bluez.audio.Device'", &err); if (dbus_error_is_set(&err)) { LOG_AND_FREE_DBUS_ERROR(&err); return JNI_FALSE; } dbus_bus_add_match(nat->conn, "type='signal',interface='org.bluez.audio.Sink'", &err); if (dbus_error_is_set(&err)) { LOG_AND_FREE_DBUS_ERROR(&err); return JNI_FALSE; } // 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)) { LOGE("%s: Can't register object path %s for agent!", __FUNCTION__, path); return JNI_FALSE; } // RegisterDefaultPasskeyAgent() will fail until hcid is up, so keep // trying for 10 seconds. int attempt; for (attempt = 0; attempt < 1000; attempt++) { DBusMessage *reply = dbus_func_args_error(env, nat->conn, &err, BLUEZ_DBUS_BASE_PATH, "org.bluez.Security", "RegisterDefaultPasskeyAgent", DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); if (reply) { // Success dbus_message_unref(reply); LOGV("Registered agent on attempt %d of 1000\n", attempt); break; } else if (dbus_error_has_name(&err, "org.freedesktop.DBus.Error.ServiceUnknown")) { // hcid is still down, retry dbus_error_free(&err); usleep(10000); // 10 ms } else { // Some other error we weren't expecting LOG_AND_FREE_DBUS_ERROR(&err); return JNI_FALSE; } } if (attempt == 1000) { LOGE("Time-out trying to call RegisterDefaultPasskeyAgent(), " "is hcid running?"); return JNI_FALSE; } // Now register the Auth agent DBusMessage *reply = dbus_func_args_error(env, nat->conn, &err, BLUEZ_DBUS_BASE_PATH, "org.bluez.Security", "RegisterDefaultAuthorizationAgent", DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); if (!reply) { LOG_AND_FREE_DBUS_ERROR(&err); return JNI_FALSE; } dbus_message_unref(reply); return JNI_TRUE; } #endif return JNI_FALSE; } static void tearDownEventLoopNative(JNIEnv *env, jobject object) { LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH native_data_t *nat = get_native_data(env, object); if (nat != NULL && nat->conn != NULL) { DBusError err; dbus_error_init(&err); const char *path = "/android/bluetooth/Agent"; DBusMessage *reply = dbus_func_args(env, 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, "org.bluez.Security", "UnregisterDefaultAuthorizationAgent", DBUS_TYPE_STRING, &path, DBUS_TYPE_INVALID); if (reply) dbus_message_unref(reply); dbus_connection_unregister_object_path(nat->conn, path); dbus_bus_remove_match(nat->conn, "type='signal',interface='org.bluez.audio.Sink'", &err); if (dbus_error_is_set(&err)) { LOG_AND_FREE_DBUS_ERROR(&err); } dbus_bus_remove_match(nat->conn, "type='signal',interface='org.bluez.audio.Device'", &err); if (dbus_error_is_set(&err)) { LOG_AND_FREE_DBUS_ERROR(&err); } dbus_bus_remove_match(nat->conn, "type='signal',interface='org.bluez.audio.Manager'", &err); if (dbus_error_is_set(&err)) { LOG_AND_FREE_DBUS_ERROR(&err); } dbus_bus_remove_match(nat->conn, "type='signal',interface='"BLUEZ_DBUS_BASE_IFC".Adapter'", &err); if (dbus_error_is_set(&err)) { LOG_AND_FREE_DBUS_ERROR(&err); } dbus_connection_remove_filter(nat->conn, event_filter, nat); } #endif } #ifdef HAVE_BLUETOOTH extern DBusHandlerResult a2dp_event_filter(DBusMessage *msg, JNIEnv *env); // Called by dbus during WaitForAndDispatchEventNative() static DBusHandlerResult event_filter(DBusConnection *conn, DBusMessage *msg, void *data) { native_data_t *nat; JNIEnv *env; DBusError err; dbus_error_init(&err); nat = (native_data_t *)data; env = nat->env; 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; } LOGV("%s: Received signal %s:%s from %s", __FUNCTION__, dbus_message_get_interface(msg), dbus_message_get_member(msg), dbus_message_get_path(msg)); if (dbus_message_is_signal(msg, "org.bluez.Adapter", "RemoteDeviceFound")) { char *c_address; int n_class; short n_rssi; if (dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &c_address, DBUS_TYPE_UINT32, &n_class, DBUS_TYPE_INT16, &n_rssi, DBUS_TYPE_INVALID)) { LOGV("... address = %s class = %#X rssi = %hd", c_address, n_class, n_rssi); env->CallVoidMethod(nat->me, method_onRemoteDeviceFound, env->NewStringUTF(c_address), (jint)n_class, (jshort)n_rssi); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "DiscoveryStarted")) { LOGI("DiscoveryStarted signal received"); env->CallVoidMethod(nat->me, method_onDiscoveryStarted); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "DiscoveryCompleted")) { LOGI("DiscoveryCompleted signal received"); env->CallVoidMethod(nat->me, method_onDiscoveryCompleted); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "RemoteDeviceDisappeared")) { char *c_address; if (dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &c_address, DBUS_TYPE_INVALID)) { LOGV("... address = %s", c_address); env->CallVoidMethod(nat->me, method_onRemoteDeviceDisappeared, env->NewStringUTF(c_address)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "RemoteClassUpdated")) { char *c_address; int n_class; if (dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &c_address, DBUS_TYPE_UINT32, &n_class, DBUS_TYPE_INVALID)) { LOGV("... address = %s", c_address); env->CallVoidMethod(nat->me, method_onRemoteClassUpdated, env->NewStringUTF(c_address), (jint)n_class); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "RemoteNameUpdated")) { char *c_address; char *c_name; if (dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &c_address, DBUS_TYPE_STRING, &c_name, DBUS_TYPE_INVALID)) { LOGV("... address = %s, name = %s", c_address, c_name); env->CallVoidMethod(nat->me, method_onRemoteNameUpdated, env->NewStringUTF(c_address), env->NewStringUTF(c_name)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "RemoteNameFailed")) { char *c_address; if (dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &c_address, DBUS_TYPE_INVALID)) { LOGV("... address = %s", c_address); env->CallVoidMethod(nat->me, method_onRemoteNameFailed, env->NewStringUTF(c_address)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "RemoteDeviceConnected")) { char *c_address; if (dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &c_address, DBUS_TYPE_INVALID)) { LOGV("... address = %s", c_address); env->CallVoidMethod(nat->me, method_onRemoteDeviceConnected, env->NewStringUTF(c_address)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "RemoteDeviceDisconnectRequested")) { char *c_address; if (dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &c_address, DBUS_TYPE_INVALID)) { LOGV("... address = %s", c_address); env->CallVoidMethod(nat->me, method_onRemoteDeviceDisconnectRequested, env->NewStringUTF(c_address)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "RemoteDeviceDisconnected")) { char *c_address; if (dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &c_address, DBUS_TYPE_INVALID)) { LOGV("... address = %s", c_address); env->CallVoidMethod(nat->me, method_onRemoteDeviceDisconnected, env->NewStringUTF(c_address)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "BondingCreated")) { char *c_address; if (dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &c_address, DBUS_TYPE_INVALID)) { LOGV("... address = %s", c_address); env->CallVoidMethod(nat->me, method_onBondingCreated, env->NewStringUTF(c_address)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "BondingRemoved")) { char *c_address; if (dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &c_address, DBUS_TYPE_INVALID)) { LOGV("... address = %s", c_address); env->CallVoidMethod(nat->me, method_onBondingRemoved, env->NewStringUTF(c_address)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "ModeChanged")) { char *c_mode; if (dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &c_mode, DBUS_TYPE_INVALID)) { LOGV("... mode = %s", c_mode); env->CallVoidMethod(nat->me, method_onModeChanged, env->NewStringUTF(c_mode)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_signal(msg, "org.bluez.Adapter", "NameChanged")) { char *c_name; if (dbus_message_get_args(msg, &err, DBUS_TYPE_STRING, &c_name, DBUS_TYPE_INVALID)) { LOGV("... name = %s", c_name); env->CallVoidMethod(nat->me, method_onNameChanged, env->NewStringUTF(c_name)); } else LOG_AND_FREE_DBUS_ERROR_WITH_MSG(&err, msg); return DBUS_HANDLER_RESULT_HANDLED; } return a2dp_event_filter(msg, env); } // Called by dbus during WaitForAndDispatchEventNative() static DBusHandlerResult agent_event_filter(DBusConnection *conn, DBusMessage *msg, void *data) { native_data_t *nat = event_loop_nat; JNIEnv *env; env = nat->env; 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; } LOGV("%s: Received method %s:%s", __FUNCTION__, dbus_message_get_interface(msg), dbus_message_get_member(msg)); if (dbus_message_is_method_call(msg, "org.bluez.PasskeyAgent", "Request")) { const char *adapter; const char *address; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &adapter, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID)) { LOGE("%s: Invalid arguments for Request() method", __FUNCTION__); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } LOGV("... address = %s", address); dbus_message_ref(msg); // increment refcount because we pass to java env->CallVoidMethod(nat->me, method_onPasskeyAgentRequest, env->NewStringUTF(address), (int)msg); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_method_call(msg, "org.bluez.PasskeyAgent", "Cancel")) { const char *adapter; const char *address; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &adapter, DBUS_TYPE_STRING, &address, DBUS_TYPE_INVALID)) { LOGE("%s: Invalid arguments for Cancel() method", __FUNCTION__); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } LOGV("... address = %s", address); env->CallVoidMethod(nat->me, method_onPasskeyAgentCancel, env->NewStringUTF(address)); // reply DBusMessage *reply = dbus_message_new_method_return(msg); if (!reply) { LOGE("%s: Cannot create message reply\n", __FUNCTION__); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } dbus_connection_send(nat->conn, reply, NULL); dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_method_call(msg, "org.bluez.PasskeyAgent", "Release")) { LOGW("We are no longer the passkey agent!"); // reply DBusMessage *reply = dbus_message_new_method_return(msg); if (!reply) { LOGE("%s: Cannot create message reply\n", __FUNCTION__); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } dbus_connection_send(nat->conn, reply, NULL); dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_method_call(msg, "org.bluez.AuthorizationAgent", "Authorize")) { const char *adapter; const char *address; const char *service; const char *uuid; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &adapter, DBUS_TYPE_STRING, &address, DBUS_TYPE_STRING, &service, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID)) { LOGE("%s: Invalid arguments for Authorize() method", __FUNCTION__); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } LOGV("... address = %s", address); LOGV("... service = %s", service); LOGV("... uuid = %s", uuid); bool auth_granted = env->CallBooleanMethod(nat->me, method_onAuthAgentAuthorize, env->NewStringUTF(address), env->NewStringUTF(service), env->NewStringUTF(uuid)); // reply if (auth_granted) { DBusMessage *reply = dbus_message_new_method_return(msg); if (!reply) { LOGE("%s: Cannot create message reply\n", __FUNCTION__); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } dbus_connection_send(nat->conn, reply, NULL); dbus_message_unref(reply); } else { DBusMessage *reply = dbus_message_new_error(msg, "org.bluez.Error.Rejected", "Authorization rejected"); if (!reply) { LOGE("%s: Cannot create message reply\n", __FUNCTION__); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } dbus_connection_send(nat->conn, reply, NULL); dbus_message_unref(reply); } return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_method_call(msg, "org.bluez.AuthorizationAgent", "Cancel")) { const char *adapter; const char *address; const char *service; const char *uuid; if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &adapter, DBUS_TYPE_STRING, &address, DBUS_TYPE_STRING, &service, DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID)) { LOGE("%s: Invalid arguments for Cancel() method", __FUNCTION__); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } LOGV("... address = %s", address); LOGV("... service = %s", service); LOGV("... uuid = %s", uuid); env->CallVoidMethod(nat->me, method_onAuthAgentCancel, env->NewStringUTF(address), env->NewStringUTF(service), env->NewStringUTF(uuid)); // reply DBusMessage *reply = dbus_message_new_method_return(msg); if (!reply) { LOGE("%s: Cannot create message reply\n", __FUNCTION__); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } dbus_connection_send(nat->conn, reply, NULL); dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } else if (dbus_message_is_method_call(msg, "org.bluez.AuthorizationAgent", "Release")) { LOGW("We are no longer the auth agent!"); // reply DBusMessage *reply = dbus_message_new_method_return(msg); if (!reply) { LOGE("%s: Cannot create message reply\n", __FUNCTION__); return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } dbus_connection_send(nat->conn, reply, NULL); dbus_message_unref(reply); return DBUS_HANDLER_RESULT_HANDLED; } else { LOGV("... ignored"); } return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } #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 #define BOND_RESULT_ERROR -1000 #define BOND_RESULT_SUCCESS 0 #define BOND_RESULT_AUTH_FAILED 1 #define BOND_RESULT_AUTH_REJECTED 2 #define BOND_RESULT_AUTH_CANCELED 3 #define BOND_RESULT_REMOTE_DEVICE_DOWN 4 void onCreateBondingResult(DBusMessage *msg, void *user) { LOGV(__FUNCTION__); const char *address = (const char *)user; DBusError err; dbus_error_init(&err); JNIEnv *env = event_loop_nat->env; LOGV("... address = %s", address); jint result = BOND_RESULT_SUCCESS; if (dbus_set_error_from_message(&err, msg)) { if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) { // Pins did not match, or remote device did not respond to pin // request in time LOGV("... error = %s (%s)\n", err.name, err.message); result = BOND_RESULT_AUTH_FAILED; } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationRejected")) { // We rejected pairing, or the remote side rejected pairing. This // happens if either side presses 'cancel' at the pairing dialog. LOGV("... error = %s (%s)\n", err.name, err.message); result = BOND_RESULT_AUTH_REJECTED; } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationCanceled")) { // Not sure if this happens LOGV("... error = %s (%s)\n", err.name, err.message); result = BOND_RESULT_AUTH_CANCELED; } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.ConnectionAttemptFailed")) { // Other device is not responding at all LOGV("... error = %s (%s)\n", err.name, err.message); result = BOND_RESULT_REMOTE_DEVICE_DOWN; } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AlreadyExists")) { // already bonded LOGV("... error = %s (%s)\n", err.name, err.message); result = BOND_RESULT_SUCCESS; } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress")) { // don't make the java callback LOGV("... error = %s (%s)\n", err.name, err.message); goto done; } else { LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); result = BOND_RESULT_ERROR; } } env->CallVoidMethod(event_loop_nat->me, method_onCreateBondingResult, env->NewStringUTF(address), result); done: dbus_error_free(&err); free(user); } void onGetRemoteServiceChannelResult(DBusMessage *msg, void *user) { LOGV(__FUNCTION__); const char *address = (const char *) user; DBusError err; dbus_error_init(&err); JNIEnv *env = event_loop_nat->env; jint channel = -2; LOGV("... address = %s", address); if (dbus_set_error_from_message(&err, msg) || !dbus_message_get_args(msg, &err, DBUS_TYPE_INT32, &channel, DBUS_TYPE_INVALID)) { /* if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.AuthenticationFailed")) */ LOGE("%s: D-Bus error: %s (%s)\n", __FUNCTION__, err.name, err.message); dbus_error_free(&err); } done: env->CallVoidMethod(event_loop_nat->me, method_onGetRemoteServiceChannelResult, env->NewStringUTF(address), channel); free(user); } #endif static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"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} }; int register_android_server_BluetoothEventLoop(JNIEnv *env) { return AndroidRuntime::registerNativeMethods(env, "android/server/BluetoothEventLoop", sMethods, NELEM(sMethods)); } } /* namespace android */