From: Vibhav Pant vibhavp@gmail.com
The unix bluetooth watcher uses a message filter on the DBus connection to listen for "InterfacesAdded" signals on DBus. If such a signal is received for an object, and the list of interfaces includes org.bluez.Adapter1, queue a BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED event. --- dlls/winebth.sys/dbus.c | 228 ++++++++++++++++++++++++++++++++ dlls/winebth.sys/unixlib.c | 3 + dlls/winebth.sys/unixlib_priv.h | 1 + 3 files changed, 232 insertions(+)
diff --git a/dlls/winebth.sys/dbus.c b/dlls/winebth.sys/dbus.c index 2a395d13e5e..5b708caa427 100644 --- a/dlls/winebth.sys/dbus.c +++ b/dlls/winebth.sys/dbus.c @@ -58,6 +58,7 @@ WINE_DECLARE_DEBUG_CHANNEL( dbus ); const int bluez_timeout = -1;
#define DBUS_INTERFACE_OBJECTMANAGER "org.freedesktop.DBus.ObjectManager" +#define DBUS_OBJECTMANAGER_SIGNAL_INTERFACESADDED "InterfacesAdded"
#define DBUS_INTERFACES_ADDED_SIGNATURE \ DBUS_TYPE_OBJECT_PATH_AS_STRING \ @@ -101,6 +102,31 @@ failed: return FALSE; }
+static NTSTATUS bluez_dbus_error_to_ntstatus( const DBusError *error ) +{ + +#define DBUS_ERROR_CASE(n, s) if(p_dbus_error_has_name( error, (n)) ) return (s) + + DBUS_ERROR_CASE( "org.bluez.Error.Failed", STATUS_INTERNAL_ERROR); + DBUS_ERROR_CASE( "org.bluez.Error.NotReady", STATUS_DEVICE_NOT_READY ); + DBUS_ERROR_CASE( "org.bluez.Error.NotAuthorized", STATUS_ACCESS_DENIED ); + DBUS_ERROR_CASE( "org.bluez.Error.InvalidArguments", STATUS_INVALID_PARAMETER ); + DBUS_ERROR_CASE( "org.bluez.Error.AlreadyExists", STATUS_NO_MORE_ENTRIES ); + DBUS_ERROR_CASE( "org.bluez.Error.AuthenticationCanceled", STATUS_CANCELLED ); + DBUS_ERROR_CASE( "org.bluez.Error.AuthenticationFailed", STATUS_INTERNAL_ERROR ); + DBUS_ERROR_CASE( "org.bluez.Error.AuthenticationRejected", STATUS_INTERNAL_ERROR ); + DBUS_ERROR_CASE( "org.bluez.Error.AuthenticationTimeout", STATUS_TIMEOUT ); + DBUS_ERROR_CASE( "org.bluez.Error.ConnectionAttemptFailed", STATUS_DEVICE_NOT_CONNECTED); + DBUS_ERROR_CASE( "org.bluez.Error.NotConnected", STATUS_DEVICE_NOT_CONNECTED ); + DBUS_ERROR_CASE( "org.bluez.Error.InProgress", STATUS_OPERATION_IN_PROGRESS ); + DBUS_ERROR_CASE( DBUS_ERROR_UNKNOWN_OBJECT, STATUS_INVALID_PARAMETER ); + DBUS_ERROR_CASE( DBUS_ERROR_NO_MEMORY, STATUS_NO_MEMORY ); + DBUS_ERROR_CASE( DBUS_ERROR_NOT_SUPPORTED, STATUS_NOT_SUPPORTED ); + DBUS_ERROR_CASE( DBUS_ERROR_ACCESS_DENIED, STATUS_ACCESS_DENIED ); + return STATUS_INTERNAL_ERROR; +#undef DBUS_ERROR_CASE +} + static const char *bluez_next_dict_entry( DBusMessageIter *iter, DBusMessageIter *variant ) { DBusMessageIter sub; @@ -117,6 +143,39 @@ static const char *bluez_next_dict_entry( DBusMessageIter *iter, DBusMessageIter return name; }
+static const char *dbgstr_dbus_message( DBusMessage *message ) +{ + const char *interface; + const char *member; + const char *path; + const char *sender; + const char *signature; + int type; + + interface = p_dbus_message_get_interface( message ); + member = p_dbus_message_get_member( message ); + path = p_dbus_message_get_path( message ); + sender = p_dbus_message_get_sender( message ); + type = p_dbus_message_get_type( message ); + signature = p_dbus_message_get_signature( message ); + + switch (type) + { + case DBUS_MESSAGE_TYPE_METHOD_CALL: + return wine_dbg_sprintf( + "{method_call sender=%s interface=%s member=%s path=%s signature='%s'}", + debugstr_a( sender ), debugstr_a( interface ), debugstr_a( member ), debugstr_a( path ), + debugstr_a( signature ) ); + case DBUS_MESSAGE_TYPE_SIGNAL: + return wine_dbg_sprintf( "{signal sender=%s interface=%s member=%s path=%s signature='%s'}", + debugstr_a( sender ), debugstr_a( interface ), + debugstr_a( member ), debugstr_a( path ), + debugstr_a( signature ) ); + default: + return wine_dbg_sprintf( "%p", message ); + } +} + static inline const char *dbgstr_dbus_connection( DBusConnection *connection ) { return wine_dbg_sprintf( "{%p connected=%d}", connection, @@ -253,6 +312,9 @@ struct bluez_watcher_ctx struct list initial_radio_list; /* struct bluez_init_entry */ struct list initial_device_list; + + /* struct bluez_watcher_event */ + struct list event_list; };
void *bluez_dbus_init( void ) @@ -299,12 +361,119 @@ struct bluez_watcher_event union winebluetooth_watcher_event_data event; };
+static BOOL bluez_event_list_queue_new_event( struct list *event_list, + enum winebluetooth_watcher_event_type event_type, + union winebluetooth_watcher_event_data event ) +{ + struct bluez_watcher_event *event_entry; + + event_entry = calloc(1, sizeof( *event_entry ) ); + if (!event_entry) + { + ERR( "Could not allocate memory for DBus event.\n" ); + return FALSE; + } + + event_entry->event_type = event_type; + event_entry->event = event; + list_add_tail( event_list, &event_entry->entry ); + + return TRUE; +} + +static DBusHandlerResult bluez_filter( DBusConnection *conn, DBusMessage *msg, void *user_data ) +{ + struct list *event_list; + SIZE_T init_count, final_count; + + if (TRACE_ON( dbus )) + { + TRACE_( dbus ) + ( "(%s, %s, %p)\n", dbgstr_dbus_connection( conn ), dbgstr_dbus_message( msg ), user_data ); + } + + event_list = &((struct bluez_watcher_ctx *)user_data)->event_list; + init_count = list_count( event_list ); + + if (p_dbus_message_is_signal( msg, DBUS_INTERFACE_OBJECTMANAGER, DBUS_OBJECTMANAGER_SIGNAL_INTERFACESADDED ) + && p_dbus_message_has_signature( msg, DBUS_INTERFACES_ADDED_SIGNATURE )) + { + DBusMessageIter iter, ifaces_iter; + const char *object_path; + + p_dbus_message_iter_init (msg, &iter); + p_dbus_message_iter_get_basic( &iter, &object_path ); + p_dbus_message_iter_next( &iter ); + p_dbus_message_iter_recurse(&iter, &ifaces_iter); + while (p_dbus_message_iter_has_next( &ifaces_iter )) + { + DBusMessageIter iface_entry; + const char *iface_name; + + p_dbus_message_iter_recurse( &ifaces_iter, &iface_entry ); + p_dbus_message_iter_get_basic( &iface_entry, &iface_name ); + if (strcmp( iface_name, BLUEZ_INTERFACE_ADAPTER ) == 0) + { + struct winebluetooth_watcher_event_radio_added radio_added = {0}; + struct unix_name *radio; + DBusMessageIter props_iter, variant; + const char *prop_name; + + p_dbus_message_iter_next( &iface_entry ); + p_dbus_message_iter_recurse( &iface_entry, &props_iter ); + + while((prop_name = bluez_next_dict_entry( &props_iter, &variant ))) + { + bluez_radio_prop_from_dict_entry( prop_name, &variant, &radio_added.props, + &radio_added.props_mask, + WINEBLUETOOTH_RADIO_ALL_PROPERTIES ); + } + + radio_added.radio.handle = radio = unix_name_get_or_create( object_path ); + if (radio_added.radio.handle == NULL) + { + ERR("failed to allocate memory for adapter path %s\n", object_path); + break; + } + else + { + union winebluetooth_watcher_event_data event = { .radio_added = radio_added }; + TRACE( "New BlueZ %s object added at %s: %p\n", BLUEZ_INTERFACE_ADAPTER, + object_path, radio_added.radio.handle ); + if (!bluez_event_list_queue_new_event( + event_list, BLUETOOTH_WATCHER_EVENT_TYPE_RADIO_ADDED, event )) + { + unix_name_free( radio ); + } + } + } + p_dbus_message_iter_next( &ifaces_iter ); + } + } + + final_count = list_count( event_list ); + assert( final_count >= init_count ); + if (final_count > init_count) + TRACE( "Received %ld new watcher-related event(s) from BlueZ\n", (final_count - init_count) ); + + return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; +} + +static const char BLUEZ_MATCH_OBJECTMANAGER[] = "type='signal'," + "interface='org.freedesktop.DBus.ObjectManager'," + "sender='"BLUEZ_DEST"'," + "path='/'"; + +static const char *BLUEZ_MATCH_RULES[] = { BLUEZ_MATCH_OBJECTMANAGER }; + NTSTATUS bluez_watcher_init( void *connection, void **ctx ) { + DBusError err; NTSTATUS status; DBusPendingCall *call; struct bluez_watcher_ctx *watcher_ctx = calloc( 1, sizeof( struct bluez_watcher_ctx ) ); + SIZE_T i;
if (watcher_ctx == NULL) return STATUS_NO_MEMORY; status = bluez_get_objects_async( connection, &call ); @@ -318,11 +487,46 @@ NTSTATUS bluez_watcher_init( void *connection, void **ctx ) list_init( &watcher_ctx->initial_radio_list ); list_init( &watcher_ctx->initial_device_list );
+ list_init( &watcher_ctx->event_list ); + + if (!p_dbus_connection_add_filter( connection, bluez_filter, watcher_ctx, free )) + { + p_dbus_pending_call_cancel( call ); + p_dbus_pending_call_unref( call ); + free( watcher_ctx ); + ERR( "could not add DBus filter\n" ); + return STATUS_NO_MEMORY; + } + p_dbus_error_init( &err ); + for (i = 0; i < ARRAY_SIZE( BLUEZ_MATCH_RULES ); i++) + { + TRACE( "Adding DBus match rule "%s"\n", BLUEZ_MATCH_RULES[i] ); + + p_dbus_bus_add_match( connection, BLUEZ_MATCH_RULES[i], &err ); + if (p_dbus_error_is_set( &err )) + { + NTSTATUS status = bluez_dbus_error_to_ntstatus( &err ); + ERR( "could not add DBus match "%s": '%s': '%s'\n", BLUEZ_MATCH_RULES[i], err.name, + err.message ); + p_dbus_pending_call_cancel( call ); + p_dbus_pending_call_unref( call ); + p_dbus_error_free( &err ); + free( watcher_ctx ); + return status; + } + } + p_dbus_error_free( &err ); *ctx = watcher_ctx; TRACE( "ctx=%p\n", ctx ); return STATUS_SUCCESS; }
+void bluez_watcher_close( void *connection, void *ctx ) +{ + p_dbus_bus_remove_match( connection, BLUEZ_MATCH_OBJECTMANAGER, NULL ); + p_dbus_connection_remove_filter( connection, bluez_filter, ctx ); +} + struct bluez_init_entry { union { @@ -401,6 +605,17 @@ static struct bluez_init_entry *bluez_init_entries_list_pop( struct list *list ) return device; }
+static struct bluez_watcher_event *bluez_watcher_event_queue_ready( struct bluez_watcher_ctx *ctx ) +{ + struct list *head = list_head( &ctx->event_list ); + struct bluez_watcher_event *event = LIST_ENTRY(head, struct bluez_watcher_event, entry); + TRACE("(%p)\n", ctx); + + list_remove( &event->entry ); + return event; + return NULL; +} + NTSTATUS bluez_dbus_loop( void *c, void *watcher, struct winebluetooth_event_loop_result *result ) { @@ -412,6 +627,8 @@ NTSTATUS bluez_dbus_loop( void *c, void *watcher,
while (TRUE) { + struct bluez_watcher_event *bluez_event; + if (!list_empty( &watcher_ctx->initial_radio_list )) { struct bluez_init_entry *radio = @@ -425,6 +642,17 @@ NTSTATUS bluez_dbus_loop( void *c, void *watcher, p_dbus_connection_unref( connection ); return STATUS_PENDING; } + else if ((bluez_event = bluez_watcher_event_queue_ready( watcher_ctx ))) + { + struct winebluetooth_watcher_event *watcher_event = &result->data.watcher_event; + + result->status = WINEBLUETOOTH_EVENT_LOOP_STATUS_WATCHER_EVENT; + watcher_event->event_type = bluez_event->event_type; + watcher_event->event_data = bluez_event->event; + free( bluez_event ); + p_dbus_connection_unref( connection ); + return STATUS_PENDING; + } else if (!p_dbus_connection_read_write_dispatch( connection, -1 )) { p_dbus_connection_unref( connection ); diff --git a/dlls/winebth.sys/unixlib.c b/dlls/winebth.sys/unixlib.c index 17fedf8137a..d6ae2c37851 100644 --- a/dlls/winebth.sys/unixlib.c +++ b/dlls/winebth.sys/unixlib.c @@ -156,7 +156,10 @@ static NTSTATUS bluetooth_watcher_init( void *args )
static NTSTATUS bluetooth_watcher_close( void *args ) { + struct bluetooth_watcher_close_params *params = args; + if (!dbus_connection) return STATUS_NOT_SUPPORTED; + bluez_watcher_close(dbus_connection, params->unix_watcher_ctx); return STATUS_SUCCESS; }
diff --git a/dlls/winebth.sys/unixlib_priv.h b/dlls/winebth.sys/unixlib_priv.h index cd79db3fd25..9131bd31e34 100644 --- a/dlls/winebth.sys/unixlib_priv.h +++ b/dlls/winebth.sys/unixlib_priv.h @@ -50,5 +50,6 @@ extern void bluez_dbus_close( void *connection ); extern void bluez_dbus_free( void *connection ); extern NTSTATUS bluez_dbus_loop( void *connection, void *watcher_ctx, struct winebluetooth_event_loop_result *result ); extern NTSTATUS bluez_watcher_init( void *connection, void **ctx ); +extern void bluez_watcher_close( void *connection, void *ctx ); #endif /* SONAME_LIBDBUS_1 */ #endif /* __WINE_WINEBTH_UNIXLIB_PRIV_H */