Applet Dbus service unavailable when turning off multiple plugins at the same time
This happens (for me) when multiple plugins are disabled in quick succession due to one being a dependency. I can trigger below when disabling PowerManager which also disables KillSwitch.
Not sure what the best approach is but we need to fix eventually.
Traceback (most recent call last):
File "/home/sander/repos/blueman/blueman/main/Manager.py", line 107, in on_applet_signal
if "PowerManager" in self.Applet.QueryPlugins():
~~~~~~~~~~~~~~~~~~~~~~~~^^
File "/usr/lib/python3.13/site-packages/gi/overrides/Gio.py", line 380, in __call__
result = self.dbus_proxy.call_sync(self.method_name, arg_variant,
flags, timeout, None)
gi.repository.GLib.GError: g-dbus-error-quark: GDBus.Error:org.freedesktop.DBus.Error.UnknownMethod: No such interface “org.blueman.Applet” on object at path /org/blueman/Applet (19)
I think this is eventually due to a GDBus limitation: To remove methods and signals (those provided by PowerManager in that case), we remove the object and re-register it with the new (reduced) DBusInterfaceInfo. Unfortunately, there does not seem to be a way to modify that data for introspection without that step, even though it should just be internal state purely used for handling introspection calls.
I was thinking to add the plugin name and it's state in the signal signature like below. Then anything interested can check and act accordingly to the state of the plugin.
diff --git a/blueman/plugins/applet/DBusService.py b/blueman/plugins/applet/DBusService.py
index e3ed45899..2bba74b2c 100644
--- a/blueman/plugins/applet/DBusService.py
+++ b/blueman/plugins/applet/DBusService.py
@@ -10,6 +10,7 @@ from blueman.Service import Service
from blueman.bluez.errors import BluezDBusException
if TYPE_CHECKING:
from blueman.main.NetworkManager import NMConnectionError
+ from blueman.main.PluginManager import PluginManager
from blueman.plugins.AppletPlugin import AppletPlugin
from blueman.bluez.Device import Device
from blueman.services.Functions import get_service
@@ -56,12 +57,12 @@ class DBusService(AppletPlugin):
self._add_dbus_method("DisconnectService", ("o", "s", "d"), "", self._disconnect_service, is_async=True)
self._add_dbus_method("OpenPluginDialog", (), "", self._open_plugin_dialog)
- self._add_dbus_signal("PluginsChanged", "")
- self.parent.Plugins.connect("plugin-loaded", lambda *args: self._plugins_changed())
- self.parent.Plugins.connect("plugin-unloaded", lambda *args: self._plugins_changed())
+ self._add_dbus_signal("PluginsChanged", "sb")
+ self.parent.Plugins.connect("plugin-loaded", self._plugins_changed, "loaded")
+ self.parent.Plugins.connect("plugin-unloaded", self._plugins_changed, "unloaded")
- def _plugins_changed(self) -> None:
- self._emit_dbus_signal("PluginsChanged")
+ def _plugins_changed(self, _pluginmanager: "PluginManager", name, action) -> None:
+ self._emit_dbus_signal("PluginsChanged", name, action == "loaded")
def connect_service(self, object_path: ObjectPath, uuid: str, ok: Callable[[], None],
err: Callable[[Union[BluezDBusException, "NMConnectionError",
You mean to avoid the QueryPlugins call at that point? Well, I'd expect dozens of possible glitches due to the object re-registration and resulting unavailability. Not sure if it's worth fixing exactly that one.
Ideally, the D-Bus object would not vanish.
Now I'm wondering... is it even necessary to add those signals and methods to introspection, i.e., does our caller side need to find them there?
You mean to avoid the
QueryPluginscall at that point? Well, I'd expect dozens of possible glitches due to the object re-registration and resulting unavailability. Not sure if it's worth fixing exactly that one.
That was my initial intention and you're right to question it. I do think having PluginsChanged provide the additional information would make things better in that we don't have to go and figure out what changed, it will just tell us. But that is not the solution for this.
Ideally, the D-Bus object would not vanish.
Having had this issue in the back of my mind for a few days I think we should have PowerManager (or any plugin in the future with dbus methods/signals) provide an interface under the name org.blueman.Applet with a unique interface name, like org.blueman.PowerManager or org.blueman.Applet.PowerManager. If I'm understanding dbus correctly, registering a new object like this triggers the InterfaceAdded signal, which we can then use in for example main.Manager to construct a DBusProxy and show the toggle. Like this we can always keep the main interface org.blueman.Applet available. Unloading the plugin will unregister the object and the InterfaceRemoved signal is sent and, using the same example, we tear down the proxy and hide the toggle.
Hopefully I have some time this weekend to see if this is actually a good idea :-).
And I couldn't sleep so here is how I imagined this roughly looks like https://github.com/blueman-project/blueman/commit/0340329e47d98db8b867bb4346f56c87c4a92524. I haven't added an ObjectManager to listen for the interface signals.
Which then exposes the following interfaces under /org/blueman/Applet.