diff options
author | Bastien Nocera <hadess@hadess.net> | 2022-08-23 12:48:19 +0200 |
---|---|---|
committer | Bastien Nocera <hadess@hadess.net> | 2022-08-23 16:11:08 +0200 |
commit | 2334239e822fe5a0f2838771b7b71df6a93af61a (patch) | |
tree | bdd093859a4097835f61e9b3d1814053c79a67ad | |
parent | da67ec337efbfeee6075e158bd79aad56f00af3c (diff) | |
download | gnome-bluetooth-2334239e822fe5a0f2838771b7b71df6a93af61a.tar.gz |
lib: Export a more precise adapter power state
Unfortunately, powering on/off Bluetooth adapters has become longer,
and less reliable over time, so front-ends need more information to
be able to figure out what it happening.
As a first pass, export whether the adapter is in the process of being
turned on, or turned off, based on our own request.
-rw-r--r-- | lib/bluetooth-client.c | 65 | ||||
-rw-r--r-- | lib/bluetooth-enums.h | 18 | ||||
-rw-r--r-- | lib/gnome-bluetooth.map | 1 | ||||
-rwxr-xr-x | tests/integration-test.py | 8 |
4 files changed, 91 insertions, 1 deletions
diff --git a/lib/bluetooth-client.c b/lib/bluetooth-client.c index 5dc2522a..a39317bb 100644 --- a/lib/bluetooth-client.c +++ b/lib/bluetooth-client.c @@ -54,11 +54,19 @@ #define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter1" #define BLUEZ_DEVICE_INTERFACE "org.bluez.Device1" +/* Subset of BluetoothAdapterState */ +typedef enum { + POWER_STATE_REQUEST_NONE = 0, + POWER_STATE_REQUEST_ON, + POWER_STATE_REQUEST_OFF, +} PowerStateRequest; + struct _BluetoothClient { GObject parent; GListStore *list_store; Adapter1 *default_adapter; + PowerStateRequest power_req; GDBusObjectManager *manager; GCancellable *cancellable; guint num_adapters; @@ -75,6 +83,7 @@ enum { PROP_NUM_ADAPTERS, PROP_DEFAULT_ADAPTER, PROP_DEFAULT_ADAPTER_POWERED, + PROP_DEFAULT_ADAPTER_STATE, PROP_DEFAULT_ADAPTER_SETUP_MODE, PROP_DEFAULT_ADAPTER_NAME, PROP_DEFAULT_ADAPTER_ADDRESS @@ -409,15 +418,31 @@ adapter_set_powered_cb (GDBusProxy *proxy, GAsyncResult *res, gpointer user_data) { + BluetoothClient *client; g_autoptr(GError) error = NULL; g_autoptr(GVariant) ret = NULL; ret = g_dbus_proxy_call_finish (proxy, res, &error); if (!ret) { + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; g_debug ("Error setting property 'Powered' on %s: %s (%s, %d)", g_dbus_proxy_get_object_path (proxy), error->message, g_quark_to_string (error->domain), error->code); } + + client = user_data; + if (client->default_adapter) { + gboolean powered; + + powered = adapter1_get_powered (client->default_adapter); + if ((powered && client->power_req == POWER_STATE_REQUEST_ON) || + (!powered && client->power_req == POWER_STATE_REQUEST_OFF)) { + /* Only reset if we don't have a conflicting state in progress */ + client->power_req = POWER_STATE_REQUEST_NONE; + } + } + g_object_notify (G_OBJECT (client), "default-adapter-state"); } static void @@ -433,6 +458,12 @@ adapter_set_powered (BluetoothClient *client, return; } + if ((powered && client->power_req == POWER_STATE_REQUEST_ON) || + (!powered && client->power_req == POWER_STATE_REQUEST_OFF)) { + g_debug ("Default adapter is already being powered %s", powered ? "on" : "off"); + return; + } + if (powered == adapter1_get_powered (client->default_adapter)) { g_debug ("Default adapter is already %spowered", powered ? "" : "un"); return; @@ -442,6 +473,9 @@ adapter_set_powered (BluetoothClient *client, powered ? "up" : "down", g_dbus_proxy_get_object_path (G_DBUS_PROXY (client->default_adapter))); variant = g_variant_new_boolean (powered); + client->power_req = powered ? + POWER_STATE_REQUEST_ON : POWER_STATE_REQUEST_OFF; + g_object_notify (G_OBJECT (client), "default-adapter-state"); g_dbus_proxy_call (G_DBUS_PROXY (client->default_adapter), "org.freedesktop.DBus.Properties.Set", g_variant_new ("(ssv)", "org.bluez.Adapter1", "Powered", variant), @@ -554,6 +588,7 @@ adapter_notify_cb (Adapter1 *adapter, g_object_notify (G_OBJECT (client), "default-adapter-setup-mode"); } else if (g_strcmp0 (property, "powered") == 0) { g_object_notify (G_OBJECT (client), "default-adapter-powered"); + g_object_notify (G_OBJECT (client), "default-adapter-state"); } } @@ -570,6 +605,7 @@ notify_default_adapter_props (BluetoothClient *client) g_object_notify (G_OBJECT (client), "default-adapter"); g_object_notify (G_OBJECT (client), "default-adapter-address"); g_object_notify (G_OBJECT (client), "default-adapter-powered"); + g_object_notify (G_OBJECT (client), "default-adapter-state"); g_object_notify (G_OBJECT (client), "default-adapter-setup-mode"); g_object_notify (G_OBJECT (client), "default-adapter-name"); } @@ -603,7 +639,6 @@ default_adapter_changed (GDBusObjectManager *manager, g_list_store_remove_all (client->list_store); g_debug ("Disabling discovery on old default adapter"); _bluetooth_client_set_default_adapter_discovering (client, FALSE); - g_clear_object (&client->default_adapter); } client->default_adapter = ADAPTER1 (g_object_ref (G_OBJECT (adapter))); @@ -1210,6 +1245,19 @@ _bluetooth_client_set_default_adapter_discovering (BluetoothClient *client, } } +static BluetoothAdapterState +adapter_get_state (BluetoothClient *client) +{ + if (!client->default_adapter) + return BLUETOOTH_ADAPTER_STATE_ABSENT; + if (client->power_req == POWER_STATE_REQUEST_NONE) + return adapter1_get_powered (client->default_adapter) ? + BLUETOOTH_ADAPTER_STATE_ON : BLUETOOTH_ADAPTER_STATE_OFF; + return client->power_req == POWER_STATE_REQUEST_ON ? + BLUETOOTH_ADAPTER_STATE_TURNING_ON : + BLUETOOTH_ADAPTER_STATE_TURNING_OFF; +} + static void bluetooth_client_get_property (GObject *object, guint property_id, @@ -1230,6 +1278,9 @@ bluetooth_client_get_property (GObject *object, g_value_set_boolean (value, client->default_adapter ? adapter1_get_powered (client->default_adapter) : FALSE); break; + case PROP_DEFAULT_ADAPTER_STATE: + g_value_set_enum (value, adapter_get_state (client)); + break; case PROP_DEFAULT_ADAPTER_NAME: g_value_set_string (value, client->default_adapter ? adapter1_get_alias (client->default_adapter) : NULL); @@ -1364,6 +1415,18 @@ static void bluetooth_client_class_init(BluetoothClientClass *klass) "Whether the default adapter is powered", FALSE, G_PARAM_READWRITE)); /** + * BluetoothClient:default-adapter-state: + * + * The #BluetoothAdapterState of the default Bluetooth adapter. More precise than + * #BluetoothClient:default-adapter-powered. + */ + g_object_class_install_property (object_class, PROP_DEFAULT_ADAPTER_STATE, + g_param_spec_enum ("default-adapter-state", NULL, + "State of the default adapter", + BLUETOOTH_TYPE_ADAPTER_STATE, + BLUETOOTH_ADAPTER_STATE_ABSENT, + G_PARAM_READABLE)); + /** * BluetoothClient:default-adapter-setup-mode: * * %TRUE if the default Bluetooth adapter is in setup mode (discoverable, and discovering). diff --git a/lib/bluetooth-enums.h b/lib/bluetooth-enums.h index 9bbe2dad..3c972d62 100644 --- a/lib/bluetooth-enums.h +++ b/lib/bluetooth-enums.h @@ -112,3 +112,21 @@ typedef enum { BLUETOOTH_BATTERY_TYPE_PERCENTAGE, BLUETOOTH_BATTERY_TYPE_COARSE } BluetoothBatteryType; + +/** + * BluetoothAdapterState: + * @BLUETOOTH_ADAPTER_STATE_ABSENT: Bluetooth adapter is missing. + * @BLUETOOTH_ADAPTER_STATE_ON: Bluetooth adapter is on. + * @BLUETOOTH_ADAPTER_STATE_TURNING_ON: Bluetooth adapter is being turned on. + * @BLUETOOTH_ADAPTER_STATE_TURNING_OFF: Bluetooth adapter is being turned off. + * @BLUETOOTH_ADAPTER_STATE_OFF: Bluetooth adapter is off. + * + * A more precise power state for a Bluetooth adapter. + **/ +typedef enum { + BLUETOOTH_ADAPTER_STATE_ABSENT = 0, + BLUETOOTH_ADAPTER_STATE_ON, + BLUETOOTH_ADAPTER_STATE_TURNING_ON, + BLUETOOTH_ADAPTER_STATE_TURNING_OFF, + BLUETOOTH_ADAPTER_STATE_OFF, +} BluetoothAdapterState; diff --git a/lib/gnome-bluetooth.map b/lib/gnome-bluetooth.map index 26295cba..7febdee1 100644 --- a/lib/gnome-bluetooth.map +++ b/lib/gnome-bluetooth.map @@ -22,6 +22,7 @@ global: bluetooth_device_get_object_path; bluetooth_device_to_string; bluetooth_battery_type_get_type; + bluetooth_adapter_state_get_type; bluetooth_agent_get_type; bluetooth_agent_error_get_type; bluetooth_agent_new; diff --git a/tests/integration-test.py b/tests/integration-test.py index e7ebf7ef..83550c3a 100755 --- a/tests/integration-test.py +++ b/tests/integration-test.py @@ -265,26 +265,34 @@ class OopTests(dbusmock.DBusTestCase): self.assertEqual(dbusprops_bluez.Get('org.bluez.Adapter1', 'Powered'), False) self.wait_for_mainloop() self.assertEqual(dbusprops_bluez.Get('org.bluez.Adapter1', 'Powered'), False) + self.assertEqual(self.client.props.default_adapter_state, GnomeBluetoothPriv.AdapterState.OFF) self.client.props.default_adapter_powered = True + self.assertEqual(self.client.props.default_adapter_state, GnomeBluetoothPriv.AdapterState.TURNING_ON) self.wait_for_condition(lambda: dbusprops_bluez.Get('org.bluez.Adapter1', 'Powered') == True) self.assertEqual(self.client.props.num_adapters, 1) self.assertEqual(dbusprops_bluez.Get('org.bluez.Adapter1', 'Powered'), True) self.wait_for_mainloop() self.assertEqual(self.client.props.default_adapter_powered, True) + self.assertEqual(self.client.props.default_adapter_state, GnomeBluetoothPriv.AdapterState.ON) self.client.props.default_adapter_powered = False self.wait_for_condition(lambda: dbusprops_bluez.Get('org.bluez.Adapter1', 'Powered') == False) self.assertEqual(dbusprops_bluez.Get('org.bluez.Adapter1', 'Powered'), False) + self.assertEqual(self.client.props.default_adapter_state, GnomeBluetoothPriv.AdapterState.TURNING_OFF) self.wait_for_mainloop() self.assertEqual(self.client.props.default_adapter_powered, False) + self.assertEqual(self.client.props.default_adapter_state, GnomeBluetoothPriv.AdapterState.OFF) dbusmock_bluez.UpdateProperties('org.bluez.Adapter1', { 'Powered': True, }) + # NOTE: this should be "turning on" when we have bluez API to keep track of it + self.assertEqual(self.client.props.default_adapter_state, GnomeBluetoothPriv.AdapterState.OFF) self.wait_for_condition(lambda: dbusprops_bluez.Get('org.bluez.Adapter1', 'Powered') == True) self.assertEqual(dbusprops_bluez.Get('org.bluez.Adapter1', 'Powered'), True) self.wait_for_mainloop() self.assertEqual(self.client.props.default_adapter_powered, True) + self.assertEqual(self.client.props.default_adapter_state, GnomeBluetoothPriv.AdapterState.ON) def _pair_cb(self, client, result, user_data=None): success, path = client.setup_device_finish(result) |