X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=plugins%2Fbluetooth.c;h=704d21644a1ecba0cca47b40910c765262a8736e;hb=132afd1b759135225e1582fce0125759b8580d9e;hp=fa2c3c6f56a5ad7060cadfaafe81846eee287010;hpb=423d4b077a9ce6539ac623d6dd289409be26894f;p=platform%2Fupstream%2Fconnman.git diff --git a/plugins/bluetooth.c b/plugins/bluetooth.c old mode 100644 new mode 100755 index fa2c3c6..704d216 --- a/plugins/bluetooth.c +++ b/plugins/bluetooth.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2007-2009 Intel Corporation. All rights reserved. + * Copyright (C) 2013 Intel Corporation. All rights reserved. * * 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 @@ -24,857 +24,1000 @@ #endif #include -#include - -#include +#include #define CONNMAN_API_SUBJECT_TO_CHANGE #include +#include +#include #include #include -#include -#include - -#define BLUEZ_SERVICE "org.bluez" -#define BLUEZ_MANAGER_INTERFACE BLUEZ_SERVICE ".Manager" -#define BLUEZ_ADAPTER_INTERFACE BLUEZ_SERVICE ".Adapter" -#define BLUEZ_DEVICE_INTERFACE BLUEZ_SERVICE ".Device" -#define BLUEZ_NETWORK_INTERFACE BLUEZ_SERVICE ".Network" - -#define LIST_ADAPTERS "ListAdapters" -#define ADAPTER_ADDED "AdapterAdded" -#define ADAPTER_REMOVED "AdapterRemoved" - -#define PROPERTY_CHANGED "PropertyChanged" -#define GET_PROPERTIES "GetProperties" -#define SET_PROPERTY "SetProperty" +#include -#define CONNECT "Connect" -#define DISCONNECT "Disconnect" +#define BLUEZ_SERVICE "org.bluez" +#define BLUEZ_PATH "/org/bluez" +#define BLUETOOTH_PAN_PANU "00001115-0000-1000-8000-00805f9b34fb" +#define BLUETOOTH_PAN_NAP "00001116-0000-1000-8000-00805f9b34fb" +#define BLUETOOTH_PAN_GN "00001117-0000-1000-8000-00805f9b34fb" -#define TIMEOUT 5000 +#define BLUETOOTH_ADDR_LEN 6 -typedef void (* properties_callback_t) (DBusConnection *connection, - const char *path, - DBusMessage *message, - void *user_data); +static DBusConnection *connection; +static GDBusClient *client; +static GHashTable *devices; +static GHashTable *networks; +static bool bluetooth_tethering; -struct properties_data { - DBusConnection *connection; - DBusMessage *message; - properties_callback_t callback; - void *user_data; +struct bluetooth_pan { + struct connman_network *network; + GDBusProxy *btdevice_proxy; + GDBusProxy *btnetwork_proxy; + const char *pan_role; }; -static void get_properties_reply(DBusPendingCall *call, void *user_data) +static void address2ident(const char *address, char *ident) { - struct properties_data *data = user_data; - DBusMessage *reply; - const char *path; + int i; - reply = dbus_pending_call_steal_reply(call); - if (reply == NULL) - goto done; + for (i = 0; i < BLUETOOTH_ADDR_LEN; i++) { + ident[i * 2] = address[i * 3]; + ident[i * 2 + 1] = address[i * 3 + 1]; + } + ident[BLUETOOTH_ADDR_LEN * 2] = '\0'; +} - path = dbus_message_get_path(data->message); +static const char *proxy_get_string(GDBusProxy *proxy, const char *property) +{ + DBusMessageIter iter; + const char *str; - data->callback(data->connection, path, reply, data->user_data); + if (!g_dbus_proxy_get_property(proxy, property, &iter)) + return NULL; + dbus_message_iter_get_basic(&iter, &str); + return str; +} - dbus_message_unref(reply); +static bool proxy_get_bool(GDBusProxy *proxy, const char *property) +{ + DBusMessageIter iter; + dbus_bool_t value; -done: - dbus_message_unref(data->message); - g_free(data); + if (!g_dbus_proxy_get_property(proxy, property, &iter)) + return false; + dbus_message_iter_get_basic(&iter, &value); + return value; } -static void get_properties(DBusConnection *connection, - const char *path, const char *interface, - properties_callback_t callback, void *user_data) +static const char *proxy_get_role(GDBusProxy *proxy) { - struct properties_data *data; - DBusMessage *message; - DBusPendingCall *call; + DBusMessageIter iter, value; + const char *pref = NULL; + + if (!proxy) + return NULL; + + if (!g_dbus_proxy_get_property(proxy, "UUIDs", &iter)) + return NULL; + + dbus_message_iter_recurse(&iter, &value); + while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) { + const char *uuid; + + dbus_message_iter_get_basic(&value, &uuid); + /* + * If a device offers more than one role, we prefer NAP, + * then GN, then PANU. + */ + if (!strcmp(uuid, BLUETOOTH_PAN_NAP)) + return "nap"; + if (!strcmp(uuid, BLUETOOTH_PAN_GN)) + pref = "gn"; + if (!strcmp(uuid, BLUETOOTH_PAN_PANU) && !pref) + pref = "panu"; + + dbus_message_iter_next(&value); + } + + return pref; +} - DBG("path %s interface %s", path, interface); +static int bluetooth_pan_probe(struct connman_network *network) +{ + GHashTableIter iter; + gpointer key, value; - data = g_try_new0(struct properties_data, 1); - if (data == NULL) - return; + DBG("network %p", network); - message = dbus_message_new_method_call(BLUEZ_SERVICE, path, - interface, GET_PROPERTIES); - if (message == NULL) { - g_free(data); - return; - } + g_hash_table_iter_init(&iter, networks); - if (dbus_connection_send_with_reply(connection, message, - &call, TIMEOUT) == FALSE) { - connman_error("Failed to get properties for %s", interface); - dbus_message_unref(message); - g_free(data); - return; - } + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct bluetooth_pan *pan = value; - if (call == NULL) { - connman_error("D-Bus connection not available"); - dbus_message_unref(message); - g_free(data); - return; + if (network == pan->network) + return 0; } - data->connection = connection; - data->message = message; - data->callback = callback; - data->user_data = user_data; - - dbus_pending_call_set_notify(call, get_properties_reply, data, NULL); + return -EOPNOTSUPP; } -struct adapter_data { - DBusConnection *connection; -}; - -struct network_data { - DBusConnection *connection; - char *interface; -}; - -static int pan_probe(struct connman_network *network) +static void pan_remove_nap(struct bluetooth_pan *pan) { - struct connman_device *device = connman_network_get_device(network); - struct adapter_data *adapter; - struct network_data *data; + struct connman_device *device; + struct connman_network *network = pan->network; - DBG("network %p", network); - - if (device == NULL) - return -EINVAL; + DBG("network %p pan %p", pan->network, pan); - adapter = connman_device_get_data(device); - if (adapter == NULL) - return -EINVAL; - - data = g_try_new0(struct network_data, 1); - if (data == NULL) - return -ENOMEM; + if (!network) + return; - data->connection = adapter->connection; + pan->network = NULL; + connman_network_set_data(network, NULL); - connman_network_set_data(network, data); + device = connman_network_get_device(network); + if (device) + connman_device_remove_network(device, network); - return 0; + connman_network_unref(network); } -static void pan_remove(struct connman_network *network) +static void bluetooth_pan_remove(struct connman_network *network) { - struct network_data *data = connman_network_get_data(network); + struct bluetooth_pan *pan = connman_network_get_data(network); - DBG("network %p", network); + DBG("network %p pan %p", network, pan); connman_network_set_data(network, NULL); - g_free(data); + if (pan) + pan_remove_nap(pan); } -static void connect_reply(DBusPendingCall *call, void *user_data) +static bool pan_connect(struct bluetooth_pan *pan, + const char *iface) { - struct connman_network *network = user_data; - struct network_data *data = connman_network_get_data(network); - DBusMessage *reply; - DBusError error; - const char *interface = NULL; int index; - DBG("network %p", network); + if (!iface) { + if (!proxy_get_bool(pan->btnetwork_proxy, "Connected")) + return false; + iface = proxy_get_string(pan->btnetwork_proxy, "Interface"); + } - reply = dbus_pending_call_steal_reply(call); - if (reply == NULL) - return; + if (!iface) + return false; - dbus_error_init(&error); + index = connman_inet_ifindex(iface); + if (index < 0) { + DBG("network %p invalid index %d", pan->network, index); + return false; + } - if (dbus_message_get_args(reply, &error, - DBUS_TYPE_STRING, &interface, - DBUS_TYPE_INVALID) == FALSE) { - if (dbus_error_is_set(&error) == TRUE) { - connman_error("%s", error.message); - dbus_error_free(&error); - } else - connman_error("Wrong arguments for connect"); - goto done; +#if defined TIZEN_EXT + if (pan->network) { +#endif + connman_network_set_index(pan->network, index); + connman_network_set_connected(pan->network, true); +#if defined TIZEN_EXT } +#endif + + return true; +} + +static void pan_connect_cb(DBusMessage *message, void *user_data) +{ + const char *path = user_data; + const char *iface = NULL; + struct bluetooth_pan *pan; + DBusMessageIter iter; - if (interface == NULL) - goto done; + pan = g_hash_table_lookup(networks, path); + if (!pan || !pan->network) { + DBG("network already removed"); + return; + } - DBG("interface %s", interface); + if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) { + const char *dbus_error = dbus_message_get_error_name(message); - data->interface = g_strdup(interface); + DBG("network %p %s", pan->network, dbus_error); - index = connman_inet_ifindex(interface); + if (strcmp(dbus_error, + "org.bluez.Error.AlreadyConnected") != 0) { + connman_network_set_associating(pan->network, false); + connman_network_set_error(pan->network, + CONNMAN_NETWORK_ERROR_ASSOCIATE_FAIL); + return; + } + } else { + if (dbus_message_iter_init(message, &iter) && + dbus_message_iter_get_arg_type(&iter) == + DBUS_TYPE_STRING) + dbus_message_iter_get_basic(&iter, &iface); + } - connman_network_set_index(network, index); - connman_network_set_connected(network, TRUE); + DBG("network %p interface %s", pan->network, iface); -done: - dbus_message_unref(reply); + pan_connect(pan, iface); } -static int pan_connect(struct connman_network *network) +static void pan_connect_append(DBusMessageIter *iter, + void *user_data) { - struct network_data *data = connman_network_get_data(network); - const char *path = connman_network_get_string(network, "Node"); - const char *uuid = "nap"; - DBusMessage *message; - DBusPendingCall *call; + const char *path = user_data; + struct bluetooth_pan *pan; - DBG("network %p", network); + pan = g_hash_table_lookup(networks, path); + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &pan->pan_role); +} - message = dbus_message_new_method_call(BLUEZ_SERVICE, path, - BLUEZ_NETWORK_INTERFACE, CONNECT); - if (message == NULL) - return -ENOMEM; +static int bluetooth_pan_connect(struct connman_network *network) +{ + struct bluetooth_pan *pan = connman_network_get_data(network); + const char *path; - dbus_message_append_args(message, DBUS_TYPE_STRING, &uuid, - DBUS_TYPE_INVALID); + DBG("network %p", network); - if (dbus_connection_send_with_reply(data->connection, message, - &call, TIMEOUT * 10) == FALSE) { - connman_error("Failed to connect service"); - dbus_message_unref(message); + if (!pan) return -EINVAL; - } - if (call == NULL) { - connman_error("D-Bus connection not available"); - dbus_message_unref(message); - return -EINVAL; - } + path = g_dbus_proxy_get_path(pan->btnetwork_proxy); - dbus_pending_call_set_notify(call, connect_reply, network, NULL); + if (!g_dbus_proxy_method_call(pan->btnetwork_proxy, "Connect", + pan_connect_append, pan_connect_cb, + g_strdup(path), g_free)) + return -EIO; - dbus_message_unref(message); +#if defined TIZEN_EXT + if (pan->network) +#endif + connman_network_set_associating(pan->network, true); return -EINPROGRESS; } -static void disconnect_reply(DBusPendingCall *call, void *user_data) +static void pan_disconnect_cb(DBusMessage *message, void *user_data) { - struct connman_network *network = user_data; - struct network_data *data = connman_network_get_data(network); - DBusMessage *reply; - DBusError error; - - DBG("network %p", network); + const char *path = user_data; + struct bluetooth_pan *pan; - reply = dbus_pending_call_steal_reply(call); - if (reply == NULL) + pan = g_hash_table_lookup(networks, path); + if (!pan || !pan->network) { + DBG("network already removed"); return; + } - dbus_error_init(&error); + if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) { + const char *dbus_error = dbus_message_get_error_name(message); - if (dbus_message_get_args(reply, &error, DBUS_TYPE_INVALID) == FALSE) { - if (dbus_error_is_set(&error) == TRUE) { - connman_error("%s", error.message); - dbus_error_free(&error); - } else - connman_error("Wrong arguments for disconnect"); - goto done; + DBG("network %p %s", pan->network, dbus_error); } - g_free(data->interface); - data->interface = NULL; + DBG("network %p", pan->network); - connman_network_set_connected(network, FALSE); - connman_network_set_index(network, -1); - -done: - dbus_message_unref(reply); +#if defined TIZEN_EXT + if (pan->network) +#endif + connman_network_set_connected(pan->network, false); } -static int pan_disconnect(struct connman_network *network) +static int bluetooth_pan_disconnect(struct connman_network *network) { - struct network_data *data = connman_network_get_data(network); - const char *path = connman_network_get_string(network, "Node"); - DBusMessage *message; - DBusPendingCall *call; + struct bluetooth_pan *pan = connman_network_get_data(network); + const char *path; DBG("network %p", network); - if (data->interface == NULL) + if (!pan) return -EINVAL; - message = dbus_message_new_method_call(BLUEZ_SERVICE, path, - BLUEZ_NETWORK_INTERFACE, DISCONNECT); - if (message == NULL) - return -ENOMEM; +#if defined TIZEN_EXT + if (connman_network_get_associating(network) == TRUE) + connman_network_clear_associating(network); +#endif - dbus_message_append_args(message, DBUS_TYPE_INVALID); + path = g_dbus_proxy_get_path(pan->btnetwork_proxy); - if (dbus_connection_send_with_reply(data->connection, message, - &call, TIMEOUT) == FALSE) { - connman_error("Failed to disconnect service"); - dbus_message_unref(message); - return -EINVAL; - } + if (!g_dbus_proxy_method_call(pan->btnetwork_proxy, "Disconnect", + NULL, pan_disconnect_cb, g_strdup(path), g_free)) + return -EIO; - if (call == NULL) { - connman_error("D-Bus connection not available"); - dbus_message_unref(message); - return -EINVAL; - } + return -EINPROGRESS; +} - dbus_pending_call_set_notify(call, disconnect_reply, network, NULL); +static void btnetwork_property_change(GDBusProxy *proxy, const char *name, + DBusMessageIter *iter, void *user_data) +{ + struct bluetooth_pan *pan; + dbus_bool_t connected; + bool proxy_connected, network_connected; - dbus_message_unref(message); + if (strcmp(name, "Connected") != 0) + return; - return -EINPROGRESS; -} + pan = g_hash_table_lookup(networks, g_dbus_proxy_get_path(proxy)); + if (!pan || !pan->network) + return; -static struct connman_network_driver pan_driver = { - .name = "bluetooth-pan", - .type = CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN, - .probe = pan_probe, - .remove = pan_remove, - .connect = pan_connect, - .disconnect = pan_disconnect, -}; + dbus_message_iter_get_basic(iter, &connected); + proxy_connected = connected; + + network_connected = connman_network_get_connected(pan->network); -static int bluetooth_probe(struct connman_device *adapter) + DBG("network %p network connected %d proxy connected %d", + pan->network, network_connected, proxy_connected); + + if (network_connected != proxy_connected) + connman_network_set_connected(pan->network, proxy_connected); +} + +static void pan_create_nap(struct bluetooth_pan *pan) { - struct adapter_data *data; + struct connman_device *device; + const char* role; + const char *adapter; - DBG("adapter %p", adapter); + role = proxy_get_role(pan->btdevice_proxy); + if (!role) { + pan_remove_nap(pan); + return; + } - data = g_try_new0(struct adapter_data, 1); - if (data == NULL) - return -ENOMEM; + adapter = proxy_get_string(pan->btdevice_proxy, "Adapter"); - data->connection = connman_dbus_get_connection(); - if (data->connection == NULL) { - g_free(data); - return -EIO; + if (!adapter) + return; + + device = g_hash_table_lookup(devices, adapter); + + if (!device || !connman_device_get_powered(device)) + return; + + if (!pan->network) { + const char *address; + char ident[BLUETOOTH_ADDR_LEN * 2 + 1]; + const char *name, *path; + + address = proxy_get_string(pan->btdevice_proxy, "Address"); + if (!address) { + connman_warn("Bluetooth device address missing"); + return; + } + + address2ident(address, ident); + + pan->network = connman_network_create(ident, + CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN); + + name = proxy_get_string(pan->btdevice_proxy, "Alias"); + path = g_dbus_proxy_get_path(pan->btnetwork_proxy); + + DBG("network %p %s %s", pan->network, path, name); + + if (!pan->network) { + connman_warn("Bluetooth network %s creation failed", + path); + return; + } + + connman_network_set_data(pan->network, pan); + connman_network_set_name(pan->network, name); + connman_network_set_group(pan->network, ident); } - connman_device_set_data(adapter, data); + pan->pan_role = role; + connman_device_add_network(device, pan->network); - return 0; + if (pan_connect(pan, NULL)) + DBG("network %p already connected", pan->network); } -static void bluetooth_remove(struct connman_device *adapter) +static void btdevice_property_change(GDBusProxy *proxy, const char *name, + DBusMessageIter *iter, void *user_data) { - struct adapter_data *data = connman_device_get_data(adapter); + struct bluetooth_pan *pan; + const char *old_role = NULL; + const char *new_role; - DBG("adapter %p", adapter); + if (strcmp(name, "UUIDs")) + return; + + pan = g_hash_table_lookup(networks, g_dbus_proxy_get_path(proxy)); + if (!pan) + return; - connman_device_set_data(adapter, NULL); + if (pan->network && + connman_network_get_device(pan->network)) + old_role = pan->pan_role; + new_role = proxy_get_role(pan->btdevice_proxy); - dbus_connection_unref(data->connection); + DBG("network %p network role %s proxy role %s", pan->network, old_role, + new_role); - g_free(data); + if (old_role && new_role && !strcmp(old_role, new_role)) + return; + + pan_create_nap(pan); } -static void powered_reply(DBusPendingCall *call, void *user_data) +static void pan_free(gpointer data) { - DBusMessage *reply; + struct bluetooth_pan *pan = data; - DBG(""); + if (pan->btnetwork_proxy) { + g_dbus_proxy_unref(pan->btnetwork_proxy); + pan->btnetwork_proxy = NULL; + } - reply = dbus_pending_call_steal_reply(call); + if (pan->btdevice_proxy) { + g_dbus_proxy_unref(pan->btdevice_proxy); + pan->btdevice_proxy = NULL; + } - dbus_message_unref(reply); + pan_remove_nap(pan); + + g_free(pan); } -static int change_powered(DBusConnection *connection, const char *path, - dbus_bool_t powered) +static void pan_create(GDBusProxy *network_proxy) { - DBusMessage *message; - DBusMessageIter iter; - DBusPendingCall *call; + const char *path = g_dbus_proxy_get_path(network_proxy); + struct bluetooth_pan *pan; - DBG(""); + pan = g_try_new0(struct bluetooth_pan, 1); - message = dbus_message_new_method_call(BLUEZ_SERVICE, path, - BLUEZ_ADAPTER_INTERFACE, SET_PROPERTY); - if (message == NULL) - return -ENOMEM; + if (!pan) { + connman_error("Out of memory creating PAN NAP"); + return; + } - dbus_message_iter_init_append(message, &iter); - connman_dbus_property_append_variant(&iter, "Powered", - DBUS_TYPE_BOOLEAN, &powered); + g_hash_table_replace(networks, g_strdup(path), pan); - if (dbus_connection_send_with_reply(connection, message, - &call, TIMEOUT) == FALSE) { - connman_error("Failed to change Powered property"); - dbus_message_unref(message); - return -EINVAL; - } + pan->btnetwork_proxy = g_dbus_proxy_ref(network_proxy); + pan->btdevice_proxy = g_dbus_proxy_new(client, path, + "org.bluez.Device1"); - if (call == NULL) { - connman_error("D-Bus connection not available"); - dbus_message_unref(message); - return -EINVAL; + if (!pan->btdevice_proxy) { + connman_error("Cannot create BT PAN watcher %s", path); + g_hash_table_remove(networks, path); + return; } - dbus_pending_call_set_notify(call, powered_reply, NULL, NULL); + g_dbus_proxy_set_property_watch(pan->btnetwork_proxy, + btnetwork_property_change, NULL); - dbus_message_unref(message); + g_dbus_proxy_set_property_watch(pan->btdevice_proxy, + btdevice_property_change, NULL); - return -EINPROGRESS; + DBG("pan %p %s role %s", pan, path, proxy_get_role(pan->btdevice_proxy)); + + pan_create_nap(pan); } -static int bluetooth_enable(struct connman_device *adapter) +static struct connman_network_driver network_driver = { + .name = "bluetooth", + .type = CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN, + .probe = bluetooth_pan_probe, + .remove = bluetooth_pan_remove, + .connect = bluetooth_pan_connect, + .disconnect = bluetooth_pan_disconnect, +}; + +static void enable_device(struct connman_device *device, const char *path) { - struct adapter_data *data = connman_device_get_data(adapter); - const char *path = connman_device_get_string(adapter, "Node"); + GHashTableIter iter; + gpointer key, value; - DBG("adapter %p", adapter); + DBG("device %p %s", device, path); + connman_device_set_powered(device, true); - return change_powered(data->connection, path, TRUE); -} + g_hash_table_iter_init(&iter, networks); + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct bluetooth_pan *pan = value; -static int bluetooth_disable(struct connman_device *adapter) -{ - struct adapter_data *data = connman_device_get_data(adapter); - const char *path = connman_device_get_string(adapter, "Node"); + if (g_strcmp0(proxy_get_string(pan->btdevice_proxy, "Adapter"), + path) == 0) { - DBG("adapter %p", adapter); - - return change_powered(data->connection, path, FALSE); + DBG("enable network %p", pan->network); + pan_create_nap(pan); + } + } } -static int bluetooth_scan(struct connman_device *adapter) +static void device_enable_cb(const DBusError *error, void *user_data) { - DBG("adapter %p", adapter); + char *path = user_data; + struct connman_device *device; - return -EIO; -} + device = g_hash_table_lookup(devices, path); + if (!device) { + DBG("device already removed"); + goto out; + } -static struct connman_device_driver bluetooth_driver = { - .name = "bluetooth", - .type = CONNMAN_DEVICE_TYPE_BLUETOOTH, - .probe = bluetooth_probe, - .remove = bluetooth_remove, - .enable = bluetooth_enable, - .disable = bluetooth_disable, - .scan = bluetooth_scan, -}; + if (dbus_error_is_set(error)) { + connman_warn("Bluetooth device %s not enabled %s", + path, error->message); + goto out; + } -static GSList *adapter_list = NULL; +#if !defined TIZEN_EXT + enable_device(device, path); +#endif +out: + g_free(path); +} -static void free_adapters(void) +static int bluetooth_device_enable(struct connman_device *device) { - GSList *list; + GDBusProxy *proxy = connman_device_get_data(device); + dbus_bool_t device_powered = TRUE; + const char *path; - DBG(""); + if (!proxy) + return 0; - for (list = adapter_list; list; list = list->next) { - struct connman_device *adapter = list->data; + path = g_dbus_proxy_get_path(proxy); - connman_device_unregister(adapter); - connman_device_unref(adapter); + if (proxy_get_bool(proxy, "Powered")) { + DBG("already enabled %p %s", device, path); + return -EALREADY; } - g_slist_free(adapter_list); - adapter_list = NULL; + DBG("device %p %s", device, path); + + g_dbus_proxy_set_property_basic(proxy, "Powered", + DBUS_TYPE_BOOLEAN, &device_powered, + device_enable_cb, g_strdup(path), NULL); + + return -EINPROGRESS; } -static struct connman_device *find_adapter(const char *path) +static void disable_device(struct connman_device *device, const char *path) { - GSList *list; + GHashTableIter iter; + gpointer key, value; + + DBG("device %p %s", device, path); + connman_device_set_powered(device, false); + + g_hash_table_iter_init(&iter, networks); + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct bluetooth_pan *pan = value; - DBG("path %s", path); + if (pan->network && connman_network_get_device(pan->network) + == device) { + DBG("disable network %p", pan->network); + connman_device_remove_network(device, pan->network); + } + } +} - for (list = adapter_list; list; list = list->next) { - struct connman_device *adapter = list->data; - const char *adapter_path = connman_device_get_string(adapter, - "Node"); +static void device_disable_cb(const DBusError *error, void *user_data) +{ + char *path = user_data; + struct connman_device *device; - if (adapter_path == NULL) - continue; + device = g_hash_table_lookup(devices, path); + if (!device) { + DBG("device already removed"); + goto out; + } - if (g_str_equal(adapter_path, path) == TRUE) - return adapter; + if (dbus_error_is_set(error)) { + connman_warn("Bluetooth device %s not disabled: %s", + path, error->message); + goto out; } - return NULL; +#if !defined TIZEN_EXT + disable_device(device, path); +#endif + +out: + g_free(path); } -static void device_properties(DBusConnection *connection, const char *path, - DBusMessage *message, void *user_data) +static int bluetooth_device_disable(struct connman_device *device) { - struct connman_device *device = user_data; - const char *node = g_basename(path); - struct connman_network *network; + GDBusProxy *proxy = connman_device_get_data(device); + dbus_bool_t device_powered = FALSE; + const char *path; - DBG("path %s", path); + if (!proxy) + return 0; - network = connman_device_get_network(device, node); - if (network != NULL) - return; + path = g_dbus_proxy_get_path(proxy); - network = connman_network_create(node, - CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN); - if (network == NULL) - return; + if (!proxy_get_bool(proxy, "Powered")) { + DBG("already disabled %p %s", device, path); + return -EALREADY; + } - connman_network_set_protocol(network, CONNMAN_NETWORK_PROTOCOL_IP); + DBG("device %p %s", device, path); - connman_network_set_string(network, "Node", path); + g_dbus_proxy_set_property_basic(proxy, "Powered", + DBUS_TYPE_BOOLEAN, &device_powered, + device_disable_cb, g_strdup(path), NULL); - connman_device_add_network(device, network); + return -EINPROGRESS; } -static void check_devices(struct connman_device *adapter, - DBusConnection *connection, DBusMessageIter *array) +static void adapter_property_change(GDBusProxy *proxy, const char *name, + DBusMessageIter *iter, void *user_data) { - DBusMessageIter value; + struct connman_device *device; + const char *path; + bool adapter_powered, device_powered; - if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY) + if (strcmp(name, "Powered") != 0) return; - dbus_message_iter_recurse(array, &value); + path = g_dbus_proxy_get_path(proxy); + device = g_hash_table_lookup(devices, path); - while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_OBJECT_PATH) { - const char *path; + adapter_powered = proxy_get_bool(proxy, "Powered"); + device_powered = connman_device_get_powered(device); - dbus_message_iter_get_basic(&value, &path); + DBG("device %p %s device powered %d adapter powered %d", device, path, + device_powered, adapter_powered); - get_properties(connection, path, BLUEZ_DEVICE_INTERFACE, - device_properties, adapter); - - dbus_message_iter_next(&value); + if (device_powered != adapter_powered) { + if (adapter_powered) + enable_device(device, path); + else + disable_device(device, path); } } -static void property_changed(DBusConnection *connection, DBusMessage *message) +static void device_free(gpointer data) { - const char *path = dbus_message_get_path(message); - struct connman_device *adapter; - DBusMessageIter iter, value; - const char *key; + struct connman_device *device = data; + GDBusProxy *proxy = connman_device_get_data(device); - DBG("path %s", path); + connman_device_set_data(device, NULL); + if (proxy) + g_dbus_proxy_unref(proxy); - adapter = find_adapter(path); - if (adapter == NULL) - return; + connman_device_unregister(device); + connman_device_unref(device); +} - if (dbus_message_iter_init(message, &iter) == FALSE) - return; +struct tethering_info { + struct connman_technology *technology; + char *bridge; + bool enable; +}; - dbus_message_iter_get_basic(&iter, &key); +static void tethering_free(void *user_data) +{ + struct tethering_info *tethering = user_data; - dbus_message_iter_next(&iter); - dbus_message_iter_recurse(&iter, &value); + g_free(tethering->bridge); + g_free(tethering); +} - if (g_str_equal(key, "Powered") == TRUE) { - gboolean val; +static void tethering_create_cb(DBusMessage *message, void *user_data) +{ + struct tethering_info *tethering = user_data; - dbus_message_iter_get_basic(&value, &val); - connman_device_set_powered(adapter, val); - } else if (g_str_equal(key, "Discovering") == TRUE) { - gboolean val; + if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) { + const char *dbus_error = dbus_message_get_error_name(message); - dbus_message_iter_get_basic(&value, &val); - connman_device_set_scanning(adapter, val); + DBG("%s tethering failed: %s", + tethering->enable ? "enable" : "disable", + dbus_error); + return; } + + DBG("bridge %s %s", tethering->bridge, tethering->enable ? + "enabled": "disabled"); + + if (tethering->technology) + connman_technology_tethering_notify(tethering->technology, + tethering->enable); } -static void parse_adapter_properties(struct connman_device *adapter, - DBusConnection *connection, - DBusMessage *reply) +static void tethering_append(DBusMessageIter *iter, void *user_data) { - DBusMessageIter array, dict; + struct tethering_info *tethering = user_data; + const char *nap = "nap"; - if (dbus_message_iter_init(reply, &array) == FALSE) - return; + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &nap); + if (tethering->enable) + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, + &tethering->bridge); +} - if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) - return; +static bool tethering_create(const char *path, + struct connman_technology *technology, const char *bridge, + bool enabled) +{ + struct tethering_info *tethering = g_new0(struct tethering_info, 1); + GDBusProxy *proxy; + const char *method; + bool result; - dbus_message_iter_recurse(&array, &dict); + DBG("path %s bridge %s", path, bridge); - while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { - DBusMessageIter entry, value; - const char *key; + if (!bridge) { + g_free(tethering); + return false; + } - dbus_message_iter_recurse(&dict, &entry); - dbus_message_iter_get_basic(&entry, &key); + proxy = g_dbus_proxy_new(client, path, "org.bluez.NetworkServer1"); + if (!proxy) { + g_free(tethering); + return false; + } - dbus_message_iter_next(&entry); - dbus_message_iter_recurse(&entry, &value); + tethering->technology = technology; + tethering->bridge = g_strdup(bridge); + tethering->enable = enabled; - if (g_str_equal(key, "Powered") == TRUE) { - gboolean val; + if (tethering->enable) + method = "Register"; + else + method = "Unregister"; - dbus_message_iter_get_basic(&value, &val); - connman_device_set_powered(adapter, val); - } else if (g_str_equal(key, "Discovering") == TRUE) { - gboolean val; + result = g_dbus_proxy_method_call(proxy, method, tethering_append, + tethering_create_cb, tethering, tethering_free); - dbus_message_iter_get_basic(&value, &val); - connman_device_set_scanning(adapter, val); - } else if (g_str_equal(key, "Devices") == TRUE) { - check_devices(adapter, connection, &value); - } + g_dbus_proxy_unref(proxy); - dbus_message_iter_next(&dict); - } + return result; } -static void adapter_properties(DBusConnection *connection, const char *path, - DBusMessage *message, void *user_data) +static void device_create(GDBusProxy *proxy) { - const char *node = g_basename(path); - struct connman_device *adapter; - - DBG("path %s", path); + struct connman_device *device = NULL; + const char *path = g_dbus_proxy_get_path(proxy); + const char *address; + char ident[BLUETOOTH_ADDR_LEN * 2 + 1]; + bool powered; + + address = proxy_get_string(proxy, "Address"); + if (!address) + return; - adapter = find_adapter(path); - if (adapter != NULL) - goto done; + address2ident(address, ident); - adapter = connman_device_create(node, CONNMAN_DEVICE_TYPE_BLUETOOTH); - if (adapter == NULL) + device = connman_device_create("bluetooth", + CONNMAN_DEVICE_TYPE_BLUETOOTH); + if (!device) return; - connman_device_set_string(adapter, "Node", path); + connman_device_set_data(device, g_dbus_proxy_ref(proxy)); + connman_device_set_ident(device, ident); - if (node != NULL && g_str_has_prefix(node, "hci") == TRUE) { - int index; - errno = 0; - index = atoi(node + 3); - if (errno == 0) - connman_device_set_index(adapter, index); - } - - connman_device_set_interface(adapter, node); + g_hash_table_replace(devices, g_strdup(path), device); - connman_device_set_mode(adapter, CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE); + DBG("device %p %s device powered %d adapter powered %d", device, + path, connman_device_get_powered(device), + proxy_get_bool(proxy, "Powered")); - if (connman_device_register(adapter) < 0) { - connman_device_unref(adapter); + if (connman_device_register(device) < 0) { + g_hash_table_remove(devices, device); return; } - adapter_list = g_slist_append(adapter_list, adapter); + g_dbus_proxy_set_property_watch(proxy, adapter_property_change, NULL); -done: - parse_adapter_properties(adapter, connection, message); -} + powered = proxy_get_bool(proxy, "Powered"); + connman_device_set_powered(device, powered); -static void add_adapter(DBusConnection *connection, const char *path) -{ - DBG("path %s", path); - - get_properties(connection, path, BLUEZ_ADAPTER_INTERFACE, - adapter_properties, NULL); + if (proxy_get_role(proxy) && !bluetooth_tethering) + tethering_create(path, NULL, NULL, false); } -static void remove_adapter(DBusConnection *connection, const char *path) +static void object_added(GDBusProxy *proxy, void *user_data) { - struct connman_device *adapter; + const char *interface; - DBG("path %s", path); - - adapter = find_adapter(path); - if (adapter == NULL) + interface = g_dbus_proxy_get_interface(proxy); + if (!interface) { + connman_warn("Interface or proxy missing when adding " + "bluetooth object"); return; + } - adapter_list = g_slist_remove(adapter_list, adapter); + if (strcmp(interface, "org.bluez.Adapter1") == 0) { + DBG("%s %s", interface, g_dbus_proxy_get_path(proxy)); + device_create(proxy); + return; + } - connman_device_unregister(adapter); - connman_device_unref(adapter); + if (strcmp(interface, "org.bluez.Network1") == 0) { + DBG("%s %s", interface, g_dbus_proxy_get_path(proxy)); + pan_create(proxy); + return; + } } -static void list_adapters_reply(DBusPendingCall *call, void *user_data) +static void object_removed(GDBusProxy *proxy, void *user_data) { - DBusConnection *connection = user_data; - DBusMessage *reply; - DBusError error; - char **adapters; - int i, num_adapters; - - DBG(""); + const char *interface, *path; - reply = dbus_pending_call_steal_reply(call); + interface = g_dbus_proxy_get_interface(proxy); + if (!interface) { + connman_warn("Interface or proxy missing when removing " + "bluetooth object"); + return; + } - dbus_error_init(&error); + if (strcmp(interface, "org.bluez.Adapter1") == 0) { + path = g_dbus_proxy_get_path(proxy); + DBG("%s %s", interface, path); - if (dbus_message_get_args(reply, &error, - DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, - &adapters, &num_adapters, - DBUS_TYPE_INVALID) == FALSE) { - if (dbus_error_is_set(&error) == TRUE) { - connman_error("%s", error.message); - dbus_error_free(&error); - } else - connman_error("Wrong arguments for adapter list"); - goto done; + g_hash_table_remove(devices, path); } - for (i = 0; i < num_adapters; i++) - get_properties(connection, adapters[i], - BLUEZ_ADAPTER_INTERFACE, - adapter_properties, NULL); + if (strcmp(interface, "org.bluez.Network1") == 0) { + path = g_dbus_proxy_get_path(proxy); + DBG("%s %s", interface, path); - g_strfreev(adapters); + g_hash_table_remove(networks, path); + } -done: - dbus_message_unref(reply); } -static void bluetooth_connect(DBusConnection *connection, void *user_data) +static int bluetooth_device_probe(struct connman_device *device) { - DBusMessage *message; - DBusPendingCall *call; + GHashTableIter iter; + gpointer key, value; - DBG("connection %p", connection); + g_hash_table_iter_init(&iter, devices); - message = dbus_message_new_method_call(BLUEZ_SERVICE, "/", - BLUEZ_MANAGER_INTERFACE, LIST_ADAPTERS); - if (message == NULL) - return; + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct connman_device *known = value; - if (dbus_connection_send_with_reply(connection, message, - &call, TIMEOUT) == FALSE) { - connman_error("Failed to get Bluetooth adapters"); - goto done; + if (device == known) + return 0; } - if (call == NULL) { - connman_error("D-Bus connection not available"); - goto done; - } + return -EOPNOTSUPP; +} + +static void bluetooth_device_remove(struct connman_device *device) +{ + DBG("%p", device); +} - dbus_pending_call_set_notify(call, list_adapters_reply, - connection, NULL); +static struct connman_device_driver device_driver = { + .name = "bluetooth", + .type = CONNMAN_DEVICE_TYPE_BLUETOOTH, + .probe = bluetooth_device_probe, + .remove = bluetooth_device_remove, + .enable = bluetooth_device_enable, + .disable = bluetooth_device_disable, +}; -done: - dbus_message_unref(message); +static int bluetooth_tech_probe(struct connman_technology *technology) +{ + return 0; } -static void bluetooth_disconnect(DBusConnection *connection, void *user_data) +static void bluetooth_tech_remove(struct connman_technology *technology) { - DBG("connection %p", connection); - free_adapters(); } -static DBusHandlerResult bluetooth_signal(DBusConnection *connection, - DBusMessage *message, void *user_data) +static int bluetooth_tech_set_tethering(struct connman_technology *technology, + const char *identifier, const char *passphrase, + const char *bridge, bool enabled) { - if (dbus_message_has_interface(message, - BLUEZ_MANAGER_INTERFACE) == FALSE && - dbus_message_has_interface(message, - BLUEZ_ADAPTER_INTERFACE) == FALSE) - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + GHashTableIter hash_iter; + gpointer key, value; + int i = 0; + + bluetooth_tethering = enabled; + + g_hash_table_iter_init(&hash_iter, devices); - DBG("connection %p", connection); + while (g_hash_table_iter_next(&hash_iter, &key, &value)) { + const char *path = key; + struct connman_device *device = value; - if (dbus_message_is_signal(message, BLUEZ_ADAPTER_INTERFACE, - PROPERTY_CHANGED) == TRUE) { - property_changed(connection, message); - } else if (dbus_message_is_signal(message, BLUEZ_MANAGER_INTERFACE, - ADAPTER_ADDED) == TRUE) { - const char *path; - dbus_message_get_args(message, NULL, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID); - add_adapter(connection, path); - } else if (dbus_message_is_signal(message, BLUEZ_MANAGER_INTERFACE, - ADAPTER_REMOVED) == TRUE) { - const char *path; - dbus_message_get_args(message, NULL, - DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID); - remove_adapter(connection, path); + DBG("device %p", device); + + if (tethering_create(path, technology, bridge, enabled) + ) + i++; } - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; -} + DBG("%s %d device(s)", enabled ? "enabled" : "disabled", i); -static DBusConnection *connection; -static guint watch; + if (i == 0) + return -ENODEV; -static const char *added_rule = "type=signal,member=" ADAPTER_ADDED - ",interface=" BLUEZ_MANAGER_INTERFACE; -static const char *removed_rule = "type=signal,member=" ADAPTER_REMOVED - ",interface=" BLUEZ_MANAGER_INTERFACE; + return 0; +} -static const char *adapter_rule = "type=signal,member=" PROPERTY_CHANGED - ",interface=" BLUEZ_ADAPTER_INTERFACE; +static struct connman_technology_driver tech_driver = { + .name = "bluetooth", + .type = CONNMAN_SERVICE_TYPE_BLUETOOTH, + .probe = bluetooth_tech_probe, + .remove = bluetooth_tech_remove, + .set_tethering = bluetooth_tech_set_tethering, +}; static int bluetooth_init(void) { - int err = -EIO; - connection = connman_dbus_get_connection(); - if (connection == NULL) - return -EIO; + if (!connection) + goto out; - if (dbus_connection_add_filter(connection, bluetooth_signal, - NULL, NULL) == FALSE) - goto unref; + if (connman_technology_driver_register(&tech_driver) < 0) { + connman_warn("Failed to initialize technology for Bluez 5"); + goto out; + } - err = connman_network_driver_register(&pan_driver); - if (err < 0) - goto remove; + devices = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + device_free); - err = connman_device_driver_register(&bluetooth_driver); - if (err < 0) { - connman_network_driver_unregister(&pan_driver); - goto remove; + if (connman_device_driver_register(&device_driver) < 0) { + connman_warn("Failed to initialize device driver for " + BLUEZ_SERVICE); + connman_technology_driver_unregister(&tech_driver); + goto out; } - watch = g_dbus_add_service_watch(connection, BLUEZ_SERVICE, - bluetooth_connect, bluetooth_disconnect, NULL, NULL); - if (watch == 0) { - connman_device_driver_unregister(&bluetooth_driver); - connman_network_driver_unregister(&pan_driver); - err = -EIO; - goto remove; + if (connman_network_driver_register(&network_driver) < 0) { + connman_technology_driver_unregister(&tech_driver); + connman_device_driver_unregister(&device_driver); + goto out; } - if (g_dbus_check_service(connection, BLUEZ_SERVICE) == TRUE) - bluetooth_connect(connection, NULL); + networks = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + pan_free); - dbus_bus_add_match(connection, added_rule, NULL); - dbus_bus_add_match(connection, removed_rule, NULL); - dbus_bus_add_match(connection, adapter_rule, NULL); - dbus_connection_flush(connection); + client = g_dbus_client_new(connection, BLUEZ_SERVICE, BLUEZ_PATH); + if (!client) { + connman_warn("Failed to initialize D-Bus client for " + BLUEZ_SERVICE); + goto out; + } + + g_dbus_client_set_proxy_handlers(client, object_added, object_removed, + NULL, NULL); return 0; -remove: - dbus_connection_remove_filter(connection, bluetooth_signal, NULL); +out: + if (networks) + g_hash_table_destroy(networks); -unref: - dbus_connection_unref(connection); + if (devices) + g_hash_table_destroy(devices); + + if (client) + g_dbus_client_unref(client); - return err; + if (connection) + dbus_connection_unref(connection); + + return -EIO; } static void bluetooth_exit(void) { - dbus_bus_remove_match(connection, adapter_rule, NULL); - dbus_bus_remove_match(connection, removed_rule, NULL); - dbus_bus_remove_match(connection, added_rule, NULL); - dbus_connection_flush(connection); - - g_dbus_remove_watch(connection, watch); + /* + * We unset the disabling of the Bluetooth device when shutting down + * so that non-PAN BT connections are not affected. + */ + device_driver.disable = NULL; - free_adapters(); + g_dbus_client_unref(client); - connman_device_driver_unregister(&bluetooth_driver); - connman_network_driver_unregister(&pan_driver); + connman_network_driver_unregister(&network_driver); + g_hash_table_destroy(networks); - dbus_connection_remove_filter(connection, bluetooth_signal, NULL); + connman_device_driver_unregister(&device_driver); + g_hash_table_destroy(devices); + connman_technology_driver_unregister(&tech_driver); dbus_connection_unref(connection); } CONNMAN_PLUGIN_DEFINE(bluetooth, "Bluetooth technology plugin", VERSION, - CONNMAN_PLUGIN_PRIORITY_DEFAULT, bluetooth_init, bluetooth_exit) + CONNMAN_PLUGIN_PRIORITY_DEFAULT, bluetooth_init, bluetooth_exit)