From cfa4aca2df54435751c99759378a645f0d2cf150 Mon Sep 17 00:00:00 2001 From: Peter Hutterer Date: Wed, 24 Jan 2018 10:52:49 +1000 Subject: [PATCH] dbus: limit StartSearch to one client only Basically copied from the device's Listening approach. While it's possible to have multiple clients searching at the same time it's a niche case and the effort at fixing the race conditions that come from that is likely not worth the effort. Let's add multiple simultaneous clients when we have a real need for it. --- README.md | 25 +++++++++++++++++- tuhi/dbusserver.py | 63 +++++++++++++++++++++++++++++++++++++++------- 2 files changed, 78 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index a1db5f7..1cbdd74 100644 --- a/README.md +++ b/README.md @@ -52,11 +52,28 @@ org.freedesktop.tuhi1.Manager initialization is independent of the Bluetooth pairing process. A Tuhi paired device may or may not be paired over Bluetooth. + Property: Searching (b) + Indicates whether the daemon is currently searching for pairable devices. + + This property is set to True when a StartSearching() request initiates + the search for device connections. When the StartSearching() request + completes upon timeout, or when StopSearching() is called, the property + is set to False. + + When a pariable device is found, the PairableDevice signal is sent to + the caller that initiated the search process. + + Read-only + Method: StartSearch() -> () Start searching for available devices in pairing mode for an unspecified timeout. When the timeout expires or an error occurs, a SearchStopped signal is sent indicating success or error. + If a client that successfully initated a listening process calls + StartSearching() again, that call is ignored and no signal is + generated for that call. + Method: StopSearch() -> () Stop listening to available devices in pairing mode. If called after StartSearch() and before a SearchStopped signal has been received, @@ -72,7 +89,8 @@ org.freedesktop.tuhi1.Manager Signal: PairableDevice(o) Indicates that a device is available for pairing. This signal may be sent after a StartSearch() call and before SearchStopped(). This - signal is sent once per available device. + signal is sent once per available device and only to the client that + initiated the search process with StartSearch. When this signal is sent, a org.freedesktop.tuhi1.Device object was created, the object path is the argument to this signal. @@ -90,6 +108,11 @@ org.freedesktop.tuhi1.Manager successful termination of the search process, either when a device has been paired or the timeout expired. + If the errno is -EAGAIN, the daemon is already searching for devices + on behalf of another client. In this case, this client should wait for + the Searching property to change and StartSearching() once the + property is set to False. + Once this signal has been sent, all devices announced through PairableDevice signals should be considered invalidated. Attempting to Pair() one of the devices after the SearchStopped() signal may result diff --git a/tuhi/dbusserver.py b/tuhi/dbusserver.py index e1e8c80..a7a737b 100755 --- a/tuhi/dbusserver.py +++ b/tuhi/dbusserver.py @@ -25,6 +25,10 @@ INTROSPECTION_XML = """ + + + + @@ -332,6 +336,34 @@ class TuhiDBusServer(GObject.Object): self._is_searching = False self._searching_client = None + @GObject.Property + def is_searching(self): + return self._is_searching + + @is_searching.setter + def is_searching(self, value): + if self._is_searching == value: + return + + self._is_searching = value + + props = GLib.VariantBuilder(GLib.VariantType('a{sv}')) + de = GLib.Variant.new_dict_entry(GLib.Variant.new_string('Searching'), + GLib.Variant.new_variant( + GLib.Variant.new_boolean(value))) + props.add_value(de) + props = props.end() + inval_props = GLib.VariantBuilder(GLib.VariantType('as')) + inval_props = inval_props.end() + + self._connection.emit_signal(None, self.objpath, + "org.freedesktop.DBus.Properties", + "PropertiesChanged", + GLib.Variant.new_tuple( + GLib.Variant.new_string(INTF_MANAGER), + props, + inval_props)) + def _bus_aquired(self, connection, name): introspection = Gio.DBusNodeInfo.new_for_xml(INTROSPECTION_XML) intf = introspection.lookup_interface(INTF_MANAGER) @@ -342,6 +374,7 @@ class TuhiDBusServer(GObject.Object): self._property_read_cb, self._property_write_cb) self._connection = connection + self.objpath = BASE_PATH def _bus_name_aquired(self, connection, name): logger.debug('Bus name aquired') @@ -367,6 +400,8 @@ class TuhiDBusServer(GObject.Object): if propname == 'Devices': return GLib.Variant.new_objv([d.objpath for d in self._devices if d.paired]) + elif propname == 'Searching': + return GLib.Variant.new_boolean(self.is_searching) return None @@ -374,11 +409,19 @@ class TuhiDBusServer(GObject.Object): pass def _start_search(self, connection, sender): - # FIXME: need to handle multiple clients here - if self._is_searching: + if self.is_searching: + logger.debug("Already searching") + + # silently ignore it for the current client but send EAGAIN to + # other clients + if sender != self._searching_client[0]: + status = GLib.Variant.new_int32(-errno.EAGAIN) + status = GLib.Variant.new_tuple(status) + connection.emit_signal(sender, self.objpath, INTF_MANAGER, + "SearchStopped", status) return - self._is_searching = True + self.is_searching = True s = connection.signal_subscribe(sender='org.freedesktop.DBus', interface_name='org.freedesktop.DBus', @@ -405,12 +448,12 @@ class TuhiDBusServer(GObject.Object): self._stop_search(connection, user_data) def _stop_search(self, connection, sender): - # FIXME: need to handle multiple clients here - if not self._is_searching or sender != self._searching_client[0]: + if not self.is_searching or sender != self._searching_client[0]: return connection.signal_unsubscribe(self._searching_client[1]) - self._is_searching = False + self.is_searching = False + self._searching_client = None self.emit("search-stop-requested") def _on_search_stop(self, status): @@ -418,10 +461,11 @@ class TuhiDBusServer(GObject.Object): Called by whoever handles the search-start-requested signal """ logger.debug("Search has stopped") - self._is_searching = False + self.is_searching = False status = GLib.Variant.new_int32(status) status = GLib.Variant.new_tuple(status) - self._connection.emit_signal(None, BASE_PATH, INTF_MANAGER, + self._connection.emit_signal(self._searching_client[0], + BASE_PATH, INTF_MANAGER, "SearchStopped", status) self._searching_client = None @@ -466,6 +510,7 @@ class TuhiDBusServer(GObject.Object): def _emit_pairable_signal(self, device): arg = GLib.Variant.new_object_path(device.objpath) - self._connection.emit_signal(None, BASE_PATH, INTF_MANAGER, + self._connection.emit_signal(self._searching_client[0], + BASE_PATH, INTF_MANAGER, "PairableDevice", GLib.Variant.new_tuple(arg))