mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	bluez5: add abstraction for remote DBus objects
Add framework for enumerating remote objects via DBus object manager interfaces.
This commit is contained in:
		
							parent
							
								
									57956ad100
								
							
						
					
					
						commit
						42e463f726
					
				
					 3 changed files with 838 additions and 0 deletions
				
			
		
							
								
								
									
										698
									
								
								spa/plugins/bluez5/dbus-monitor.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										698
									
								
								spa/plugins/bluez5/dbus-monitor.c
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,698 @@
 | 
				
			||||||
 | 
					/* Spa dbus
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright © 2022 Pauli Virtanen
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
				
			||||||
 | 
					 * copy of this software and associated documentation files (the "Software"),
 | 
				
			||||||
 | 
					 * to deal in the Software without restriction, including without limitation
 | 
				
			||||||
 | 
					 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
				
			||||||
 | 
					 * and/or sell copies of the Software, and to permit persons to whom the
 | 
				
			||||||
 | 
					 * Software is furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice (including the next
 | 
				
			||||||
 | 
					 * paragraph) shall be included in all copies or substantial portions of the
 | 
				
			||||||
 | 
					 * Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | 
				
			||||||
 | 
					 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
				
			||||||
 | 
					 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
				
			||||||
 | 
					 * DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					#include <stddef.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <dbus/dbus.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <spa/support/log.h>
 | 
				
			||||||
 | 
					#include <spa/utils/hook.h>
 | 
				
			||||||
 | 
					#include <spa/utils/list.h>
 | 
				
			||||||
 | 
					#include <spa/utils/string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "dbus-monitor.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#undef SPA_LOG_TOPIC_DEFAULT
 | 
				
			||||||
 | 
					#define SPA_LOG_TOPIC_DEFAULT (&impl->log_topic)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct impl
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct spa_dbus_monitor this;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DBusConnection *conn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct spa_log_topic log_topic;
 | 
				
			||||||
 | 
						struct spa_log *log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DBusPendingCall *get_managed_objects_call;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned int filters_added:1;
 | 
				
			||||||
 | 
						unsigned int objects_listed:1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct spa_list objects[];
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct object
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						unsigned int removed:1;
 | 
				
			||||||
 | 
						unsigned int updating:1;
 | 
				
			||||||
 | 
						union {
 | 
				
			||||||
 | 
							struct spa_dbus_object this;
 | 
				
			||||||
 | 
							/* extra data follows 'this', force safer alignment */
 | 
				
			||||||
 | 
							long double _align;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void object_emit_update(struct object *o)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (o->this.interface->update)
 | 
				
			||||||
 | 
							o->this.interface->update(&o->this);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void object_emit_remove(struct object *o)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (o->this.interface->remove)
 | 
				
			||||||
 | 
							o->this.interface->remove(&o->this);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void object_emit_property(struct object *o, const char *key, DBusMessageIter *value)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (o->this.interface->property)
 | 
				
			||||||
 | 
							o->this.interface->property(&o->this, key, value);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct object *object_new(struct impl *impl, const struct spa_dbus_interface *iface,
 | 
				
			||||||
 | 
							struct spa_list *object_list, const char *path)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct object *o;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_assert(iface->object_size >= sizeof(struct spa_dbus_object));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						o = calloc(1, sizeof(struct object) + iface->object_size - sizeof(struct spa_dbus_object));
 | 
				
			||||||
 | 
						if (o == NULL)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						o->this.path = strdup(path);
 | 
				
			||||||
 | 
						o->this.interface = iface;
 | 
				
			||||||
 | 
						o->this.user_data = impl->this.user_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_assert(o->this.path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_list_append(object_list, &o->this.link);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return o;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void object_remove(struct object *o)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (o->removed)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						o->updating = true;
 | 
				
			||||||
 | 
						o->removed = true;
 | 
				
			||||||
 | 
						object_emit_remove(o);
 | 
				
			||||||
 | 
						spa_list_remove(&o->this.link);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void object_destroy(struct object *o)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						object_remove(o);
 | 
				
			||||||
 | 
						free((void *)o->this.path);
 | 
				
			||||||
 | 
						free(o);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct spa_dbus_interface *interface_find(struct impl *impl, const char *interface, struct spa_list **objects)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const struct spa_dbus_interface *iface;
 | 
				
			||||||
 | 
						size_t i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (iface = impl->this.interfaces, i = 0; iface && iface->name; ++iface, ++i) {
 | 
				
			||||||
 | 
							if (spa_streq(iface->name, interface)) {
 | 
				
			||||||
 | 
								*objects = &impl->objects[i];
 | 
				
			||||||
 | 
								return iface;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct object *object_find(struct spa_list *object_list, const char *path)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct spa_dbus_object *object;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_list_for_each(object, object_list, link) {
 | 
				
			||||||
 | 
							struct object *o = SPA_CONTAINER_OF(object, struct object, this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (spa_streq(o->this.path, path))
 | 
				
			||||||
 | 
								return o;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int object_update_props(struct impl *impl,
 | 
				
			||||||
 | 
							struct object *o,
 | 
				
			||||||
 | 
							DBusMessageIter *props_iter,
 | 
				
			||||||
 | 
							DBusMessageIter *invalidated_iter)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						o->updating = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (o->this.interface->property) {
 | 
				
			||||||
 | 
							while (invalidated_iter && dbus_message_iter_get_arg_type(invalidated_iter) != DBUS_TYPE_INVALID) {
 | 
				
			||||||
 | 
								const char *key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								dbus_message_iter_get_basic(invalidated_iter, &key);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								object_emit_property(o, key, NULL);
 | 
				
			||||||
 | 
								if (o->removed)
 | 
				
			||||||
 | 
									goto removed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								dbus_message_iter_next(invalidated_iter);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							while (props_iter && dbus_message_iter_get_arg_type(props_iter) != DBUS_TYPE_INVALID) {
 | 
				
			||||||
 | 
								DBusMessageIter entry, value;
 | 
				
			||||||
 | 
								const char *key;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								dbus_message_iter_recurse(props_iter, &entry);
 | 
				
			||||||
 | 
								dbus_message_iter_get_basic(&entry, &key);
 | 
				
			||||||
 | 
								dbus_message_iter_next(&entry);
 | 
				
			||||||
 | 
								dbus_message_iter_recurse(&entry, &value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								object_emit_property(o, key, &value);
 | 
				
			||||||
 | 
								if (o->removed)
 | 
				
			||||||
 | 
									goto removed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								dbus_message_iter_next(props_iter);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						object_emit_update(o);
 | 
				
			||||||
 | 
						if (o->removed)
 | 
				
			||||||
 | 
							goto removed;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						o->updating = false;
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					removed:
 | 
				
			||||||
 | 
						object_destroy(o);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void interface_added(struct impl *impl,
 | 
				
			||||||
 | 
							const char *object_path,
 | 
				
			||||||
 | 
							const char *interface_name,
 | 
				
			||||||
 | 
							DBusMessageIter *props_iter)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct object *o;
 | 
				
			||||||
 | 
						const struct spa_dbus_interface *iface;
 | 
				
			||||||
 | 
						struct spa_list *object_list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						iface = interface_find(impl, interface_name, &object_list);
 | 
				
			||||||
 | 
						if (!iface) {
 | 
				
			||||||
 | 
							spa_log_trace(impl->log, "dbus: skip path=%s, interface=%s",
 | 
				
			||||||
 | 
									object_path, interface_name);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_log_debug(impl->log, "dbus: added path=%s, interface=%s", object_path, interface_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						o = object_find(object_list, object_path);
 | 
				
			||||||
 | 
						if (o == NULL) {
 | 
				
			||||||
 | 
							o = object_new(impl, iface, object_list, object_path);
 | 
				
			||||||
 | 
							if (o == NULL) {
 | 
				
			||||||
 | 
								spa_log_warn(impl->log, "can't create object: %m");
 | 
				
			||||||
 | 
								return;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						object_update_props(impl, o, props_iter, NULL);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void interfaces_added(struct impl *impl, DBusMessageIter *arg_iter)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						DBusMessageIter it[3];
 | 
				
			||||||
 | 
						const char *object_path;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_message_iter_get_basic(arg_iter, &object_path);
 | 
				
			||||||
 | 
						dbus_message_iter_next(arg_iter);
 | 
				
			||||||
 | 
						dbus_message_iter_recurse(arg_iter, &it[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (dbus_message_iter_get_arg_type(&it[0]) != DBUS_TYPE_INVALID) {
 | 
				
			||||||
 | 
							const char *interface_name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							dbus_message_iter_recurse(&it[0], &it[1]);
 | 
				
			||||||
 | 
							dbus_message_iter_get_basic(&it[1], &interface_name);
 | 
				
			||||||
 | 
							dbus_message_iter_next(&it[1]);
 | 
				
			||||||
 | 
							dbus_message_iter_recurse(&it[1], &it[2]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							interface_added(impl, object_path, interface_name, &it[2]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							dbus_message_iter_next(&it[0]);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void interfaces_removed(struct impl *impl, DBusMessageIter *arg_iter)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const char *object_path;
 | 
				
			||||||
 | 
						DBusMessageIter it;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_message_iter_get_basic(arg_iter, &object_path);
 | 
				
			||||||
 | 
						dbus_message_iter_next(arg_iter);
 | 
				
			||||||
 | 
						dbus_message_iter_recurse(arg_iter, &it);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (dbus_message_iter_get_arg_type(&it) != DBUS_TYPE_INVALID) {
 | 
				
			||||||
 | 
							const char *interface_name;
 | 
				
			||||||
 | 
							const struct spa_dbus_interface *iface;
 | 
				
			||||||
 | 
							struct spa_list *object_list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							dbus_message_iter_get_basic(&it, &interface_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							iface = interface_find(impl, interface_name, &object_list);
 | 
				
			||||||
 | 
							if (iface) {
 | 
				
			||||||
 | 
								struct object *o;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								spa_log_debug(impl->log, "dbus: removed path=%s, interface=%s",
 | 
				
			||||||
 | 
										object_path, interface_name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								o = object_find(object_list, object_path);
 | 
				
			||||||
 | 
								if (o)
 | 
				
			||||||
 | 
									object_destroy(o);
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								spa_log_trace(impl->log, "dbus: skip removed path=%s, interface=%s",
 | 
				
			||||||
 | 
										object_path, interface_name);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							dbus_message_iter_next(&it);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void get_managed_objects_reply(DBusPendingCall *pending, void *user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct impl *impl = user_data;
 | 
				
			||||||
 | 
						DBusMessage *r;
 | 
				
			||||||
 | 
						DBusMessageIter it[6];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_assert(pending == impl->get_managed_objects_call);
 | 
				
			||||||
 | 
						impl->get_managed_objects_call = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						r = dbus_pending_call_steal_reply(pending);
 | 
				
			||||||
 | 
						dbus_pending_call_unref(pending);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (r == NULL)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) {
 | 
				
			||||||
 | 
							spa_log_warn(impl->log, "BlueZ D-Bus ObjectManager not available");
 | 
				
			||||||
 | 
							goto done;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
 | 
				
			||||||
 | 
							spa_log_error(impl->log, "GetManagedObjects() failed: %s",
 | 
				
			||||||
 | 
									dbus_message_get_error_name(r));
 | 
				
			||||||
 | 
							goto done;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!dbus_message_iter_init(r, &it[0]) ||
 | 
				
			||||||
 | 
								!spa_streq(dbus_message_get_signature(r), "a{oa{sa{sv}}}")) {
 | 
				
			||||||
 | 
							spa_log_error(impl->log, "Invalid reply signature for GetManagedObjects()");
 | 
				
			||||||
 | 
							goto done;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Add fake object representing the service itself */
 | 
				
			||||||
 | 
						interface_added(impl, impl->this.path, SPA_DBUS_MONITOR_NAME_OWNER_INTERFACE, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_message_iter_recurse(&it[0], &it[1]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (dbus_message_iter_get_arg_type(&it[1]) != DBUS_TYPE_INVALID) {
 | 
				
			||||||
 | 
							dbus_message_iter_recurse(&it[1], &it[2]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							interfaces_added(impl, &it[2]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							dbus_message_iter_next(&it[1]);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						impl->objects_listed = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					done:
 | 
				
			||||||
 | 
						dbus_message_unref(r);
 | 
				
			||||||
 | 
						return;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int get_managed_objects(struct impl *impl)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						DBusMessage *m = NULL;
 | 
				
			||||||
 | 
						DBusPendingCall *call;
 | 
				
			||||||
 | 
						int res = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (impl->objects_listed || impl->get_managed_objects_call)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						m = dbus_message_new_method_call(impl->this.service,
 | 
				
			||||||
 | 
								impl->this.path,
 | 
				
			||||||
 | 
								DBUS_INTERFACE_OBJECT_MANAGER,
 | 
				
			||||||
 | 
								"GetManagedObjects");
 | 
				
			||||||
 | 
						if (m == NULL)
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_message_set_auto_start(m, false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!dbus_connection_send_with_reply(impl->conn, m, &call, -1)) {
 | 
				
			||||||
 | 
							res = -EIO;
 | 
				
			||||||
 | 
							goto done;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_pending_call_set_notify(call, get_managed_objects_reply, impl, NULL);
 | 
				
			||||||
 | 
						impl->get_managed_objects_call = call;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					done:
 | 
				
			||||||
 | 
						if (m)
 | 
				
			||||||
 | 
							dbus_message_unref(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return res;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct impl *impl = user_data;
 | 
				
			||||||
 | 
						DBusError err;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_error_init(&err);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (dbus_message_is_signal(m, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
 | 
				
			||||||
 | 
							const char *name, *old_owner, *new_owner;
 | 
				
			||||||
 | 
							bool has_old_owner, has_new_owner;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							spa_log_debug(impl->log, "dbus: name owner changed %s", dbus_message_get_path(m));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!dbus_message_get_args(m, &err,
 | 
				
			||||||
 | 
											DBUS_TYPE_STRING, &name,
 | 
				
			||||||
 | 
											DBUS_TYPE_STRING, &old_owner,
 | 
				
			||||||
 | 
											DBUS_TYPE_STRING, &new_owner,
 | 
				
			||||||
 | 
											DBUS_TYPE_INVALID)) {
 | 
				
			||||||
 | 
								spa_log_error(impl->log,
 | 
				
			||||||
 | 
										"Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s",
 | 
				
			||||||
 | 
										err.message);
 | 
				
			||||||
 | 
								goto done;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!spa_streq(name, impl->this.service))
 | 
				
			||||||
 | 
								goto done;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							has_old_owner = old_owner && *old_owner;
 | 
				
			||||||
 | 
							has_new_owner = new_owner && *new_owner;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (has_old_owner)
 | 
				
			||||||
 | 
								spa_log_debug(impl->log, "dbus: %s disappeared", impl->this.service);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (has_old_owner || has_new_owner) {
 | 
				
			||||||
 | 
								size_t i;
 | 
				
			||||||
 | 
								const struct spa_dbus_interface *iface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								impl->objects_listed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								for (iface = impl->this.interfaces, i = 0; iface && iface->name; ++iface, ++i) {
 | 
				
			||||||
 | 
									struct spa_dbus_object *object;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									spa_list_consume(object, &impl->objects[i], link) {
 | 
				
			||||||
 | 
										struct object *o = SPA_CONTAINER_OF(object, struct object, this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
										object_destroy(o);
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (has_new_owner) {
 | 
				
			||||||
 | 
								spa_log_debug(impl->log, "dbus: %s appeared", impl->this.service);
 | 
				
			||||||
 | 
								get_managed_objects(impl);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if (dbus_message_is_signal(m, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesAdded")) {
 | 
				
			||||||
 | 
							DBusMessageIter it;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							spa_log_debug(impl->log, "dbus: interfaces added on path=%s", dbus_message_get_path(m));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!impl->objects_listed)
 | 
				
			||||||
 | 
								goto done;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!dbus_message_iter_init(m, &it) || !spa_streq(dbus_message_get_signature(m), "oa{sa{sv}}")) {
 | 
				
			||||||
 | 
								spa_log_error(impl->log, "Invalid signature found in InterfacesAdded");
 | 
				
			||||||
 | 
								goto done;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							interfaces_added(impl, &it);
 | 
				
			||||||
 | 
						} else if (dbus_message_is_signal(m, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesRemoved")) {
 | 
				
			||||||
 | 
							DBusMessageIter it;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							spa_log_debug(impl->log, "dbus: interfaces removed on path=%s", dbus_message_get_path(m));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!impl->objects_listed)
 | 
				
			||||||
 | 
								goto done;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!dbus_message_iter_init(m, &it) || !spa_streq(dbus_message_get_signature(m), "oas")) {
 | 
				
			||||||
 | 
								spa_log_error(impl->log, "Invalid signature found in InterfacesRemoved");
 | 
				
			||||||
 | 
								goto done;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							interfaces_removed(impl, &it);
 | 
				
			||||||
 | 
						} else if (dbus_message_is_signal(m, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged")) {
 | 
				
			||||||
 | 
							DBusMessageIter args, props, invalidated;
 | 
				
			||||||
 | 
							const char *interface_name, *path;
 | 
				
			||||||
 | 
							const struct spa_dbus_interface *iface;
 | 
				
			||||||
 | 
							struct spa_list *object_list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!impl->objects_listed)
 | 
				
			||||||
 | 
								goto done;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (!dbus_message_iter_init(m, &args) ||
 | 
				
			||||||
 | 
									!spa_streq(dbus_message_get_signature(m), "sa{sv}as")) {
 | 
				
			||||||
 | 
								spa_log_error(impl->log, "Invalid signature found in PropertiesChanged");
 | 
				
			||||||
 | 
								goto done;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							path = dbus_message_get_path(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							dbus_message_iter_get_basic(&args, &interface_name);
 | 
				
			||||||
 | 
							dbus_message_iter_next(&args);
 | 
				
			||||||
 | 
							dbus_message_iter_recurse(&args, &props);
 | 
				
			||||||
 | 
							dbus_message_iter_next(&args);
 | 
				
			||||||
 | 
							dbus_message_iter_recurse(&args, &invalidated);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							iface = interface_find(impl, interface_name, &object_list);
 | 
				
			||||||
 | 
							if (iface) {
 | 
				
			||||||
 | 
								struct object *o;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								o = object_find(object_list, path);
 | 
				
			||||||
 | 
								if (o == NULL) {
 | 
				
			||||||
 | 
									spa_log_debug(impl->log, "Properties changed in unknown object %s",
 | 
				
			||||||
 | 
											path);
 | 
				
			||||||
 | 
									goto done;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								spa_log_debug(impl->log, "dbus: properties changed in path=%s", path);
 | 
				
			||||||
 | 
								object_update_props(impl, o, &props, &invalidated);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					done:
 | 
				
			||||||
 | 
						dbus_error_free(&err);
 | 
				
			||||||
 | 
						return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int add_filters(struct impl *impl)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						DBusError err;
 | 
				
			||||||
 | 
						char rule[1024];
 | 
				
			||||||
 | 
						const struct spa_dbus_interface *iface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (impl->filters_added)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!dbus_connection_add_filter(impl->conn, filter_cb, impl, NULL)) {
 | 
				
			||||||
 | 
							spa_log_error(impl->log, "failed to add DBus filter");
 | 
				
			||||||
 | 
							return -EIO;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						impl->filters_added = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_error_init(&err);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_scnprintf(rule, sizeof(rule),
 | 
				
			||||||
 | 
								"type='signal',sender='org.freedesktop.DBus',"
 | 
				
			||||||
 | 
								"interface='org.freedesktop.DBus',member='NameOwnerChanged',"
 | 
				
			||||||
 | 
								"arg0='%s'",
 | 
				
			||||||
 | 
								impl->this.service);
 | 
				
			||||||
 | 
						spa_log_trace(impl->log, "add match: %s", rule);
 | 
				
			||||||
 | 
						dbus_bus_add_match(impl->conn, rule, &err);
 | 
				
			||||||
 | 
						if (dbus_error_is_set(&err))
 | 
				
			||||||
 | 
							goto fail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_scnprintf(rule, sizeof(rule),
 | 
				
			||||||
 | 
								"type='signal',sender='%s',"
 | 
				
			||||||
 | 
								"interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded'",
 | 
				
			||||||
 | 
								impl->this.service);
 | 
				
			||||||
 | 
						spa_log_trace(impl->log, "add match: %s", rule);
 | 
				
			||||||
 | 
						dbus_bus_add_match(impl->conn, rule, &err);
 | 
				
			||||||
 | 
						if (dbus_error_is_set(&err))
 | 
				
			||||||
 | 
							goto fail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_scnprintf(rule, sizeof(rule),
 | 
				
			||||||
 | 
								"type='signal',sender='%s',"
 | 
				
			||||||
 | 
								"interface='org.freedesktop.DBus.ObjectManager',member='InterfacesRemoved'",
 | 
				
			||||||
 | 
								impl->this.service);
 | 
				
			||||||
 | 
						spa_log_trace(impl->log, "add match: %s", rule);
 | 
				
			||||||
 | 
						dbus_bus_add_match(impl->conn, rule, &err);
 | 
				
			||||||
 | 
						if (dbus_error_is_set(&err))
 | 
				
			||||||
 | 
							goto fail;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (iface = impl->this.interfaces; iface && iface->name; ++iface) {
 | 
				
			||||||
 | 
							spa_scnprintf(rule, sizeof(rule),
 | 
				
			||||||
 | 
									"type='signal',sender='%s',"
 | 
				
			||||||
 | 
									"interface='org.freedesktop.DBus.Properties',member='PropertiesChanged',"
 | 
				
			||||||
 | 
									"arg0='%s'",
 | 
				
			||||||
 | 
									impl->this.service, iface->name);
 | 
				
			||||||
 | 
							spa_log_trace(impl->log, "add match: %s", rule);
 | 
				
			||||||
 | 
							dbus_bus_add_match(impl->conn, rule, &err);
 | 
				
			||||||
 | 
							if (dbus_error_is_set(&err))
 | 
				
			||||||
 | 
								goto fail;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_error_free(&err);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					fail:
 | 
				
			||||||
 | 
						spa_log_error(impl->log, "failed to add DBus match: %s", err.message);
 | 
				
			||||||
 | 
						dbus_error_free(&err);
 | 
				
			||||||
 | 
						return -EIO;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct spa_dbus_monitor *spa_dbus_monitor_new(DBusConnection *conn,
 | 
				
			||||||
 | 
							const char *service,
 | 
				
			||||||
 | 
							const char *path,
 | 
				
			||||||
 | 
							const struct spa_dbus_interface *interfaces,
 | 
				
			||||||
 | 
							struct spa_log *log,
 | 
				
			||||||
 | 
							void *user_data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct impl *impl;
 | 
				
			||||||
 | 
						const struct spa_dbus_interface *iface;
 | 
				
			||||||
 | 
						size_t num_interfaces = 0, i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (iface = interfaces; iface && iface->name; ++iface) {
 | 
				
			||||||
 | 
							spa_assert(iface->object_size >= sizeof(struct spa_dbus_object));
 | 
				
			||||||
 | 
							++num_interfaces;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						impl = calloc(1, sizeof(struct impl) + sizeof(struct spa_list) * num_interfaces);
 | 
				
			||||||
 | 
						if (impl == NULL)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_connection_ref(conn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						impl->conn = conn;
 | 
				
			||||||
 | 
						impl->this.service = strdup(service);
 | 
				
			||||||
 | 
						impl->this.path = strdup(path);
 | 
				
			||||||
 | 
						impl->this.interfaces = interfaces;
 | 
				
			||||||
 | 
						impl->this.user_data = user_data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						impl->log = log;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						impl->log_topic = SPA_LOG_TOPIC(0, "spa.bluez5.dbus");
 | 
				
			||||||
 | 
						spa_log_topic_init(impl->log, &impl->log_topic);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < num_interfaces; ++i)
 | 
				
			||||||
 | 
							spa_list_init(&impl->objects[i]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_assert(impl->this.service);
 | 
				
			||||||
 | 
						spa_assert(impl->this.path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						add_filters(impl);
 | 
				
			||||||
 | 
						get_managed_objects(impl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &impl->this;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void spa_dbus_monitor_destroy(struct spa_dbus_monitor *this)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
 | 
				
			||||||
 | 
						const struct spa_dbus_interface *iface;
 | 
				
			||||||
 | 
						struct spa_dbus_object *object;
 | 
				
			||||||
 | 
						size_t i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (impl->filters_added) {
 | 
				
			||||||
 | 
							dbus_connection_remove_filter(impl->conn, filter_cb, impl);
 | 
				
			||||||
 | 
							impl->filters_added = false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (impl->get_managed_objects_call) {
 | 
				
			||||||
 | 
							dbus_pending_call_cancel(impl->get_managed_objects_call);
 | 
				
			||||||
 | 
							dbus_pending_call_unref(impl->get_managed_objects_call);
 | 
				
			||||||
 | 
							impl->get_managed_objects_call = NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (iface = impl->this.interfaces, i = 0; iface && iface->name; ++iface, ++i) {
 | 
				
			||||||
 | 
							spa_list_consume(object, &impl->objects[i], link) {
 | 
				
			||||||
 | 
								struct object *o = SPA_CONTAINER_OF(object, struct object, this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								object_destroy(o);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_connection_unref(impl->conn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						free((void *)impl->this.service);
 | 
				
			||||||
 | 
						free((void *)impl->this.path);
 | 
				
			||||||
 | 
						free(impl);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct spa_dbus_object *spa_dbus_monitor_find(struct spa_dbus_monitor *this, const char *path, const char *interface)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
 | 
				
			||||||
 | 
						struct object *o;
 | 
				
			||||||
 | 
						const struct spa_dbus_interface *iface;
 | 
				
			||||||
 | 
						struct spa_list *object_list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (path == NULL)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_assert(interface);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						iface = interface_find(impl, interface, &object_list);
 | 
				
			||||||
 | 
						if (!iface)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						o = object_find(object_list, path);
 | 
				
			||||||
 | 
						if (o)
 | 
				
			||||||
 | 
							return &o->this;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void spa_dbus_monitor_ignore_object(struct spa_dbus_monitor *this, struct spa_dbus_object *object)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
 | 
				
			||||||
 | 
						struct object *o = SPA_CONTAINER_OF(object, struct object, this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_log_trace(impl->log, "ignore path=%s", o->this.path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (o->updating)
 | 
				
			||||||
 | 
							object_remove(o);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							object_destroy(o);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct spa_list *spa_dbus_monitor_object_list(struct spa_dbus_monitor *this, const char *interface)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct impl *impl = SPA_CONTAINER_OF(this, struct impl, this);
 | 
				
			||||||
 | 
						const struct spa_dbus_interface *iface;
 | 
				
			||||||
 | 
						struct spa_list *object_list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						iface = interface_find(impl, interface, &object_list);
 | 
				
			||||||
 | 
						if (!iface)
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return object_list;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										139
									
								
								spa/plugins/bluez5/dbus-monitor.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								spa/plugins/bluez5/dbus-monitor.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,139 @@
 | 
				
			||||||
 | 
					/* Spa dbus
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright © 2022 Pauli Virtanen
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
				
			||||||
 | 
					 * copy of this software and associated documentation files (the "Software"),
 | 
				
			||||||
 | 
					 * to deal in the Software without restriction, including without limitation
 | 
				
			||||||
 | 
					 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
				
			||||||
 | 
					 * and/or sell copies of the Software, and to permit persons to whom the
 | 
				
			||||||
 | 
					 * Software is furnished to do so, subject to the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice (including the next
 | 
				
			||||||
 | 
					 * paragraph) shall be included in all copies or substantial portions of the
 | 
				
			||||||
 | 
					 * Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
				
			||||||
 | 
					 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
				
			||||||
 | 
					 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 | 
				
			||||||
 | 
					 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
				
			||||||
 | 
					 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
				
			||||||
 | 
					 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
				
			||||||
 | 
					 * DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef SPA_DBUS_MONITOR_H_
 | 
				
			||||||
 | 
					#define SPA_DBUS_MONITOR_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <dbus/dbus.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <spa/utils/list.h>
 | 
				
			||||||
 | 
					#include <spa/support/log.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct spa_dbus_object;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * DBus interface specification.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct spa_dbus_interface {
 | 
				
			||||||
 | 
						/** DBus interface name. */
 | 
				
			||||||
 | 
						const char *name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Size of object struct. Must be at least sizeof(struct spa_dbus_object). */
 | 
				
			||||||
 | 
						const size_t object_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Property value updated. */
 | 
				
			||||||
 | 
						void (*property) (struct spa_dbus_object *object, const char *key, DBusMessageIter *value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Interface at the object path added, or property updates complete. */
 | 
				
			||||||
 | 
						void (*update) (struct spa_dbus_object *object);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Interface at the object path removed.
 | 
				
			||||||
 | 
						 * The object will be deallocated after this, so any associated data,
 | 
				
			||||||
 | 
						 * for example in a custom object struct, can be freed in this hook.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						void (*remove) (struct spa_dbus_object *object);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * DBus object instance, for one interface.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * A custom object struct can be also used for each interface, specified
 | 
				
			||||||
 | 
					 * as
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *     struct my_dbus_object {
 | 
				
			||||||
 | 
					 *         struct spa_dbus_object object;
 | 
				
			||||||
 | 
					 *         int some_extra_value;
 | 
				
			||||||
 | 
					 *     }
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The struct will be zero-initialized when first allocated. The object
 | 
				
			||||||
 | 
					 * instances are owned by spa_dbus_monitor and allocated and freed by it.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct spa_dbus_object
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct spa_list link;	/**< Link in interface's object list */
 | 
				
			||||||
 | 
						const char *path;	/**< DBus object path */
 | 
				
			||||||
 | 
						const struct spa_dbus_interface *interface;	/**< The interface of the object */
 | 
				
			||||||
 | 
						void *user_data;	/**< Pointer passed in spa_dbus_monitor_new */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** DBus object monitor */
 | 
				
			||||||
 | 
					struct spa_dbus_monitor
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const char *service;	/**< Service name */
 | 
				
			||||||
 | 
						const char *path;	/**< Object path */
 | 
				
			||||||
 | 
						const struct spa_dbus_interface *interfaces;	/**< Monitored interfaces
 | 
				
			||||||
 | 
												 * (zero-terminated) */
 | 
				
			||||||
 | 
						void *user_data;	/**< Pointer passed in spa_dbus_monitor_new */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Non-DBus interface type representing the monitored service itself.
 | 
				
			||||||
 | 
					 * Can be used to track NameOwner events.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#define SPA_DBUS_MONITOR_NAME_OWNER_INTERFACE	"org.freedesktop.pipewire.spa.dbus.monitor.owner"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Create new object monitor.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * \param conn DBus connection
 | 
				
			||||||
 | 
					 * \param service DBus service name to monitor
 | 
				
			||||||
 | 
					 * \param path Object path to monitor
 | 
				
			||||||
 | 
					 * \param interfaces Zero-terminated array of interfaces to monitor. Corresponding objects
 | 
				
			||||||
 | 
					 *                   will be created.
 | 
				
			||||||
 | 
					 * \param log Log to output to
 | 
				
			||||||
 | 
					 * \param user_data User data to set in object instances.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct spa_dbus_monitor *spa_dbus_monitor_new(DBusConnection *conn,
 | 
				
			||||||
 | 
							const char *service,
 | 
				
			||||||
 | 
							const char *path,
 | 
				
			||||||
 | 
							const struct spa_dbus_interface *interfaces,
 | 
				
			||||||
 | 
							struct spa_log *log,
 | 
				
			||||||
 | 
							void *user_data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Destroy object monitor and all interface objects owned by it.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void spa_dbus_monitor_destroy(struct spa_dbus_monitor *monitor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Find interface object by name */
 | 
				
			||||||
 | 
					struct spa_dbus_object *spa_dbus_monitor_find(struct spa_dbus_monitor *monitor,
 | 
				
			||||||
 | 
							const char *path, const char *interface);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Destroy an object, and don't receive further updates concerning it. */
 | 
				
			||||||
 | 
					void spa_dbus_monitor_ignore_object(struct spa_dbus_monitor *monitor,
 | 
				
			||||||
 | 
							struct spa_dbus_object *object);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * Get the object list for the named interface.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The returned list is not mutable.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * \param monitor The DBus monitor.
 | 
				
			||||||
 | 
					 * \param interface Interface name. Must be one of those monitored.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					struct spa_list *spa_dbus_monitor_object_list(struct spa_dbus_monitor *monitor,
 | 
				
			||||||
 | 
							const char *interface);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
| 
						 | 
					@ -30,6 +30,7 @@ bluez5_sources = [
 | 
				
			||||||
  'bluez5-dbus.c',
 | 
					  'bluez5-dbus.c',
 | 
				
			||||||
  'hci.c',
 | 
					  'hci.c',
 | 
				
			||||||
  'dbus-manager.c',
 | 
					  'dbus-manager.c',
 | 
				
			||||||
 | 
					  'dbus-monitor.c',
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bluez5_data = ['bluez-hardware.conf']
 | 
					bluez5_data = ['bluez-hardware.conf']
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue