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.
This commit is contained in:
Peter Hutterer 2018-01-24 10:52:49 +10:00
parent cce63d267b
commit cfa4aca2df
2 changed files with 78 additions and 10 deletions

View File

@ -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

View File

@ -25,6 +25,10 @@ INTROSPECTION_XML = """
<annotation name='org.freedesktop.DBus.Property.EmitsChangedSignal' value='true'/>
</property>
<property type='ao' name='Searching' access='read'>
<annotation name='org.freedesktop.DBus.Property.EmitsChangedSignal' value='true'/>
</property>
<method name='StartSearch'>
<annotation name='org.freedesktop.DBus.Method.NoReply' value='true'/>
</method>
@ -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))