Linux bluetooth development
 help / color / mirror / Atom feed
* [PATCH BlueZ] gatt-client: Add PreferredNotifyType property
@ 2026-06-08 14:20 Simon Mikuda
  2026-06-08 15:21 ` Luiz Augusto von Dentz
  2026-06-08 15:49 ` [BlueZ] " bluez.test.bot
  0 siblings, 2 replies; 6+ messages in thread
From: Simon Mikuda @ 2026-06-08 14:20 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Simon Mikuda

When a characteristic supports both notifications and indications the
CCC we always register for notifications, leaving no way to choose
indications from D-Bus.

Add PreferredNotifyType (string, "notification"/"indication") to
org.bluez.GattCharacteristic1, only present when both flags are set.
StartNotify() and AcquireNotify() honor it on the next CCC write.
---
 doc/org.bluez.GattCharacteristic.rst | 21 +++++++
 src/gatt-client.c                    | 85 ++++++++++++++++++++++++++++
 src/shared/gatt-client.c             | 31 +++++++++-
 src/shared/gatt-client.h             |  3 +
 4 files changed, 138 insertions(+), 2 deletions(-)

diff --git a/doc/org.bluez.GattCharacteristic.rst b/doc/org.bluez.GattCharacteristic.rst
index 805f39593..f7e541590 100644
--- a/doc/org.bluez.GattCharacteristic.rst
+++ b/doc/org.bluez.GattCharacteristic.rst
@@ -401,3 +401,24 @@ uint16 MTU [read-only]
 
 Characteristic MTU, this is valid both for **ReadValue()** and **WriteValue()**
 but either method can use long procedures when supported.
+
+string PreferredNotifyType [readwrite, optional, experimental]
+``````````````````````````````````````````````````````````````
+
+Selects whether **StartNotify()** and **AcquireNotify()** enable notifications
+or indications when the characteristic supports both. Only present when both
+"notify" and "indicate" flags are set.
+
+Possible values:
+
+:"notification":
+
+	Enable notifications (CCC = 0x0001). Default.
+
+:"indication":
+
+	Enable indications (CCC = 0x0002).
+
+Note: the preference applies on the next CCC write. If another client has
+already enabled the CCC on this characteristic, the existing setting is kept
+and the new preference takes effect only after the last subscriber stops.
diff --git a/src/gatt-client.c b/src/gatt-client.c
index 3baf95c4f..29564a87d 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -115,6 +115,7 @@ struct characteristic {
 	struct queue *descs;
 
 	bool notifying;
+	bool prefer_indicate;
 	struct queue *notify_clients;
 };
 
@@ -1595,6 +1596,9 @@ static DBusMessage *characteristic_acquire_notify(DBusConnection *conn,
 	if (!client)
 		return btd_error_failed(msg, "Failed allocate notify session");
 
+	bt_gatt_client_set_notify_prefer_indicate(gatt, chrc->value_handle,
+							chrc->prefer_indicate);
+
 	client->notify_id = bt_gatt_client_register_notify(gatt,
 						chrc->value_handle,
 						register_notify_io_cb,
@@ -1673,6 +1677,9 @@ static DBusMessage *characteristic_start_notify(DBusConnection *conn,
 
 	op = async_dbus_op_new(msg, client);
 
+	bt_gatt_client_set_notify_prefer_indicate(gatt, chrc->value_handle,
+							chrc->prefer_indicate);
+
 	client->notify_id = bt_gatt_client_register_notify(gatt,
 						chrc->value_handle,
 						register_notify_cb, notify_cb,
@@ -1719,6 +1726,76 @@ static DBusMessage *characteristic_stop_notify(DBusConnection *conn,
 	return dbus_message_new_method_return(msg);
 }
 
+static gboolean
+characteristic_get_prefer_notify_type(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct characteristic *chrc = data;
+	const char *str = chrc->prefer_indicate ? "indication" : "notification";
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+
+	return TRUE;
+}
+
+static void
+characteristic_set_prefer_notify_type(const GDBusPropertyTable *property,
+					DBusMessageIter *iter,
+					GDBusPendingPropertySet id, void *data)
+{
+	struct characteristic *chrc = data;
+	struct bt_gatt_client *gatt = chrc->service->client->gatt;
+	const char *str;
+	bool prefer;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	dbus_message_iter_get_basic(iter, &str);
+
+	if (!strcmp(str, "notification"))
+		prefer = false;
+	else if (!strcmp(str, "indication"))
+		prefer = true;
+	else {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	if (chrc->prefer_indicate == prefer) {
+		g_dbus_pending_property_success(id);
+		return;
+	}
+
+	chrc->prefer_indicate = prefer;
+
+	if (gatt)
+		bt_gatt_client_set_notify_prefer_indicate(gatt,
+						chrc->value_handle, prefer);
+
+	g_dbus_emit_property_changed(btd_get_dbus_connection(), chrc->path,
+					GATT_CHARACTERISTIC_IFACE,
+					"PreferredNotifyType");
+
+	g_dbus_pending_property_success(id);
+}
+
+static gboolean
+characteristic_prefer_notify_type_exists(const GDBusPropertyTable *property,
+					void *data)
+{
+	struct characteristic *chrc = data;
+
+	return (chrc->props & BT_GATT_CHRC_PROP_NOTIFY) &&
+				(chrc->props & BT_GATT_CHRC_PROP_INDICATE);
+}
+
 static const GDBusPropertyTable characteristic_properties[] = {
 	{ "Handle", "q", characteristic_get_handle },
 	{ "UUID", "s", characteristic_get_uuid, NULL, NULL },
@@ -1733,6 +1810,10 @@ static const GDBusPropertyTable characteristic_properties[] = {
 	{ "NotifyAcquired", "b", characteristic_get_notify_acquired, NULL,
 				characteristic_notify_acquired_exists },
 	{ "MTU", "q", characteristic_get_mtu, NULL, characteristic_mtu_exists },
+	{ "PreferredNotifyType", "s", characteristic_get_prefer_notify_type,
+				characteristic_set_prefer_notify_type,
+				characteristic_prefer_notify_type_exists,
+				G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
 	{ }
 };
 
@@ -2282,6 +2363,10 @@ static void register_notify(void *data, void *user_data)
 	op = new0(struct async_dbus_op, 1);
 	op->data = notify_client;
 
+	bt_gatt_client_set_notify_prefer_indicate(client->gatt,
+					notify_client->chrc->value_handle,
+					notify_client->chrc->prefer_indicate);
+
 	notify_client->notify_id = bt_gatt_client_register_notify(client->gatt,
 					notify_client->chrc->value_handle,
 					register_notify_cb, notify_cb,
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index a6abe8ac2..403d22758 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -217,6 +217,7 @@ struct notify_chrc {
 	uint16_t properties;
 	unsigned int notify_id;
 	int notify_count;  /* Reference count of registered notify callbacks */
+	bool prefer_indicate;
 
 	/* Pending calls to register_notify are queued here so that they can be
 	 * processed after a write that modifies the CCC descriptor.
@@ -1671,9 +1672,13 @@ static bool notify_data_write_ccc(struct notify_data *notify_data, bool enable,
 
 	if (enable) {
 		/* Try to enable notifications or indications based on
-		 * whatever the characteristic supports.
+		 * whatever the characteristic supports. If both are
+		 * available honor the per-chrc prefer_indicate flag.
 		 */
-		if (properties & BT_GATT_CHRC_PROP_NOTIFY)
+		if (notify_data->chrc->prefer_indicate &&
+				(properties & BT_GATT_CHRC_PROP_INDICATE))
+			value = cpu_to_le16(0x0002);
+		else if (properties & BT_GATT_CHRC_PROP_NOTIFY)
 			value = cpu_to_le16(0x0001);
 		else if (properties & BT_GATT_CHRC_PROP_INDICATE)
 			value = cpu_to_le16(0x0002);
@@ -3838,6 +3843,28 @@ unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
 							user_data, destroy);
 }
 
+bool bt_gatt_client_set_notify_prefer_indicate(struct bt_gatt_client *client,
+						uint16_t value_handle,
+						bool prefer)
+{
+	struct notify_chrc *chrc;
+
+	if (!client || !client->db || !value_handle)
+		return false;
+
+	chrc = queue_find(client->notify_chrcs, match_notify_chrc_value_handle,
+						UINT_TO_PTR(value_handle));
+	if (!chrc) {
+		chrc = notify_chrc_create(client, value_handle);
+		if (!chrc)
+			return false;
+	}
+
+	chrc->prefer_indicate = prefer;
+
+	return true;
+}
+
 bool bt_gatt_client_unregister_notify(struct bt_gatt_client *client,
 							unsigned int id)
 {
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
index 63cf99500..0d08f8014 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -124,6 +124,9 @@ unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
 				bt_gatt_client_destroy_func_t destroy);
 bool bt_gatt_client_unregister_notify(struct bt_gatt_client *client,
 							unsigned int id);
+bool bt_gatt_client_set_notify_prefer_indicate(struct bt_gatt_client *client,
+						uint16_t value_handle,
+						bool prefer);
 
 bool bt_gatt_client_set_security(struct bt_gatt_client *client, int level);
 int bt_gatt_client_get_security(struct bt_gatt_client *client);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-06-15 15:20 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-08 14:20 [PATCH BlueZ] gatt-client: Add PreferredNotifyType property Simon Mikuda
2026-06-08 15:21 ` Luiz Augusto von Dentz
2026-06-14 10:29   ` [PATCH BlueZ 2/2] gatt-database: Prefer notifications over indications Simon Mikuda
2026-06-15 15:20     ` patchwork-bot+bluetooth
2026-06-14 10:34   ` [PATCH BlueZ] gatt-client: Add PreferredNotifyType property Simon Mikuda
2026-06-08 15:49 ` [BlueZ] " bluez.test.bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox