summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBastien Nocera <hadess@hadess.net>2022-08-23 12:48:19 +0200
committerBastien Nocera <hadess@hadess.net>2022-08-23 16:11:08 +0200
commit2334239e822fe5a0f2838771b7b71df6a93af61a (patch)
treebdd093859a4097835f61e9b3d1814053c79a67ad
parentda67ec337efbfeee6075e158bd79aad56f00af3c (diff)
downloadgnome-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.c65
-rw-r--r--lib/bluetooth-enums.h18
-rw-r--r--lib/gnome-bluetooth.map1
-rwxr-xr-xtests/integration-test.py8
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)