summaryrefslogtreecommitdiffstats
path: root/core/jni/android_server_BluetoothEventLoop.cpp
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:31:44 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2009-03-03 19:31:44 -0800
commit9066cfe9886ac131c34d59ed0e2d287b0e3c0087 (patch)
treed88beb88001f2482911e3d28e43833b50e4b4e97 /core/jni/android_server_BluetoothEventLoop.cpp
parentd83a98f4ce9cfa908f5c54bbd70f03eec07e7553 (diff)
downloadframeworks_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.cpp826
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 */