diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:31:44 -0800 |
commit | 9066cfe9886ac131c34d59ed0e2d287b0e3c0087 (patch) | |
tree | d88beb88001f2482911e3d28e43833b50e4b4e97 /core/jni/android_server_BluetoothEventLoop.cpp | |
parent | d83a98f4ce9cfa908f5c54bbd70f03eec07e7553 (diff) | |
download | frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.zip frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.gz frameworks_base-9066cfe9886ac131c34d59ed0e2d287b0e3c0087.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'core/jni/android_server_BluetoothEventLoop.cpp')
-rw-r--r-- | core/jni/android_server_BluetoothEventLoop.cpp | 826 |
1 files changed, 826 insertions, 0 deletions
diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp new file mode 100644 index 0000000..adfd912 --- /dev/null +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -0,0 +1,826 @@ +/* +** 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 <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> + +#ifdef HAVE_BLUETOOTH +#include <dbus/dbus.h> +#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 +#define BOND_RESULT_DISCOVERY_IN_PROGRESS 5 +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") && + !strcmp(err.message, "Bonding in progress")) { + LOGV("... error = %s (%s)\n", err.name, err.message); + goto done; + } else if (!strcmp(err.name, BLUEZ_DBUS_BASE_IFC ".Error.InProgress") && + !strcmp(err.message, "Discover in progress")) { + LOGV("... error = %s (%s)\n", err.name, err.message); + result = BOND_RESULT_DISCOVERY_IN_PROGRESS; + } 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 */ |