diff options
author | Claudio Takahasi <claudio.takahasi@openbossa.org> | 2013-01-16 10:31:27 -0300 |
---|---|---|
committer | Denis Kenzior <denkenz@gmail.com> | 2013-01-16 13:37:23 -0600 |
commit | caad5322f6117ab53bacc5d1999b055f7839beab (patch) | |
tree | 810492d01e6b16eaf7f953b33d480d927d7add84 /plugins/bluez4.c | |
parent | 145112dad26f79c8d8eaa9f0f1411a49b6fb7001 (diff) | |
download | ofono-caad5322f6117ab53bacc5d1999b055f7839beab.tar.bz2 |
bluetooth: Rename bluetooth plugins
As BlueZ 5 introduced backwards incompatible API changes, and we want to
keep support for BlueZ 4 based plugins for some time, we need to separate
the plugins that are based on BlueZ 4 from the ones based on BlueZ 5.
The bluetooth.c plugin has now been renamed to bluez4.c and the hfp_hf.c
plugin is renamed to hfp_hf_bluez4. This will make it easy to add a
HFP HF plugin for BlueZ 5.
Diffstat (limited to 'plugins/bluez4.c')
-rw-r--r-- | plugins/bluez4.c | 989 |
1 files changed, 989 insertions, 0 deletions
diff --git a/plugins/bluez4.c b/plugins/bluez4.c new file mode 100644 index 00000000..6a29d9fa --- /dev/null +++ b/plugins/bluez4.c @@ -0,0 +1,989 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) 2008-2011 Intel Corporation. All rights reserved. + * Copyright (C) 2010 ProFUSION embedded systems + * Copyright (C) 2010 Gustavo F. Padovan <gustavo@padovan.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <unistd.h> +#include <glib.h> +#include <gdbus.h> + +#define OFONO_API_SUBJECT_TO_CHANGE +#include <ofono/plugin.h> +#include <ofono/log.h> + +#include <btio.h> +#include "bluez4.h" + +static DBusConnection *connection; +static GHashTable *uuid_hash = NULL; +static GHashTable *adapter_address_hash = NULL; +static gint bluetooth_refcount; +static GSList *server_list = NULL; +static const char *adapter_any_name = "any"; +static char *adapter_any_path; + +#define TIMEOUT 60 /* Timeout for user response (seconds) */ + +struct server { + guint8 channel; + char *sdp_record; + guint32 handle; + GIOChannel *io; + ConnectFunc connect_cb; + gpointer user_data; +}; + +struct cb_data { + struct server *server; + char *path; + guint source; + GIOChannel *io; +}; + +void bluetooth_create_path(const char *dev_addr, const char *adapter_addr, + char *buf, int size) +{ + int i, j; + + for (i = 0, j = 0; adapter_addr[j] && i < size - 1; j++) + if (adapter_addr[j] >= '0' && adapter_addr[j] <= '9') + buf[i++] = adapter_addr[j]; + else if (adapter_addr[j] >= 'A' && adapter_addr[j] <= 'F') + buf[i++] = adapter_addr[j]; + + if (i < size - 1) + buf[i++] = '_'; + + for (j = 0; dev_addr[j] && i < size - 1; j++) + if (dev_addr[j] >= '0' && dev_addr[j] <= '9') + buf[i++] = dev_addr[j]; + else if (dev_addr[j] >= 'A' && dev_addr[j] <= 'F') + buf[i++] = dev_addr[j]; + + buf[i] = '\0'; +} + +int bluetooth_send_with_reply(const char *path, const char *interface, + const char *method, DBusPendingCall **call, + DBusPendingCallNotifyFunction cb, + void *user_data, DBusFreeFunction free_func, + int timeout, int type, ...) +{ + DBusMessage *msg; + DBusPendingCall *c; + va_list args; + int err; + + msg = dbus_message_new_method_call(BLUEZ_SERVICE, path, + interface, method); + if (msg == NULL) { + ofono_error("Unable to allocate new D-Bus %s message", method); + err = -ENOMEM; + goto fail; + } + + va_start(args, type); + + if (!dbus_message_append_args_valist(msg, type, args)) { + va_end(args); + err = -EIO; + goto fail; + } + + va_end(args); + + if (timeout > 0) + timeout *= 1000; + + if (!dbus_connection_send_with_reply(connection, msg, &c, timeout)) { + ofono_error("Sending %s failed", method); + err = -EIO; + goto fail; + } + + if (call != NULL) + *call = c; + + dbus_pending_call_set_notify(c, cb, user_data, free_func); + dbus_pending_call_unref(c); + + dbus_message_unref(msg); + + return 0; + +fail: + if (free_func && user_data) + free_func(user_data); + + if (msg) + dbus_message_unref(msg); + + return err; +} + +typedef void (*PropertyHandler)(DBusMessageIter *iter, gpointer user_data); + +struct property_handler { + const char *property; + PropertyHandler callback; + gpointer user_data; +}; + +static gint property_handler_compare(gconstpointer a, gconstpointer b) +{ + const struct property_handler *handler = a; + const char *property = b; + + return strcmp(handler->property, property); +} + +void bluetooth_parse_properties(DBusMessage *reply, const char *property, ...) +{ + va_list args; + GSList *prop_handlers = NULL; + DBusMessageIter array, dict; + + va_start(args, property); + + while (property != NULL) { + struct property_handler *handler = + g_new0(struct property_handler, 1); + + handler->property = property; + handler->callback = va_arg(args, PropertyHandler); + handler->user_data = va_arg(args, gpointer); + + property = va_arg(args, const char *); + + prop_handlers = g_slist_prepend(prop_handlers, handler); + } + + va_end(args); + + if (dbus_message_iter_init(reply, &array) == FALSE) + goto done; + + if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) + goto done; + + dbus_message_iter_recurse(&array, &dict); + + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry, value; + const char *key; + GSList *l; + + dbus_message_iter_recurse(&dict, &entry); + + if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING) + goto done; + + dbus_message_iter_get_basic(&entry, &key); + + dbus_message_iter_next(&entry); + + if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT) + goto done; + + dbus_message_iter_recurse(&entry, &value); + + l = g_slist_find_custom(prop_handlers, key, + property_handler_compare); + + if (l) { + struct property_handler *handler = l->data; + + handler->callback(&value, handler->user_data); + } + + dbus_message_iter_next(&dict); + } + +done: + g_slist_foreach(prop_handlers, (GFunc) g_free, NULL); + g_slist_free(prop_handlers); +} + +static void parse_uuids(DBusMessageIter *array, gpointer user_data) +{ + GSList **uuids = user_data; + DBusMessageIter value; + + if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY) + return; + + dbus_message_iter_recurse(array, &value); + + while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) { + const char *uuid; + + dbus_message_iter_get_basic(&value, &uuid); + + *uuids = g_slist_prepend(*uuids, (char *) uuid); + + dbus_message_iter_next(&value); + } +} + +static void parse_string(DBusMessageIter *iter, gpointer user_data) +{ + char **str = user_data; + int arg_type = dbus_message_iter_get_arg_type(iter); + + if (arg_type != DBUS_TYPE_OBJECT_PATH && arg_type != DBUS_TYPE_STRING) + return; + + dbus_message_iter_get_basic(iter, str); +} + +static void bluetooth_probe(GSList *uuids, const char *path, + const char *device, const char *adapter, + const char *alias) +{ + for (; uuids; uuids = uuids->next) { + struct bluetooth_profile *driver; + const char *uuid = uuids->data; + int err; + + driver = g_hash_table_lookup(uuid_hash, uuid); + if (driver == NULL) + continue; + + err = driver->probe(path, device, adapter, alias); + if (err == 0 || err == -EALREADY) + continue; + + ofono_error("%s probe: %s (%d)", driver->name, strerror(-err), + -err); + } +} + +static void device_properties_cb(DBusPendingCall *call, gpointer user_data) +{ + DBusMessage *reply; + const char *path = user_data; + const char *adapter = NULL; + const char *adapter_addr = NULL; + const char *device_addr = NULL; + const char *alias = NULL; + struct DBusError derr; + GSList *uuids = NULL; + + reply = dbus_pending_call_steal_reply(call); + + dbus_error_init(&derr); + + if (dbus_set_error_from_message(&derr, reply)) { + ofono_error("Device.GetProperties replied an error: %s, %s", + derr.name, derr.message); + dbus_error_free(&derr); + goto done; + } + + DBG(""); + + bluetooth_parse_properties(reply, "UUIDs", parse_uuids, &uuids, + "Adapter", parse_string, &adapter, + "Address", parse_string, &device_addr, + "Alias", parse_string, &alias, NULL); + + if (adapter) + adapter_addr = g_hash_table_lookup(adapter_address_hash, + adapter); + + if (!device_addr || !adapter_addr) + goto done; + + bluetooth_probe(uuids, path, device_addr, adapter_addr, alias); + +done: + g_slist_free(uuids); + dbus_message_unref(reply); +} + +static void parse_devices(DBusMessageIter *array, gpointer user_data) +{ + DBusMessageIter value; + GSList **device_list = user_data; + + DBG(""); + + if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY) + return; + + dbus_message_iter_recurse(array, &value); + + while (dbus_message_iter_get_arg_type(&value) + == DBUS_TYPE_OBJECT_PATH) { + const char *path; + + dbus_message_iter_get_basic(&value, &path); + + *device_list = g_slist_prepend(*device_list, (gpointer) path); + + dbus_message_iter_next(&value); + } +} + +static gboolean property_changed(DBusConnection *conn, DBusMessage *msg, + void *user_data) +{ + const char *property; + DBusMessageIter iter; + + dbus_message_iter_init(msg, &iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return FALSE; + + dbus_message_iter_get_basic(&iter, &property); + if (g_str_equal(property, "UUIDs") == TRUE) { + GSList *uuids = NULL; + const char *path = dbus_message_get_path(msg); + DBusMessageIter variant; + + if (!dbus_message_iter_next(&iter)) + return FALSE; + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) + return FALSE; + + dbus_message_iter_recurse(&iter, &variant); + + parse_uuids(&variant, &uuids); + + /* We need the full set of properties to be able to create + * the modem properly, including Adapter and Alias, so + * refetch everything again + */ + if (uuids) + bluetooth_send_with_reply(path, BLUEZ_DEVICE_INTERFACE, + "GetProperties", NULL, + device_properties_cb, g_strdup(path), + g_free, -1, DBUS_TYPE_INVALID); + } else if (g_str_equal(property, "Alias") == TRUE) { + const char *path = dbus_message_get_path(msg); + struct bluetooth_profile *profile; + const char *alias = NULL; + DBusMessageIter variant; + GHashTableIter hash_iter; + gpointer key, value; + + if (!dbus_message_iter_next(&iter)) + return FALSE; + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) + return FALSE; + + dbus_message_iter_recurse(&iter, &variant); + + parse_string(&variant, &alias); + + g_hash_table_iter_init(&hash_iter, uuid_hash); + while (g_hash_table_iter_next(&hash_iter, &key, &value)) { + profile = value; + if (profile->set_alias) + profile->set_alias(path, alias); + } + } + + return TRUE; +} + +static void adapter_properties_cb(DBusPendingCall *call, gpointer user_data) +{ + const char *path = user_data; + DBusMessage *reply; + DBusError derr; + GSList *device_list = NULL; + GSList *l; + const char *addr; + + reply = dbus_pending_call_steal_reply(call); + + dbus_error_init(&derr); + + if (dbus_set_error_from_message(&derr, reply)) { + ofono_error("Adapter.GetProperties replied an error: %s, %s", + derr.name, derr.message); + dbus_error_free(&derr); + goto done; + } + + DBG(""); + + bluetooth_parse_properties(reply, + "Devices", parse_devices, &device_list, + "Address", parse_string, &addr, + NULL); + + DBG("Adapter Address: %s, Path: %s", addr, path); + g_hash_table_insert(adapter_address_hash, + g_strdup(path), g_strdup(addr)); + + for (l = device_list; l; l = l->next) { + const char *device = l->data; + + bluetooth_send_with_reply(device, BLUEZ_DEVICE_INTERFACE, + "GetProperties", NULL, + device_properties_cb, g_strdup(device), + g_free, -1, DBUS_TYPE_INVALID); + } + +done: + g_slist_free(device_list); + dbus_message_unref(reply); +} + +static void get_adapter_properties(const char *path, const char *handle, + gpointer user_data) +{ + bluetooth_send_with_reply(path, BLUEZ_ADAPTER_INTERFACE, + "GetProperties", NULL, adapter_properties_cb, + g_strdup(path), g_free, -1, DBUS_TYPE_INVALID); +} + +static void remove_record(struct server *server) +{ + DBusMessage *msg; + + if (server->handle == 0) + return; + + msg = dbus_message_new_method_call(BLUEZ_SERVICE, adapter_any_path, + BLUEZ_SERVICE_INTERFACE, + "RemoveRecord"); + if (msg == NULL) { + ofono_error("Unable to allocate D-Bus RemoveRecord message"); + return; + } + + dbus_message_append_args(msg, DBUS_TYPE_UINT32, &server->handle, + DBUS_TYPE_INVALID); + g_dbus_send_message(connection, msg); + + ofono_info("Unregistered handle for channel %d: 0x%x", + server->channel, server->handle); +} + +static void cb_data_destroy(gpointer data) +{ + struct cb_data *cb_data = data; + + if (cb_data->source != 0) + g_source_remove(cb_data->source); + + g_free(cb_data->path); + g_free(cb_data); +} + +static void cancel_authorization(struct cb_data *user_data) +{ + DBusMessage *msg; + + msg = dbus_message_new_method_call(BLUEZ_SERVICE, user_data->path, + BLUEZ_SERVICE_INTERFACE, + "CancelAuthorization"); + + if (msg == NULL) { + ofono_error("Unable to allocate D-Bus CancelAuthorization" + " message"); + return; + } + + g_dbus_send_message(connection, msg); +} + +static gboolean client_event(GIOChannel *chan, GIOCondition cond, gpointer data) +{ + struct cb_data *cb_data = data; + + cancel_authorization(cb_data); + cb_data->source = 0; + + return FALSE; +} + +static void auth_cb(DBusPendingCall *call, gpointer user_data) +{ + struct cb_data *cb_data = user_data; + struct server *server = cb_data->server; + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError derr; + GError *err = NULL; + + dbus_error_init(&derr); + + if (dbus_set_error_from_message(&derr, reply)) { + ofono_error("RequestAuthorization error: %s, %s", + derr.name, derr.message); + + if (dbus_error_has_name(&derr, DBUS_ERROR_NO_REPLY)) + cancel_authorization(cb_data); + + dbus_error_free(&derr); + } else { + ofono_info("RequestAuthorization succeeded"); + + if (!bt_io_accept(cb_data->io, server->connect_cb, + server->user_data, NULL, &err)) { + ofono_error("%s", err->message); + g_error_free(err); + } + } + + dbus_message_unref(reply); +} + +static void new_connection(GIOChannel *io, gpointer user_data) +{ + struct server *server = user_data; + struct cb_data *cbd; + const char *addr; + GError *err = NULL; + char laddress[18], raddress[18]; + guint8 channel; + GHashTableIter iter; + gpointer key, value; + const char *path; + + bt_io_get(io, BT_IO_RFCOMM, &err, BT_IO_OPT_SOURCE, laddress, + BT_IO_OPT_DEST, raddress, + BT_IO_OPT_CHANNEL, &channel, + BT_IO_OPT_INVALID); + if (err) { + ofono_error("%s", err->message); + g_error_free(err); + return; + } + + ofono_info("New connection for %s on channel %u from: %s,", laddress, + channel, raddress); + + path = NULL; + g_hash_table_iter_init(&iter, adapter_address_hash); + + while (g_hash_table_iter_next(&iter, &key, &value)) { + if (g_str_equal(laddress, value) == TRUE) { + path = key; + break; + } + } + + if (path == NULL) + return; + + cbd = g_try_new0(struct cb_data, 1); + if (cbd == NULL) { + ofono_error("Unable to allocate client cb_data structure"); + return; + } + + cbd->path = g_strdup(path); + cbd->server = server; + cbd->io = io; + + addr = raddress; + + if (bluetooth_send_with_reply(path, BLUEZ_SERVICE_INTERFACE, + "RequestAuthorization", NULL, + auth_cb, cbd, cb_data_destroy, + TIMEOUT, DBUS_TYPE_STRING, &addr, + DBUS_TYPE_UINT32, &server->handle, + DBUS_TYPE_INVALID) < 0) { + ofono_error("Request Bluetooth authorization failed"); + return; + } + + ofono_info("RequestAuthorization(%s, 0x%x)", raddress, server->handle); + + cbd->source = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL, + client_event, cbd); +} + +static void remove_service_handle(gpointer data, gpointer user_data) +{ + struct server *server = data; + + server->handle = 0; +} + +static void add_record_cb(DBusPendingCall *call, gpointer user_data) +{ + struct server *server = user_data; + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError derr; + + dbus_error_init(&derr); + + if (dbus_set_error_from_message(&derr, reply)) { + ofono_error("Replied with an error: %s, %s", + derr.name, derr.message); + dbus_error_free(&derr); + goto done; + } + + dbus_message_get_args(reply, NULL, DBUS_TYPE_UINT32, &server->handle, + DBUS_TYPE_INVALID); + + ofono_info("Registered handle for channel %d: 0x%x", + server->channel, server->handle); + +done: + dbus_message_unref(reply); +} + +static void add_record(gpointer data, gpointer user_data) +{ + struct server *server = data; + + if (server->sdp_record == NULL) + return; + + bluetooth_send_with_reply(adapter_any_path, + BLUEZ_SERVICE_INTERFACE, "AddRecord", + NULL, add_record_cb, server, NULL, -1, + DBUS_TYPE_STRING, &server->sdp_record, + DBUS_TYPE_INVALID); +} + +static void find_adapter_cb(DBusPendingCall *call, gpointer user_data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError derr; + const char *path; + + dbus_error_init(&derr); + + if (dbus_set_error_from_message(&derr, reply)) { + ofono_error("Replied with an error: %s, %s", + derr.name, derr.message); + dbus_error_free(&derr); + goto done; + } + + dbus_message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); + + adapter_any_path = g_strdup(path); + + g_slist_foreach(server_list, (GFunc) add_record, NULL); + +done: + dbus_message_unref(reply); +} + +static gboolean adapter_added(DBusConnection *conn, DBusMessage *message, + void *user_data) +{ + const char *path; + + dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); + + bluetooth_send_with_reply(path, BLUEZ_ADAPTER_INTERFACE, + "GetProperties", NULL, adapter_properties_cb, + g_strdup(path), g_free, -1, DBUS_TYPE_INVALID); + + return TRUE; +} + +static void bluetooth_remove(gpointer key, gpointer value, gpointer user_data) +{ + struct bluetooth_profile *profile = value; + + profile->remove(user_data); +} + +static gboolean adapter_removed(DBusConnection *conn, + DBusMessage *message, void *user_data) +{ + const char *path; + + if (dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID) == FALSE) + return FALSE; + + g_hash_table_foreach(uuid_hash, bluetooth_remove, (gpointer) path); + g_hash_table_remove(adapter_address_hash, path); + + return TRUE; +} + +static gboolean device_removed(DBusConnection *conn, + DBusMessage *message, void *user_data) +{ + const char *path; + + if (dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID) == FALSE) + return FALSE; + + g_hash_table_foreach(uuid_hash, bluetooth_remove, (gpointer) path); + + return TRUE; +} + +static void parse_adapters(DBusMessageIter *array, gpointer user_data) +{ + DBusMessageIter value; + + DBG(""); + + if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY) + return; + + dbus_message_iter_recurse(array, &value); + + while (dbus_message_iter_get_arg_type(&value) + == DBUS_TYPE_OBJECT_PATH) { + const char *path; + + dbus_message_iter_get_basic(&value, &path); + + DBG("Calling GetProperties on %s", path); + + bluetooth_send_with_reply(path, BLUEZ_ADAPTER_INTERFACE, + "GetProperties", NULL, adapter_properties_cb, + g_strdup(path), g_free, -1, DBUS_TYPE_INVALID); + + dbus_message_iter_next(&value); + } +} + +static void manager_properties_cb(DBusPendingCall *call, gpointer user_data) +{ + DBusMessage *reply; + DBusError derr; + + reply = dbus_pending_call_steal_reply(call); + + dbus_error_init(&derr); + + if (dbus_set_error_from_message(&derr, reply)) { + ofono_error("Manager.GetProperties() replied an error: %s, %s", + derr.name, derr.message); + dbus_error_free(&derr); + goto done; + } + + DBG(""); + + bluetooth_parse_properties(reply, "Adapters", parse_adapters, NULL, + NULL); + +done: + dbus_message_unref(reply); +} + +static void bluetooth_connect(DBusConnection *conn, void *user_data) +{ + bluetooth_send_with_reply("/", BLUEZ_MANAGER_INTERFACE, "GetProperties", + NULL, manager_properties_cb, NULL, NULL, -1, + DBUS_TYPE_INVALID); + + bluetooth_send_with_reply("/", BLUEZ_MANAGER_INTERFACE, "FindAdapter", + NULL, find_adapter_cb, NULL, NULL, -1, + DBUS_TYPE_STRING, &adapter_any_name, + DBUS_TYPE_INVALID); +} + +static void bluetooth_disconnect(DBusConnection *conn, void *user_data) +{ + if (uuid_hash == NULL) + return; + + g_hash_table_foreach(uuid_hash, bluetooth_remove, NULL); + + g_slist_foreach(server_list, (GFunc) remove_service_handle, NULL); +} + +static guint bluetooth_watch; +static guint adapter_added_watch; +static guint adapter_removed_watch; +static guint device_removed_watch; +static guint property_watch; + +static void bluetooth_ref(void) +{ + if (bluetooth_refcount > 0) + goto increment; + + connection = ofono_dbus_get_connection(); + + bluetooth_watch = g_dbus_add_service_watch(connection, BLUEZ_SERVICE, + bluetooth_connect, + bluetooth_disconnect, NULL, NULL); + + adapter_added_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE, + NULL, BLUEZ_MANAGER_INTERFACE, + "AdapterAdded", + adapter_added, NULL, NULL); + + adapter_removed_watch = g_dbus_add_signal_watch(connection, + BLUEZ_SERVICE, NULL, + BLUEZ_MANAGER_INTERFACE, + "AdapterRemoved", + adapter_removed, NULL, NULL); + + device_removed_watch = g_dbus_add_signal_watch(connection, + BLUEZ_SERVICE, NULL, + BLUEZ_ADAPTER_INTERFACE, + "DeviceRemoved", + device_removed, NULL, NULL); + + property_watch = g_dbus_add_signal_watch(connection, + BLUEZ_SERVICE, NULL, + BLUEZ_DEVICE_INTERFACE, + "PropertyChanged", + property_changed, NULL, NULL); + + if (bluetooth_watch == 0 || adapter_added_watch == 0 || + adapter_removed_watch == 0 || property_watch == 0) { + goto remove; + } + + uuid_hash = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, NULL); + + adapter_address_hash = g_hash_table_new_full(g_str_hash, g_str_equal, + g_free, g_free); + +increment: + g_atomic_int_inc(&bluetooth_refcount); + + return; + +remove: + g_dbus_remove_watch(connection, bluetooth_watch); + g_dbus_remove_watch(connection, adapter_added_watch); + g_dbus_remove_watch(connection, adapter_removed_watch); + g_dbus_remove_watch(connection, property_watch); +} + +static void bluetooth_unref(void) +{ + if (g_atomic_int_dec_and_test(&bluetooth_refcount) == FALSE) + return; + + g_free(adapter_any_path); + adapter_any_path = NULL; + + g_dbus_remove_watch(connection, bluetooth_watch); + g_dbus_remove_watch(connection, adapter_added_watch); + g_dbus_remove_watch(connection, adapter_removed_watch); + g_dbus_remove_watch(connection, property_watch); + + g_hash_table_destroy(uuid_hash); + g_hash_table_destroy(adapter_address_hash); +} + +void bluetooth_get_properties() +{ + g_hash_table_foreach(adapter_address_hash, + (GHFunc) get_adapter_properties, NULL); +} + +int bluetooth_register_uuid(const char *uuid, struct bluetooth_profile *profile) +{ + bluetooth_ref(); + + g_hash_table_insert(uuid_hash, g_strdup(uuid), profile); + + g_hash_table_foreach(adapter_address_hash, + (GHFunc) get_adapter_properties, NULL); + + return 0; +} + +void bluetooth_unregister_uuid(const char *uuid) +{ + g_hash_table_remove(uuid_hash, uuid); + + bluetooth_unref(); +} + +struct server *bluetooth_register_server(guint8 channel, const char *sdp_record, + ConnectFunc cb, gpointer user_data) +{ + struct server *server; + GError *err = NULL; + + server = g_try_new0(struct server, 1); + if (!server) + return NULL; + + server->channel = channel; + + server->io = bt_io_listen(BT_IO_RFCOMM, NULL, new_connection, + server, NULL, &err, + BT_IO_OPT_CHANNEL, server->channel, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_INVALID); + if (server->io == NULL) { + g_error_free(err); + g_free(server); + return NULL; + } + + bluetooth_ref(); + + if (sdp_record != NULL) + server->sdp_record = g_strdup(sdp_record); + + server->connect_cb = cb; + server->user_data = user_data; + + server_list = g_slist_prepend(server_list, server); + + if (adapter_any_path != NULL) + add_record(server, NULL); + + return server; +} + +void bluetooth_unregister_server(struct server *server) +{ + server_list = g_slist_remove(server_list, server); + + remove_record(server); + + if (server->io != NULL) { + g_io_channel_shutdown(server->io, TRUE, NULL); + g_io_channel_unref(server->io); + server->io = NULL; + } + + g_free(server->sdp_record); + g_free(server); + + bluetooth_unref(); +} + +OFONO_PLUGIN_DEFINE(bluez4, "Bluetooth Utils Plugins", VERSION, + OFONO_PLUGIN_PRIORITY_DEFAULT, NULL, NULL) |