+static GSList *adapter_list = NULL;
+
+static void free_adapters(void)
+{
+ GSList *list;
+
+ DBG("");
+
+ for (list = adapter_list; list; list = list->next) {
+ struct connman_device *adapter = list->data;
+
+ connman_device_unregister(adapter);
+ connman_device_unref(adapter);
+ }
+
+ g_slist_free(adapter_list);
+ adapter_list = NULL;
+}
+
+static struct connman_device *find_adapter(const char *path)
+{
+ GSList *list;
+
+ DBG("path %s", path);
+
+ for (list = adapter_list; list; list = list->next) {
+ struct connman_device *adapter = list->data;
+ const char *adapter_path = connman_device_get_string(adapter,
+ "Node");
+
+ if (adapter_path == NULL)
+ continue;
+
+ if (g_str_equal(adapter_path, path) == TRUE)
+ return adapter;
+ }
+
+ return NULL;
+}
+
+static void device_properties(DBusConnection *connection, const char *path,
+ DBusMessage *message, void *user_data)
+{
+ struct connman_device *device = user_data;
+ const char *node = g_basename(path);
+ struct connman_network *network;
+
+ DBG("path %s", path);
+
+ network = connman_device_get_network(device, node);
+ if (network != NULL)
+ return;
+
+ network = connman_network_create(node,
+ CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN);
+ if (network == NULL)
+ return;
+
+ connman_network_set_protocol(network, CONNMAN_NETWORK_PROTOCOL_IP);
+
+ connman_network_set_string(network, "Node", path);
+
+ connman_device_add_network(device, network);
+}
+
+static void check_devices(struct connman_device *adapter,
+ DBusConnection *connection, DBusMessageIter *array)
+{
+ 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_OBJECT_PATH) {
+ const char *path;
+
+ dbus_message_iter_get_basic(&value, &path);
+
+ get_properties(connection, path, BLUEZ_DEVICE_INTERFACE,
+ device_properties, adapter);
+
+ dbus_message_iter_next(&value);
+ }
+}
+
+static void property_changed(DBusConnection *connection, DBusMessage *message)
+{
+ const char *path = dbus_message_get_path(message);
+ struct connman_device *adapter;
+ DBusMessageIter iter, value;
+ const char *key;
+
+ DBG("path %s", path);
+
+ adapter = find_adapter(path);
+ if (adapter == NULL)
+ return;
+
+ if (dbus_message_iter_init(message, &iter) == FALSE)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &key);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &value);
+
+ if (g_str_equal(key, "Powered") == TRUE) {
+ gboolean val;
+
+ dbus_message_iter_get_basic(&value, &val);
+ connman_device_set_powered(adapter, val);
+ } else if (g_str_equal(key, "Discovering") == TRUE) {
+ gboolean val;
+
+ dbus_message_iter_get_basic(&value, &val);
+ connman_device_set_scanning(adapter, val);
+ }
+}
+
+static void parse_adapter_properties(struct connman_device *adapter,
+ DBusConnection *connection,
+ DBusMessage *reply)
+{
+ DBusMessageIter array, dict;
+
+ if (dbus_message_iter_init(reply, &array) == FALSE)
+ return;
+
+ if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
+ return;
+
+ dbus_message_iter_recurse(&array, &dict);
+
+ while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry, value;
+ const char *key;
+
+ dbus_message_iter_recurse(&dict, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ if (g_str_equal(key, "Powered") == TRUE) {
+ gboolean val;
+
+ dbus_message_iter_get_basic(&value, &val);
+ connman_device_set_powered(adapter, val);
+ } else if (g_str_equal(key, "Discovering") == TRUE) {
+ gboolean val;
+
+ 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);
+ }
+
+ dbus_message_iter_next(&dict);
+ }
+}
+
+static void adapter_properties(DBusConnection *connection, const char *path,
+ DBusMessage *message, void *user_data)
+{
+ const char *node = g_basename(path);
+ struct connman_device *adapter;
+
+ DBG("path %s", path);
+
+ adapter = find_adapter(path);
+ if (adapter != NULL)
+ goto done;
+
+ adapter = connman_device_create(node, CONNMAN_DEVICE_TYPE_BLUETOOTH);
+ if (adapter == NULL)
+ return;
+
+ connman_device_set_string(adapter, "Node", path);
+
+ 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);
+
+ connman_device_set_policy(adapter, CONNMAN_DEVICE_POLICY_MANUAL);
+ connman_device_set_mode(adapter, CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE);
+
+ if (connman_device_register(adapter) < 0) {
+ connman_device_unref(adapter);
+ return;
+ }
+
+ adapter_list = g_slist_append(adapter_list, adapter);
+
+done:
+ parse_adapter_properties(adapter, connection, message);
+}
+
+static void add_adapter(DBusConnection *connection, const char *path)
+{
+ DBG("path %s", path);
+
+ get_properties(connection, path, BLUEZ_ADAPTER_INTERFACE,
+ adapter_properties, NULL);
+}
+
+static void remove_adapter(DBusConnection *connection, const char *path)
+{
+ struct connman_device *adapter;
+
+ DBG("path %s", path);
+
+ adapter = find_adapter(path);
+ if (adapter == NULL)
+ return;
+
+ adapter_list = g_slist_remove(adapter_list, adapter);
+
+ connman_device_unregister(adapter);
+ connman_device_unref(adapter);
+}
+
+static void list_adapters_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusConnection *connection = user_data;
+ DBusMessage *reply;
+ DBusError error;
+ char **adapters;
+ int i, num_adapters;
+
+ DBG("");
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&error);
+
+ 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;
+ }
+
+ for (i = 0; i < num_adapters; i++)
+ get_properties(connection, adapters[i],
+ BLUEZ_ADAPTER_INTERFACE,
+ adapter_properties, NULL);
+
+ g_strfreev(adapters);
+
+done:
+ dbus_message_unref(reply);
+}
+
+static void bluetooth_connect(DBusConnection *connection, void *user_data)
+{
+ DBusMessage *message;
+ DBusPendingCall *call;
+
+ DBG("connection %p", connection);
+
+ message = dbus_message_new_method_call(BLUEZ_SERVICE, "/",
+ BLUEZ_MANAGER_INTERFACE, LIST_ADAPTERS);
+ if (message == NULL)
+ return;
+
+ if (dbus_connection_send_with_reply(connection, message,
+ &call, TIMEOUT) == FALSE) {
+ connman_error("Failed to get Bluetooth adapters");
+ dbus_message_unref(message);
+ return;
+ }
+
+ dbus_pending_call_set_notify(call, list_adapters_reply,
+ connection, NULL);
+
+ dbus_message_unref(message);
+}
+
+static void bluetooth_disconnect(DBusConnection *connection, void *user_data)
+{
+ DBG("connection %p", connection);
+
+ free_adapters();
+}
+
+static DBusHandlerResult bluetooth_signal(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ 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;
+
+ DBG("connection %p", connection);
+
+ 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);
+ }
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+