X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=plugins%2Fbluetooth.c;h=704d21644a1ecba0cca47b40910c765262a8736e;hb=a424f79696a666ac27812d23af170552e760309d;hp=9bb29d469d4dd612d3f51108255eb31935d7c422;hpb=3740318614518a1af92f22b297eb4603c6ca20e8;p=platform%2Fupstream%2Fconnman.git diff --git a/plugins/bluetooth.c b/plugins/bluetooth.c old mode 100644 new mode 100755 index 9bb29d4..704d216 --- a/plugins/bluetooth.c +++ b/plugins/bluetooth.c @@ -2,7 +2,7 @@ * * Connection Manager * - * Copyright (C) 2007-2008 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,501 +24,1000 @@ #endif #include +#include -#include - +#define CONNMAN_API_SUBJECT_TO_CHANGE #include -#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_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 LIST_ADAPTERS "ListAdapters" -#define ADAPTER_ADDED "AdapterAdded" -#define ADAPTER_REMOVED "AdapterRemoved" +#define BLUETOOTH_ADDR_LEN 6 -#define PROPERTY_CHANGED "PropertyChanged" -#define GET_PROPERTIES "GetProperties" -#define SET_PROPERTY "SetProperty" +static DBusConnection *connection; +static GDBusClient *client; +static GHashTable *devices; +static GHashTable *networks; +static bool bluetooth_tethering; + +struct bluetooth_pan { + struct connman_network *network; + GDBusProxy *btdevice_proxy; + GDBusProxy *btnetwork_proxy; + const char *pan_role; +}; -#define TIMEOUT 5000 +static void address2ident(const char *address, char *ident) +{ + int i; -struct adapter_data { - DBusConnection *connection; -}; + 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'; +} -static int bluetooth_probe(struct connman_element *adapter) +static const char *proxy_get_string(GDBusProxy *proxy, const char *property) { - struct adapter_data *data; + DBusMessageIter iter; + const char *str; - DBG("adapter %p name %s", adapter, adapter->name); + if (!g_dbus_proxy_get_property(proxy, property, &iter)) + return NULL; + dbus_message_iter_get_basic(&iter, &str); + return str; +} - data = g_try_new0(struct adapter_data, 1); - if (data == NULL) - return -ENOMEM; +static bool proxy_get_bool(GDBusProxy *proxy, const char *property) +{ + DBusMessageIter iter; + dbus_bool_t value; - data->connection = connman_dbus_get_connection(); - if (data->connection == NULL) { - g_free(data); - return -EIO; - } + if (!g_dbus_proxy_get_property(proxy, property, &iter)) + return false; + dbus_message_iter_get_basic(&iter, &value); + return value; +} + +static const char *proxy_get_role(GDBusProxy *proxy) +{ + 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; +} + +static int bluetooth_pan_probe(struct connman_network *network) +{ + GHashTableIter iter; + gpointer key, value; - connman_element_set_data(adapter, data); + DBG("network %p", network); - return 0; + g_hash_table_iter_init(&iter, networks); + + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct bluetooth_pan *pan = value; + + if (network == pan->network) + return 0; + } + + return -EOPNOTSUPP; } -static void bluetooth_remove(struct connman_element *adapter) +static void pan_remove_nap(struct bluetooth_pan *pan) { - struct adapter_data *data = connman_element_get_data(adapter); + struct connman_device *device; + struct connman_network *network = pan->network; + + DBG("network %p pan %p", pan->network, pan); + + if (!network) + return; - DBG("adapter %p name %s", adapter, adapter->name); + pan->network = NULL; + connman_network_set_data(network, NULL); - connman_element_set_data(adapter, NULL); + device = connman_network_get_device(network); + if (device) + connman_device_remove_network(device, network); - dbus_connection_unref(data->connection); + connman_network_unref(network); +} + +static void bluetooth_pan_remove(struct connman_network *network) +{ + struct bluetooth_pan *pan = connman_network_get_data(network); + + DBG("network %p pan %p", network, pan); - g_free(data); + connman_network_set_data(network, NULL); + + if (pan) + pan_remove_nap(pan); } -static void powered_reply(DBusPendingCall *call, void *user_data) +static bool pan_connect(struct bluetooth_pan *pan, + const char *iface) { - DBusMessage *reply; + int index; + + if (!iface) { + if (!proxy_get_bool(pan->btnetwork_proxy, "Connected")) + return false; + iface = proxy_get_string(pan->btnetwork_proxy, "Interface"); + } - DBG(""); + if (!iface) + return false; - reply = dbus_pending_call_steal_reply(call); + index = connman_inet_ifindex(iface); + if (index < 0) { + DBG("network %p invalid index %d", pan->network, index); + return false; + } - dbus_message_unref(reply); +#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 int change_powered(DBusConnection *connection, const char *path, - dbus_bool_t powered) +static void pan_connect_cb(DBusMessage *message, void *user_data) { - DBusMessage *message; + const char *path = user_data; + const char *iface = NULL; + struct bluetooth_pan *pan; DBusMessageIter iter; - DBusPendingCall *call; - DBG(""); + pan = g_hash_table_lookup(networks, path); + if (!pan || !pan->network) { + DBG("network already removed"); + return; + } - message = dbus_message_new_method_call(BLUEZ_SERVICE, path, - BLUEZ_ADAPTER_INTERFACE, SET_PROPERTY); - if (message == NULL) - return -ENOMEM; + if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) { + const char *dbus_error = dbus_message_get_error_name(message); - dbus_message_iter_init_append(message, &iter); - connman_dbus_property_append_variant(&iter, "Powered", - DBUS_TYPE_BOOLEAN, &powered); + DBG("network %p %s", pan->network, dbus_error); - if (dbus_connection_send_with_reply(connection, message, - &call, TIMEOUT) == FALSE) { - connman_error("Failed to change Powered property"); - dbus_message_unref(message); - return -EINVAL; + 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); } - dbus_pending_call_set_notify(call, powered_reply, NULL, NULL); + DBG("network %p interface %s", pan->network, iface); + + pan_connect(pan, iface); +} + +static void pan_connect_append(DBusMessageIter *iter, + void *user_data) +{ + const char *path = user_data; + struct bluetooth_pan *pan; + + pan = g_hash_table_lookup(networks, path); + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &pan->pan_role); +} - dbus_message_unref(message); +static int bluetooth_pan_connect(struct connman_network *network) +{ + struct bluetooth_pan *pan = connman_network_get_data(network); + const char *path; + + DBG("network %p", network); + + if (!pan) + return -EINVAL; + + path = g_dbus_proxy_get_path(pan->btnetwork_proxy); + + if (!g_dbus_proxy_method_call(pan->btnetwork_proxy, "Connect", + pan_connect_append, pan_connect_cb, + g_strdup(path), g_free)) + return -EIO; + +#if defined TIZEN_EXT + if (pan->network) +#endif + connman_network_set_associating(pan->network, true); return -EINPROGRESS; } -static int bluetooth_enable(struct connman_element *adapter) +static void pan_disconnect_cb(DBusMessage *message, void *user_data) { - struct adapter_data *data = connman_element_get_data(adapter); + const char *path = user_data; + struct bluetooth_pan *pan; + + pan = g_hash_table_lookup(networks, path); + if (!pan || !pan->network) { + DBG("network already removed"); + return; + } - DBG("adapter %p name %s", adapter, adapter->name); + if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) { + const char *dbus_error = dbus_message_get_error_name(message); - return change_powered(data->connection, adapter->devpath, TRUE); + DBG("network %p %s", pan->network, dbus_error); + } + + DBG("network %p", pan->network); + +#if defined TIZEN_EXT + if (pan->network) +#endif + connman_network_set_connected(pan->network, false); } -static int bluetooth_disable(struct connman_element *adapter) +static int bluetooth_pan_disconnect(struct connman_network *network) { - struct adapter_data *data = connman_element_get_data(adapter); + struct bluetooth_pan *pan = connman_network_get_data(network); + const char *path; - DBG("adapter %p name %s", adapter, adapter->name); + DBG("network %p", network); + + if (!pan) + return -EINVAL; + +#if defined TIZEN_EXT + if (connman_network_get_associating(network) == TRUE) + connman_network_clear_associating(network); +#endif + + path = g_dbus_proxy_get_path(pan->btnetwork_proxy); + + if (!g_dbus_proxy_method_call(pan->btnetwork_proxy, "Disconnect", + NULL, pan_disconnect_cb, g_strdup(path), g_free)) + return -EIO; - return change_powered(data->connection, adapter->devpath, FALSE); + return -EINPROGRESS; } -static struct connman_driver bluetooth_driver = { - .name = "bluetooth", - .type = CONNMAN_ELEMENT_TYPE_DEVICE, - .subtype = CONNMAN_ELEMENT_SUBTYPE_BLUETOOTH, - .probe = bluetooth_probe, - .remove = bluetooth_remove, - .enable = bluetooth_enable, - .disable = bluetooth_disable, -}; +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; + + if (strcmp(name, "Connected") != 0) + return; + + pan = g_hash_table_lookup(networks, g_dbus_proxy_get_path(proxy)); + if (!pan || !pan->network) + return; + + dbus_message_iter_get_basic(iter, &connected); + proxy_connected = connected; -static GSList *device_list = NULL; + network_connected = connman_network_get_connected(pan->network); -static struct connman_element *find_adapter(const char *path) + 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) { - GSList *list; + struct connman_device *device; + const char* role; + const char *adapter; - DBG("path %s", path); + role = proxy_get_role(pan->btdevice_proxy); + if (!role) { + pan_remove_nap(pan); + return; + } + + adapter = proxy_get_string(pan->btdevice_proxy, "Adapter"); - for (list = device_list; list; list = list->next) { - struct connman_element *device = list->data; + if (!adapter) + return; - if (g_str_equal(device->devpath, path) == TRUE) - return device; + 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); } - return NULL; + pan->pan_role = role; + connman_device_add_network(device, pan->network); + + if (pan_connect(pan, NULL)) + DBG("network %p already connected", pan->network); } -static void check_devices(struct connman_element *adapter, - DBusMessageIter *array) +static void btdevice_property_change(GDBusProxy *proxy, const char *name, + DBusMessageIter *iter, void *user_data) { - DBusMessageIter value; + struct bluetooth_pan *pan; + const char *old_role = NULL; + const char *new_role; - DBG("adapter %p name %s", adapter, adapter->name); + if (strcmp(name, "UUIDs")) + return; - if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY) + pan = g_hash_table_lookup(networks, g_dbus_proxy_get_path(proxy)); + if (!pan) return; - dbus_message_iter_recurse(array, &value); + if (pan->network && + connman_network_get_device(pan->network)) + old_role = pan->pan_role; + new_role = proxy_get_role(pan->btdevice_proxy); - while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_OBJECT_PATH) { - const char *path; + DBG("network %p network role %s proxy role %s", pan->network, old_role, + new_role); - dbus_message_iter_get_basic(&value, &path); + if (old_role && new_role && !strcmp(old_role, new_role)) + return; - DBG("device %s", path); + pan_create_nap(pan); +} + +static void pan_free(gpointer data) +{ + struct bluetooth_pan *pan = data; - dbus_message_iter_next(&value); + if (pan->btnetwork_proxy) { + g_dbus_proxy_unref(pan->btnetwork_proxy); + pan->btnetwork_proxy = NULL; } + + if (pan->btdevice_proxy) { + g_dbus_proxy_unref(pan->btdevice_proxy); + pan->btdevice_proxy = NULL; + } + + pan_remove_nap(pan); + + g_free(pan); } -static void property_changed(DBusConnection *connection, DBusMessage *message) +static void pan_create(GDBusProxy *network_proxy) { - const char *path = dbus_message_get_path(message); - struct connman_element *device; - DBusMessageIter iter, value; - const char *key; + const char *path = g_dbus_proxy_get_path(network_proxy); + struct bluetooth_pan *pan; - DBG("path %s", path); + pan = g_try_new0(struct bluetooth_pan, 1); - device = find_adapter(path); - if (device == NULL) + if (!pan) { + connman_error("Out of memory creating PAN NAP"); return; + } + + g_hash_table_replace(networks, g_strdup(path), pan); + + pan->btnetwork_proxy = g_dbus_proxy_ref(network_proxy); + pan->btdevice_proxy = g_dbus_proxy_new(client, path, + "org.bluez.Device1"); - if (dbus_message_iter_init(message, &iter) == FALSE) + if (!pan->btdevice_proxy) { + connman_error("Cannot create BT PAN watcher %s", path); + g_hash_table_remove(networks, path); return; + } - dbus_message_iter_get_basic(&iter, &key); + g_dbus_proxy_set_property_watch(pan->btnetwork_proxy, + btnetwork_property_change, NULL); - dbus_message_iter_next(&iter); - dbus_message_iter_recurse(&iter, &value); + g_dbus_proxy_set_property_watch(pan->btdevice_proxy, + btdevice_property_change, NULL); - if (g_str_equal(key, "Powered") == TRUE) { - gboolean val; + DBG("pan %p %s role %s", pan, path, proxy_get_role(pan->btdevice_proxy)); - dbus_message_iter_get_basic(&value, &val); - connman_element_set_enabled(device, val); - } else if (g_str_equal(key, "Discovering") == TRUE) { - gboolean val; + pan_create_nap(pan); +} - dbus_message_iter_get_basic(&value, &val); - connman_element_set_scanning(device, val); +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) +{ + GHashTableIter iter; + gpointer key, value; + + DBG("device %p %s", device, path); + connman_device_set_powered(device, true); + + g_hash_table_iter_init(&iter, networks); + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct bluetooth_pan *pan = value; + + if (g_strcmp0(proxy_get_string(pan->btdevice_proxy, "Adapter"), + path) == 0) { + + DBG("enable network %p", pan->network); + pan_create_nap(pan); + } } } -static void properties_reply(DBusPendingCall *call, void *user_data) +static void device_enable_cb(const DBusError *error, void *user_data) { - DBusMessage *message = user_data; - const char *path = dbus_message_get_path(message); - struct connman_element *device; - DBusMessageIter array, dict; - DBusMessage *reply; + char *path = user_data; + struct connman_device *device; - DBG("path %s", path); + device = g_hash_table_lookup(devices, path); + if (!device) { + DBG("device already removed"); + goto out; + } - device = find_adapter(path); + if (dbus_error_is_set(error)) { + connman_warn("Bluetooth device %s not enabled %s", + path, error->message); + goto out; + } - dbus_message_unref(message); +#if !defined TIZEN_EXT + enable_device(device, path); +#endif +out: + g_free(path); +} - reply = dbus_pending_call_steal_reply(call); +static int bluetooth_device_enable(struct connman_device *device) +{ + GDBusProxy *proxy = connman_device_get_data(device); + dbus_bool_t device_powered = TRUE; + const char *path; + + if (!proxy) + return 0; - if (device == NULL) - goto done; + path = g_dbus_proxy_get_path(proxy); - if (dbus_message_iter_init(reply, &array) == FALSE) - goto done; + if (proxy_get_bool(proxy, "Powered")) { + DBG("already enabled %p %s", device, path); + return -EALREADY; + } - if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY) - goto done; + DBG("device %p %s", device, path); - dbus_message_iter_recurse(&array, &dict); - while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { - DBusMessageIter entry, value; - const char *key; + g_dbus_proxy_set_property_basic(proxy, "Powered", + DBUS_TYPE_BOOLEAN, &device_powered, + device_enable_cb, g_strdup(path), NULL); - dbus_message_iter_recurse(&dict, &entry); - dbus_message_iter_get_basic(&entry, &key); + return -EINPROGRESS; +} - dbus_message_iter_next(&entry); - dbus_message_iter_recurse(&entry, &value); +static void disable_device(struct connman_device *device, const char *path) +{ + GHashTableIter iter; + gpointer key, value; - if (g_str_equal(key, "Powered") == TRUE) { - gboolean val; + DBG("device %p %s", device, path); + connman_device_set_powered(device, false); - dbus_message_iter_get_basic(&value, &val); - connman_element_set_enabled(device, val); - } else if (g_str_equal(key, "Discovering") == TRUE) { - gboolean val; + g_hash_table_iter_init(&iter, networks); + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct bluetooth_pan *pan = value; - dbus_message_iter_get_basic(&value, &val); - connman_element_set_scanning(device, val); - } else if (g_str_equal(key, "Devices") == TRUE) { - check_devices(device, &value); + if (pan->network && connman_network_get_device(pan->network) + == device) { + DBG("disable network %p", pan->network); + connman_device_remove_network(device, pan->network); } + } +} + +static void device_disable_cb(const DBusError *error, void *user_data) +{ + char *path = user_data; + struct connman_device *device; - dbus_message_iter_next(&dict); + device = g_hash_table_lookup(devices, path); + if (!device) { + DBG("device already removed"); + goto out; } -done: - dbus_message_unref(reply); + if (dbus_error_is_set(error)) { + connman_warn("Bluetooth device %s not disabled: %s", + path, error->message); + goto out; + } + +#if !defined TIZEN_EXT + disable_device(device, path); +#endif + +out: + g_free(path); } -static void add_adapter(DBusConnection *connection, const char *path) +static int bluetooth_device_disable(struct connman_device *device) { - struct connman_element *device; - DBusMessage *message; - DBusPendingCall *call; + GDBusProxy *proxy = connman_device_get_data(device); + dbus_bool_t device_powered = FALSE; + const char *path; - DBG("path %s", path); + if (!proxy) + return 0; - device = find_adapter(path); - if (device != NULL) - return; + path = g_dbus_proxy_get_path(proxy); - device = connman_element_create(NULL); - device->type = CONNMAN_ELEMENT_TYPE_DEVICE; - device->subtype = CONNMAN_ELEMENT_SUBTYPE_BLUETOOTH; - device->policy = CONNMAN_ELEMENT_POLICY_IGNORE; + if (!proxy_get_bool(proxy, "Powered")) { + DBG("already disabled %p %s", device, path); + return -EALREADY; + } - device->name = g_path_get_basename(path); - device->devpath = g_strdup(path); + DBG("device %p %s", device, path); - if (connman_element_register(device, NULL) < 0) { - connman_element_unref(device); - return; - } + g_dbus_proxy_set_property_basic(proxy, "Powered", + DBUS_TYPE_BOOLEAN, &device_powered, + device_disable_cb, g_strdup(path), NULL); - device_list = g_slist_append(device_list, device); + return -EINPROGRESS; +} - message = dbus_message_new_method_call(BLUEZ_SERVICE, path, - BLUEZ_ADAPTER_INTERFACE, GET_PROPERTIES); - if (message == NULL) - return; +static void adapter_property_change(GDBusProxy *proxy, const char *name, + DBusMessageIter *iter, void *user_data) +{ + struct connman_device *device; + const char *path; + bool adapter_powered, device_powered; - if (dbus_connection_send_with_reply(connection, message, - &call, TIMEOUT) == FALSE) { - connman_error("Failed to get adapter properties"); - dbus_message_unref(message); + if (strcmp(name, "Powered") != 0) return; + + path = g_dbus_proxy_get_path(proxy); + device = g_hash_table_lookup(devices, path); + + adapter_powered = proxy_get_bool(proxy, "Powered"); + device_powered = connman_device_get_powered(device); + + DBG("device %p %s device powered %d adapter powered %d", device, path, + device_powered, adapter_powered); + + if (device_powered != adapter_powered) { + if (adapter_powered) + enable_device(device, path); + else + disable_device(device, path); } +} + +static void device_free(gpointer data) +{ + struct connman_device *device = data; + GDBusProxy *proxy = connman_device_get_data(device); + + connman_device_set_data(device, NULL); + if (proxy) + g_dbus_proxy_unref(proxy); - dbus_pending_call_set_notify(call, properties_reply, message, NULL); + connman_device_unregister(device); + connman_device_unref(device); } -static void remove_adapter(DBusConnection *connection, const char *path) +struct tethering_info { + struct connman_technology *technology; + char *bridge; + bool enable; +}; + +static void tethering_free(void *user_data) { - struct connman_element *device; + struct tethering_info *tethering = user_data; - DBG("path %s", path); + g_free(tethering->bridge); + g_free(tethering); +} - device = find_adapter(path); - if (device == NULL) +static void tethering_create_cb(DBusMessage *message, void *user_data) +{ + struct tethering_info *tethering = user_data; + + if (dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_ERROR) { + const char *dbus_error = dbus_message_get_error_name(message); + + DBG("%s tethering failed: %s", + tethering->enable ? "enable" : "disable", + dbus_error); return; + } - device_list = g_slist_remove(device_list, device); + DBG("bridge %s %s", tethering->bridge, tethering->enable ? + "enabled": "disabled"); - connman_element_unregister(device); - connman_element_unref(device); + if (tethering->technology) + connman_technology_tethering_notify(tethering->technology, + tethering->enable); } -static void adapters_reply(DBusPendingCall *call, void *user_data) +static void tethering_append(DBusMessageIter *iter, void *user_data) { - DBusConnection *connection = user_data; - DBusMessage *reply; - DBusError error; - char **adapters; - int i, num_adapters; + struct tethering_info *tethering = user_data; + const char *nap = "nap"; - DBG(""); + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &nap); + if (tethering->enable) + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, + &tethering->bridge); +} + +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; - reply = dbus_pending_call_steal_reply(call); + DBG("path %s bridge %s", path, bridge); - dbus_error_init(&error); + if (!bridge) { + g_free(tethering); + return false; + } - 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; + proxy = g_dbus_proxy_new(client, path, "org.bluez.NetworkServer1"); + if (!proxy) { + g_free(tethering); + return false; } - for (i = 0; i < num_adapters; i++) - add_adapter(connection, adapters[i]); + tethering->technology = technology; + tethering->bridge = g_strdup(bridge); + tethering->enable = enabled; + + if (tethering->enable) + method = "Register"; + else + method = "Unregister"; - g_strfreev(adapters); + result = g_dbus_proxy_method_call(proxy, method, tethering_append, + tethering_create_cb, tethering, tethering_free); -done: - dbus_message_unref(reply); + g_dbus_proxy_unref(proxy); + + return result; } -static void bluetooth_connect(DBusConnection *connection, void *user_data) +static void device_create(GDBusProxy *proxy) { - DBusMessage *message; - DBusPendingCall *call; + 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; + + address2ident(address, ident); + + device = connman_device_create("bluetooth", + CONNMAN_DEVICE_TYPE_BLUETOOTH); + if (!device) + return; - DBG("connection %p", connection); + connman_device_set_data(device, g_dbus_proxy_ref(proxy)); + connman_device_set_ident(device, ident); - message = dbus_message_new_method_call(BLUEZ_SERVICE, "/", - BLUEZ_MANAGER_INTERFACE, LIST_ADAPTERS); - if (message == NULL) + g_hash_table_replace(devices, g_strdup(path), device); + + 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(device) < 0) { + g_hash_table_remove(devices, device); return; + } + + g_dbus_proxy_set_property_watch(proxy, adapter_property_change, NULL); - if (dbus_connection_send_with_reply(connection, message, - &call, TIMEOUT) == FALSE) { - connman_error("Failed to get Bluetooth adapters"); - dbus_message_unref(message); + powered = proxy_get_bool(proxy, "Powered"); + connman_device_set_powered(device, powered); + + if (proxy_get_role(proxy) && !bluetooth_tethering) + tethering_create(path, NULL, NULL, false); +} + +static void object_added(GDBusProxy *proxy, void *user_data) +{ + const char *interface; + + interface = g_dbus_proxy_get_interface(proxy); + if (!interface) { + connman_warn("Interface or proxy missing when adding " + "bluetooth object"); return; } - dbus_pending_call_set_notify(call, adapters_reply, connection, NULL); + if (strcmp(interface, "org.bluez.Adapter1") == 0) { + DBG("%s %s", interface, g_dbus_proxy_get_path(proxy)); + device_create(proxy); + return; + } - dbus_message_unref(message); + if (strcmp(interface, "org.bluez.Network1") == 0) { + DBG("%s %s", interface, g_dbus_proxy_get_path(proxy)); + pan_create(proxy); + return; + } } -static void bluetooth_disconnect(DBusConnection *connection, void *user_data) +static void object_removed(GDBusProxy *proxy, void *user_data) { - GSList *list; + const char *interface, *path; + + interface = g_dbus_proxy_get_interface(proxy); + if (!interface) { + connman_warn("Interface or proxy missing when removing " + "bluetooth object"); + return; + } + + if (strcmp(interface, "org.bluez.Adapter1") == 0) { + path = g_dbus_proxy_get_path(proxy); + DBG("%s %s", interface, path); - DBG("connection %p", connection); + g_hash_table_remove(devices, path); + } - for (list = device_list; list; list = list->next) { - struct connman_element *device = list->data; + if (strcmp(interface, "org.bluez.Network1") == 0) { + path = g_dbus_proxy_get_path(proxy); + DBG("%s %s", interface, path); - connman_element_unregister(device); - connman_element_unref(device); + g_hash_table_remove(networks, path); } - g_slist_free(device_list); - device_list = NULL; } -static DBusHandlerResult bluetooth_signal(DBusConnection *connection, - DBusMessage *message, void *user_data) +static int bluetooth_device_probe(struct connman_device *device) { - DBG("connection %p", connection); + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init(&iter, devices); - 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); + while (g_hash_table_iter_next(&iter, &key, &value)) { + struct connman_device *known = value; + + if (device == known) + return 0; } - return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; + return -EOPNOTSUPP; } -static DBusConnection *connection; -static guint watch; +static void bluetooth_device_remove(struct connman_device *device) +{ + DBG("%p", device); +} -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; +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, +}; -static const char *adapter_rule = "type=signal,member=" PROPERTY_CHANGED - ",interface=" BLUEZ_ADAPTER_INTERFACE; +static int bluetooth_tech_probe(struct connman_technology *technology) +{ + return 0; +} -static int bluetooth_init(void) +static void bluetooth_tech_remove(struct connman_technology *technology) { - int err = -EIO; +} + +static int bluetooth_tech_set_tethering(struct connman_technology *technology, + const char *identifier, const char *passphrase, + const char *bridge, bool enabled) +{ + GHashTableIter hash_iter; + gpointer key, value; + int i = 0; + + bluetooth_tethering = enabled; + + g_hash_table_iter_init(&hash_iter, devices); + + while (g_hash_table_iter_next(&hash_iter, &key, &value)) { + const char *path = key; + struct connman_device *device = value; + + DBG("device %p", device); + + if (tethering_create(path, technology, bridge, enabled) + ) + i++; + } + + DBG("%s %d device(s)", enabled ? "enabled" : "disabled", i); + + if (i == 0) + return -ENODEV; + + return 0; +} + +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) +{ connection = connman_dbus_get_connection(); - if (connection == NULL) - return -EIO; + if (!connection) + goto out; + + if (connman_technology_driver_register(&tech_driver) < 0) { + connman_warn("Failed to initialize technology for Bluez 5"); + goto out; + } - if (dbus_connection_add_filter(connection, bluetooth_signal, - NULL, NULL) == FALSE) - goto unref; + devices = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + device_free); - err = connman_driver_register(&bluetooth_driver); - if (err < 0) - 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_driver_unregister(&bluetooth_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); + + 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; + } - 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); + 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); - return err; + if (client) + g_dbus_client_unref(client); + + 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; - bluetooth_disconnect(connection, NULL); + g_dbus_client_unref(client); - connman_driver_unregister(&bluetooth_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, - bluetooth_init, bluetooth_exit) + CONNMAN_PLUGIN_PRIORITY_DEFAULT, bluetooth_init, bluetooth_exit)