| /* |
| ** 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; |
| |
| static jmethodID method_onRestartRequired; |
| |
| 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"); |
| |
| method_onRestartRequired = env->GetMethodID(clazz, "onRestartRequired", "()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='org.freedesktop.DBus'", |
| &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='"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_bus_remove_match(nat->conn, |
| "type='signal',interface='org.freedesktop.DBus'", |
| &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; |
| } else if (dbus_message_is_signal(msg, |
| "org.freedesktop.DBus", |
| "NameOwnerChanged")) { |
| char *c_name; |
| char *c_old_owner; |
| char *c_new_owner; |
| if (dbus_message_get_args(msg, &err, |
| DBUS_TYPE_STRING, &c_name, |
| DBUS_TYPE_STRING, &c_old_owner, |
| DBUS_TYPE_STRING, &c_new_owner, |
| DBUS_TYPE_INVALID)) { |
| LOGV("... name = %s", c_name); |
| LOGV("... old_owner = %s", c_old_owner); |
| LOGV("... new_owner = %s", c_new_owner); |
| if (!strcmp(c_name, "org.bluez") && c_new_owner[0] != '\0') { |
| // New owner of org.bluez. This can only happen when hcid |
| // restarts. Need to restart framework BT services to recover. |
| LOGE("Looks like hcid restarted"); |
| env->CallVoidMethod(nat->me, method_onRestartRequired); |
| } |
| } 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 */ |