-static GDBusMethodTable service_methods[] = {
- { "GetProperties", "", "a{sv}", get_properties },
- { "SetProperty", "sv", "", set_property },
- { "ClearProperty", "s", "", clear_property },
- { "Connect", "", "", connect_service,
- G_DBUS_METHOD_FLAG_ASYNC },
- { "Disconnect", "", "", disconnect_service },
- { "Remove", "", "", remove_service },
- { "MoveBefore", "o", "", move_before },
- { "MoveAfter", "o", "", move_after },
- { "ResetCounters", "", "", reset_counters },
+static struct _services_notify {
+ int id;
+ GHashTable *add;
+ GHashTable *remove;
+} *services_notify;
+
+static void service_append_added_foreach(gpointer data, gpointer user_data)
+{
+ struct connman_service *service = data;
+ DBusMessageIter *iter = user_data;
+
+ if (service == NULL || service->path == NULL) {
+ DBG("service %p or path is NULL", service);
+ return;
+ }
+
+ if (g_hash_table_lookup(services_notify->add, service->path) != NULL) {
+ DBG("new %s", service->path);
+
+ append_struct(service, iter);
+ g_hash_table_remove(services_notify->add, service->path);
+ } else {
+ DBG("changed %s", service->path);
+
+ append_struct_service(iter, NULL, service);
+ }
+}
+
+static void service_append_ordered(DBusMessageIter *iter, void *user_data)
+{
+ if (service_list != NULL)
+ g_sequence_foreach(service_list,
+ service_append_added_foreach, iter);
+}
+
+static void append_removed(gpointer key, gpointer value, gpointer user_data)
+{
+ char *objpath = key;
+ DBusMessageIter *iter = user_data;
+
+ DBG("removed %s", objpath);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &objpath);
+}
+
+static gboolean service_send_changed(gpointer data)
+{
+ DBusMessage *signal;
+ DBusMessageIter iter, array;
+
+ DBG("");
+
+ signal = dbus_message_new_signal(CONNMAN_MANAGER_PATH,
+ CONNMAN_MANAGER_INTERFACE, "ServicesChanged");
+ if (signal == NULL)
+ return FALSE;
+
+ __connman_dbus_append_objpath_dict_array(signal,
+ service_append_ordered, NULL);
+
+ dbus_message_iter_init_append(signal, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_OBJECT_PATH_AS_STRING, &array);
+
+ g_hash_table_foreach(services_notify->remove, append_removed, &array);
+
+ dbus_message_iter_close_container(&iter, &array);
+
+ dbus_connection_send(connection, signal, NULL);
+ dbus_message_unref(signal);
+
+ g_hash_table_remove_all(services_notify->remove);
+ g_hash_table_remove_all(services_notify->add);
+
+ services_notify->id = 0;
+ return FALSE;
+}
+
+static void service_schedule_changed(void)
+{
+ if (services_notify->id != 0)
+ return;
+
+ services_notify->id = g_timeout_add(100, service_send_changed, NULL);
+}
+
+static void service_schedule_added(struct connman_service *service)
+{
+ DBG("service %p", service);
+
+ g_hash_table_remove(services_notify->remove, service->path);
+ g_hash_table_replace(services_notify->add, service->path, service);
+
+ service_schedule_changed();
+}
+
+static void service_schedule_removed(struct connman_service *service)
+{
+ DBG("service %p %s", service, service->path);
+
+ if (service == NULL || service->path == NULL) {
+ DBG("service %p or path is NULL", service);
+ return;
+ }
+
+ g_hash_table_remove(services_notify->add, service->path);
+ g_hash_table_replace(services_notify->remove, g_strdup(service->path),
+ NULL);
+
+ service_schedule_changed();
+}
+
+static const GDBusMethodTable service_methods[] = {
+ { GDBUS_DEPRECATED_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ get_properties) },
+ { GDBUS_METHOD("SetProperty",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" }),
+ NULL, set_property) },
+ { GDBUS_METHOD("ClearProperty",
+ GDBUS_ARGS({ "name", "s" }), NULL,
+ clear_property) },
+ { GDBUS_ASYNC_METHOD("Connect", NULL, NULL,
+ connect_service) },
+ { GDBUS_METHOD("Disconnect", NULL, NULL,
+ disconnect_service) },
+ { GDBUS_METHOD("Remove", NULL, NULL, remove_service) },
+ { GDBUS_METHOD("MoveBefore",
+ GDBUS_ARGS({ "service", "o" }), NULL,
+ move_before) },
+ { GDBUS_METHOD("MoveAfter",
+ GDBUS_ARGS({ "service", "o" }), NULL,
+ move_after) },
+ { GDBUS_METHOD("ResetCounters", NULL, NULL, reset_counters) },