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.
pull/27/head
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 initialization is independent of the Bluetooth pairing process. A Tuhi
paired device may or may not be paired over Bluetooth. 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() -> () Method: StartSearch() -> ()
Start searching for available devices in pairing mode for an Start searching for available devices in pairing mode for an
unspecified timeout. When the timeout expires or an error occurs, a unspecified timeout. When the timeout expires or an error occurs, a
SearchStopped signal is sent indicating success or error. 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() -> () Method: StopSearch() -> ()
Stop listening to available devices in pairing mode. If called after Stop listening to available devices in pairing mode. If called after
StartSearch() and before a SearchStopped signal has been received, StartSearch() and before a SearchStopped signal has been received,
@ -72,7 +89,8 @@ org.freedesktop.tuhi1.Manager
Signal: PairableDevice(o) Signal: PairableDevice(o)
Indicates that a device is available for pairing. This signal may be Indicates that a device is available for pairing. This signal may be
sent after a StartSearch() call and before SearchStopped(). This 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 When this signal is sent, a org.freedesktop.tuhi1.Device object was
created, the object path is the argument to this signal. 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 successful termination of the search process, either when a device
has been paired or the timeout expired. 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 Once this signal has been sent, all devices announced through
PairableDevice signals should be considered invalidated. Attempting to PairableDevice signals should be considered invalidated. Attempting to
Pair() one of the devices after the SearchStopped() signal may result 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'/> <annotation name='org.freedesktop.DBus.Property.EmitsChangedSignal' value='true'/>
</property> </property>
<property type='ao' name='Searching' access='read'>
<annotation name='org.freedesktop.DBus.Property.EmitsChangedSignal' value='true'/>
</property>
<method name='StartSearch'> <method name='StartSearch'>
<annotation name='org.freedesktop.DBus.Method.NoReply' value='true'/> <annotation name='org.freedesktop.DBus.Method.NoReply' value='true'/>
</method> </method>
@ -332,6 +336,34 @@ class TuhiDBusServer(GObject.Object):
self._is_searching = False self._is_searching = False
self._searching_client = None 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): def _bus_aquired(self, connection, name):
introspection = Gio.DBusNodeInfo.new_for_xml(INTROSPECTION_XML) introspection = Gio.DBusNodeInfo.new_for_xml(INTROSPECTION_XML)
intf = introspection.lookup_interface(INTF_MANAGER) intf = introspection.lookup_interface(INTF_MANAGER)
@ -342,6 +374,7 @@ class TuhiDBusServer(GObject.Object):
self._property_read_cb, self._property_read_cb,
self._property_write_cb) self._property_write_cb)
self._connection = connection self._connection = connection
self.objpath = BASE_PATH
def _bus_name_aquired(self, connection, name): def _bus_name_aquired(self, connection, name):
logger.debug('Bus name aquired') logger.debug('Bus name aquired')
@ -367,6 +400,8 @@ class TuhiDBusServer(GObject.Object):
if propname == 'Devices': if propname == 'Devices':
return GLib.Variant.new_objv([d.objpath for d in self._devices if d.paired]) 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 return None
@ -374,11 +409,19 @@ class TuhiDBusServer(GObject.Object):
pass pass
def _start_search(self, connection, sender): 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 return
self._is_searching = True self.is_searching = True
s = connection.signal_subscribe(sender='org.freedesktop.DBus', s = connection.signal_subscribe(sender='org.freedesktop.DBus',
interface_name='org.freedesktop.DBus', interface_name='org.freedesktop.DBus',
@ -405,12 +448,12 @@ class TuhiDBusServer(GObject.Object):
self._stop_search(connection, user_data) self._stop_search(connection, user_data)
def _stop_search(self, connection, sender): 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 return
connection.signal_unsubscribe(self._searching_client[1]) connection.signal_unsubscribe(self._searching_client[1])
self._is_searching = False self.is_searching = False
self._searching_client = None
self.emit("search-stop-requested") self.emit("search-stop-requested")
def _on_search_stop(self, status): def _on_search_stop(self, status):
@ -418,10 +461,11 @@ class TuhiDBusServer(GObject.Object):
Called by whoever handles the search-start-requested signal Called by whoever handles the search-start-requested signal
""" """
logger.debug("Search has stopped") logger.debug("Search has stopped")
self._is_searching = False self.is_searching = False
status = GLib.Variant.new_int32(status) status = GLib.Variant.new_int32(status)
status = GLib.Variant.new_tuple(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) "SearchStopped", status)
self._searching_client = None self._searching_client = None
@ -466,6 +510,7 @@ class TuhiDBusServer(GObject.Object):
def _emit_pairable_signal(self, device): def _emit_pairable_signal(self, device):
arg = GLib.Variant.new_object_path(device.objpath) 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", "PairableDevice",
GLib.Variant.new_tuple(arg)) GLib.Variant.new_tuple(arg))