#include #include "e.h" #include "agent.h" #include "e_mod_main.h" #include "ebluez4.h" typedef struct _Pair_Cb { void (*cb)(void *, Eina_Bool, const char *); void *data; } Pair_Cb; Service services[] = { { HumanInterfaceDevice_UUID, INPUT }, { AudioSource_UUID, AUDIO_SOURCE }, { AudioSink_UUID, AUDIO_SINK }, { NULL, NONE} }; static int _addr_cmp(const void *d1, const void *d2) { const Device *dev = d1; const char *addr = d2; return strcmp(dev->addr, addr); } static void _free_dev(Device *dev) { if (dev->obj) edbus_object_unref(dev->obj); eina_stringshare_del(dev->addr); dev->addr = NULL; eina_stringshare_del(dev->name); dev->name = NULL; free(dev); } static void _free_dev_list(Eina_List **list) { Device *dev; EINA_LIST_FREE(*list, dev) _free_dev(dev); *list = NULL; } static void _unset_adapter() { if (!ctxt->adap_obj) return; DBG("Remove adapter %s", edbus_object_path_get(ctxt->adap_obj)); _free_dev_list(&ctxt->devices); ctxt->devices = NULL; _free_dev_list(&ctxt->found_devices); ctxt->found_devices = NULL; edbus_object_unref(ctxt->adap_obj); ctxt->adap_obj = NULL; ebluez4_update_all_gadgets_visibility(); } static Profile _uuid_to_profile(const char *uuid) { Service *service; for (service = (Service *)services; service && service->uuid; service++) if (!strcmp(service->uuid, uuid)) return service->profile; return NONE; } static void _set_dev_services(Device *dev, EDBus_Message_Iter *uuids) { const char *uuid; while (edbus_message_iter_get_and_next(uuids, 's', &uuid)) switch (_uuid_to_profile(uuid)) { case INPUT: if (!dev->proxy.input) dev->proxy.input = edbus_proxy_get(dev->obj, INPUT_INTERFACE); break; case AUDIO_SOURCE: if (!dev->proxy.audio_source) dev->proxy.audio_source = edbus_proxy_get(dev->obj, AUDIO_SOURCE_INTERFACE); break; case AUDIO_SINK: if (!dev->proxy.audio_sink) dev->proxy.audio_sink = edbus_proxy_get(dev->obj, AUDIO_SINK_INTERFACE); break; default: break; } } static void _retrieve_properties(EDBus_Message_Iter *dict, const char **addr, const char **name, Eina_Bool *paired, Eina_Bool *connected, EDBus_Message_Iter **uuids) { EDBus_Message_Iter *entry, *variant; const char *key; while (edbus_message_iter_get_and_next(dict, 'e', &entry)) { if(!edbus_message_iter_arguments_get(entry, "sv", &key, &variant)) return; if (!strcmp(key, "Address")) { if(!edbus_message_iter_arguments_get(variant, "s", addr)) return; } else if (!strcmp(key, "Name")) { if(!edbus_message_iter_arguments_get(variant, "s", name)) return; } else if (!strcmp(key, "Paired")) { if(!edbus_message_iter_arguments_get(variant, "b", paired)) return; } else if (!strcmp(key, "Connected")) { if(!edbus_message_iter_arguments_get(variant, "b", connected)) return; } else if (!strcmp(key, "UUIDs")) { if(!edbus_message_iter_arguments_get(variant, "as", uuids)) return; } } } static void _on_prop_changed(void *context, const EDBus_Message *msg) { const char *key, *name; char err_msg[4096]; Eina_Bool paired, connected; EDBus_Message_Iter *variant, *uuids; Device *dev = context; Device *found_dev = eina_list_search_unsorted(ctxt->found_devices, _addr_cmp, dev->addr); if (!edbus_message_arguments_get(msg, "sv", &key, &variant)) { snprintf(err_msg, sizeof(err_msg), "Property of %s changed, but could not be read", dev->name); ERR("%s", err_msg); ebluez4_show_error("Bluez Error", err_msg); return; } if (!strcmp(key, "Name")) { if(!edbus_message_iter_arguments_get(variant, "s", &name)) return; DBG("'%s' property of %s changed to %s", key, dev->name, name); eina_stringshare_del(dev->name); dev->name = eina_stringshare_add(name); if (found_dev) { eina_stringshare_del(found_dev->name); found_dev->name = eina_stringshare_add(name); ebluez4_update_instances(ctxt->found_devices); } } else if (!strcmp(key, "Paired")) { if(!edbus_message_iter_arguments_get(variant, "b", &paired)) return; DBG("'%s' property of %s changed to %d", key, dev->name, paired); dev->paired = paired; if (found_dev) { found_dev->paired = paired; ebluez4_update_instances(ctxt->found_devices); } } else if (!strcmp(key, "Connected")) { if(!edbus_message_iter_arguments_get(variant, "b", &connected)) return; DBG("'%s' property of %s changed to %d", key, dev->name, connected); dev->connected = connected; } else if (!strcmp(key, "UUIDs")) { if(!edbus_message_iter_arguments_get(variant, "as", &uuids)) return; _set_dev_services(dev, uuids); } } static void _on_connected(void *data, const EDBus_Message *msg, EDBus_Pending *pending) { const char *err_name, *err_msg; if (edbus_message_error_get(msg, &err_name, &err_msg)) { ERR("%s: %s", err_name, err_msg); ebluez4_show_error(err_name, err_msg); return; } } static void _try_to_connect(EDBus_Proxy *proxy) { if (proxy) edbus_proxy_call(proxy, "Connect", _on_connected, NULL, -1, ""); } static void _on_disconnected(void *data, const EDBus_Message *msg, EDBus_Pending *pending) { const char *err_name, *err_msg; if (edbus_message_error_get(msg, &err_name, &err_msg)) { ERR("%s: %s", err_name, err_msg); ebluez4_show_error(err_name, err_msg); return; } } static void _try_to_disconnect(EDBus_Proxy *proxy) { if (proxy) edbus_proxy_call(proxy, "Disconnect", _on_disconnected, NULL, -1, ""); } static void _on_paired(void *data, const EDBus_Message *msg, EDBus_Pending *pending) { const char *err_name, *err_msg; Pair_Cb *d = data; Eina_Bool success = EINA_TRUE; if (edbus_message_error_get(msg, &err_name, &err_msg)) { ERR("%s: %s", err_name, err_msg); success = EINA_FALSE; } if (d->cb) d->cb(d->data, success, err_msg); free(d); } static void _on_dev_properties(void *data, const EDBus_Message *msg, EDBus_Pending *pending) { EDBus_Message_Iter *dict, *uuids; const char *addr, *name; Eina_Bool paired; Eina_Bool connected; Device *dev = data; if (!edbus_message_arguments_get(msg, "a{sv}", &dict)) return; _retrieve_properties(dict, &addr, &name, &paired, &connected, &uuids); dev->addr = eina_stringshare_add(addr); dev->name = eina_stringshare_add(name); dev->paired = paired; dev->connected = connected; _set_dev_services(dev, uuids); } static void _unset_dev(Device *dev, Eina_List *list) { if (!dev) return; if (list == ctxt->devices) ctxt->devices = eina_list_remove(list, dev); else ctxt->found_devices = eina_list_remove(list, dev); _free_dev(dev); } static void _set_dev(const char *path) { Device *dev = calloc(1, sizeof(Device)); dev->obj = edbus_object_get(ctxt->conn, BLUEZ_BUS, path); dev->proxy.dev = edbus_proxy_get(dev->obj, DEVICE_INTERFACE); edbus_proxy_call(dev->proxy.dev, "GetProperties", _on_dev_properties, dev, -1, ""); edbus_proxy_signal_handler_add(dev->proxy.dev, "PropertyChanged", _on_prop_changed, dev); ctxt->devices = eina_list_append(ctxt->devices, dev); } static void _on_removed(void *context, const EDBus_Message *msg) { const char *path; Device *dev, *fdev; if (!edbus_message_arguments_get(msg, "o", &path)) return; dev = eina_list_search_unsorted(ctxt->devices, ebluez4_path_cmp, path); fdev = eina_list_search_unsorted(ctxt->found_devices, _addr_cmp, dev->addr); _unset_dev(dev, ctxt->devices); _unset_dev(fdev, ctxt->found_devices); } static void _on_created(void *context, const EDBus_Message *msg) { const char *path; if (!edbus_message_arguments_get(msg, "o", &path)) return; _set_dev(path); } static void _on_device_found(void *context, const EDBus_Message *msg) { EDBus_Message_Iter *dict, *uuids; const char *addr, *name; Eina_Bool paired, connected; Device *dev; if (!edbus_message_arguments_get(msg, "sa{sv}", &addr, &dict)) return; if(eina_list_search_unsorted(ctxt->found_devices, _addr_cmp, addr)) return; if (!edbus_message_arguments_get(msg, "a{sv}", &dict)) return; _retrieve_properties(dict, &addr, &name, &paired, &connected, &uuids); dev = calloc(1, sizeof(Device)); dev->addr = eina_stringshare_add(addr); dev->name = eina_stringshare_add(name); dev->paired = paired; ctxt->found_devices = eina_list_append(ctxt->found_devices, dev); ebluez4_update_instances(ctxt->found_devices); } static void _on_list(void *data, const EDBus_Message *msg, EDBus_Pending *pending) { EDBus_Message_Iter *array; const char *path; const char *err_msg = "Error reading list of devices"; if (!edbus_message_arguments_get(msg, "ao", &array)) { ERR("%s", err_msg); ebluez4_show_error("Bluez Error", err_msg); return; } while (edbus_message_iter_get_and_next(array, 'o', &path)) _set_dev(path); } static void _set_adapter(const EDBus_Message *msg) { const char *adap_path; const char *err_msg = "Error reading path of Default Adapter"; if (!edbus_message_arguments_get(msg, "o", &adap_path)) { ERR("%s", err_msg); ebluez4_show_error("Bluez Error", err_msg); return; } DBG("Setting adapter to %s", adap_path); if (ctxt->adap_obj) _unset_adapter(); ctxt->adap_obj = edbus_object_get(ctxt->conn, BLUEZ_BUS, adap_path); ctxt->adap_proxy = edbus_proxy_get(ctxt->adap_obj, ADAPTER_INTERFACE); edbus_proxy_signal_handler_add(ctxt->adap_proxy, "DeviceFound", _on_device_found, NULL); edbus_proxy_signal_handler_add(ctxt->adap_proxy, "DeviceCreated", _on_created, NULL); edbus_proxy_signal_handler_add(ctxt->adap_proxy, "DeviceRemoved", _on_removed, NULL); edbus_proxy_call(ctxt->adap_proxy, "ListDevices", _on_list, NULL, -1, ""); edbus_proxy_call(ctxt->adap_proxy, "RegisterAgent", NULL, NULL, -1, "os", REMOTE_AGENT_PATH, "KeyboardDisplay"); ebluez4_update_all_gadgets_visibility(); } static void _default_adapter_get(void *data, const EDBus_Message *msg, EDBus_Pending *pending) { const char *err_name, *err_msg; /* * If bluetoothd is starting up, we can fail here and wait for the * DefaultAdapterChanged signal later */ if (edbus_message_error_get(msg, &err_name, &err_msg)) return; if (!ctxt->adap_obj) _set_adapter(msg); } static void _on_adapter_changed(void *context, const EDBus_Message *msg) { _set_adapter(msg); } static void _on_adapter_removed(void *context, const EDBus_Message *msg) { const char *adap_path; const char *err_msg = "Error reading path of Removed Adapter"; if (!edbus_message_arguments_get(msg, "o", &adap_path)) { ERR("%s", err_msg); ebluez4_show_error("Bluez Error", err_msg); return; } if (!strcmp(edbus_object_path_get(ctxt->adap_obj), adap_path)) _unset_adapter(); } static void _bluez_monitor(void *data, const char *bus, const char *old_id, const char *new_id) { if (!strcmp(old_id,"") && strcmp(new_id,"")) // Bluez up edbus_proxy_call(ctxt->man_proxy, "DefaultAdapter", _default_adapter_get, NULL, -1, ""); else if (strcmp(old_id,"") && !strcmp(new_id,"")) // Bluez down _unset_adapter(); } /* Public Functions */ void ebluez4_edbus_init() { EDBus_Object *obj; ctxt = calloc(1, sizeof(Context)); edbus_init(); ctxt->conn = edbus_connection_get(EDBUS_CONNECTION_TYPE_SYSTEM); obj = edbus_object_get(ctxt->conn, BLUEZ_BUS, MANAGER_PATH); ctxt->man_proxy = edbus_proxy_get(obj, MANAGER_INTERFACE); ebluez4_register_agent_interfaces(ctxt->conn); edbus_proxy_signal_handler_add(ctxt->man_proxy, "DefaultAdapterChanged", _on_adapter_changed, NULL); edbus_proxy_signal_handler_add(ctxt->man_proxy, "AdapterRemoved", _on_adapter_removed, NULL); edbus_name_owner_changed_callback_add(ctxt->conn, BLUEZ_BUS, _bluez_monitor, NULL, EINA_TRUE); } void ebluez4_edbus_shutdown() { _free_dev_list(&ctxt->devices); _free_dev_list(&ctxt->found_devices); edbus_connection_unref(ctxt->conn); free(ctxt); edbus_shutdown(); } void ebluez4_start_discovery() { _free_dev_list(&ctxt->found_devices); ebluez4_update_instances(ctxt->found_devices); edbus_proxy_call(ctxt->adap_proxy, "StartDiscovery", NULL, NULL, -1, ""); } void ebluez4_stop_discovery() { edbus_proxy_call(ctxt->adap_proxy, "StopDiscovery", NULL, NULL, -1, ""); } void ebluez4_connect_to_device(Device *dev) { _try_to_connect(dev->proxy.input); _try_to_connect(dev->proxy.audio_source); _try_to_connect(dev->proxy.audio_sink); } void ebluez4_disconnect_device(Device *dev) { _try_to_disconnect(dev->proxy.input); _try_to_disconnect(dev->proxy.audio_source); _try_to_disconnect(dev->proxy.audio_sink); } void ebluez4_pair_with_device(const char *addr, void (*cb)(void *, Eina_Bool, const char *), void *data) { Pair_Cb *d = malloc(sizeof(Pair_Cb)); EINA_SAFETY_ON_NULL_RETURN(d); d->cb = cb; d->data = data; edbus_proxy_call(ctxt->adap_proxy, "CreatePairedDevice", _on_paired, d, -1, "sos", addr, AGENT_PATH, "KeyboardDisplay"); } void ebluez4_remove_device(EDBus_Object *obj) { edbus_proxy_call(ctxt->adap_proxy, "RemoveDevice", NULL, NULL, -1, "o", edbus_object_path_get(obj)); } int ebluez4_path_cmp(const void *d1, const void *d2) { const Device *dev = d1; const char *path = d2; return strcmp(edbus_object_path_get(dev->obj), path); }