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

* Re: [PATCH BlueZ] gatt-client: Add PreferredNotifyType property
  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-14 10:34   ` [PATCH BlueZ] gatt-client: Add PreferredNotifyType property Simon Mikuda
  2026-06-08 15:49 ` [BlueZ] " bluez.test.bot
  1 sibling, 2 replies; 6+ messages in thread
From: Luiz Augusto von Dentz @ 2026-06-08 15:21 UTC (permalink / raw)
  To: Simon Mikuda; +Cc: linux-bluetooth

Hi Simon,

On Mon, Jun 8, 2026 at 10:31 AM Simon Mikuda
<simon.mikuda@streamunlimited.com> wrote:
>
> 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.

Well the spec seems to have changed to allow setting both notify and
indicate simultaneously as well:

If both the Notification and Indication bits are set, then the server
shall use the notification procedure (see Section 4.10) when an
acknowledgment is not required by a higher-layer specification or
shall use the indication procedure (see Section 4.11) when an
acknowledgment is required. The server should not use both procedures
to send the same characteristic value.

So perhaps we need to handle that as well, that said I don't know if
it wouldn't be more efficient to add another method which would enable
passing the value directly rather modifying the
StartNotify/AcquireNotify behavior, but then we would need to create a
different interface name in order to break the existing methods, so
maybe going wit this is better and we just need to handle 0x0003 as
being both enabled and the server decides what to do with it.

> 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
>
>


-- 
Luiz Augusto von Dentz

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

* RE: [BlueZ] gatt-client: Add PreferredNotifyType property
  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-08 15:49 ` bluez.test.bot
  1 sibling, 0 replies; 6+ messages in thread
From: bluez.test.bot @ 2026-06-08 15:49 UTC (permalink / raw)
  To: linux-bluetooth, simon.mikuda

[-- Attachment #1: Type: text/plain, Size: 989 bytes --]

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=1107894

---Test result---

Test Summary:
CheckPatch                    PASS      0.61 seconds
GitLint                       PASS      0.34 seconds
BuildEll                      PASS      20.15 seconds
BluezMake                     PASS      607.22 seconds
MakeCheck                     PASS      19.31 seconds
MakeDistcheck                 PASS      233.45 seconds
CheckValgrind                 PASS      272.52 seconds
CheckSmatch                   PASS      321.85 seconds
bluezmakeextell               PASS      163.91 seconds
IncrementalBuild              PASS      609.86 seconds
ScanBuild                     PASS      915.31 seconds



https://github.com/bluez/bluez/pull/2195

---
Regards,
Linux Bluetooth


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

* [PATCH BlueZ 2/2] gatt-database: Prefer notifications over indications
  2026-06-08 15:21 ` Luiz Augusto von Dentz
@ 2026-06-14 10:29   ` Simon Mikuda
  2026-06-15 15:20     ` patchwork-bot+bluetooth
  2026-06-14 10:34   ` [PATCH BlueZ] gatt-client: Add PreferredNotifyType property Simon Mikuda
  1 sibling, 1 reply; 6+ messages in thread
From: Simon Mikuda @ 2026-06-14 10:29 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Simon Mikuda

When both notifications and indications are enabled (CCC value=0x0003)
we will send notifications by default.
---
 src/gatt-database.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/gatt-database.c b/src/gatt-database.c
index 680a52952..dfa83b31a 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -1446,7 +1446,7 @@ static void send_notification_to_device(void *data, void *user_data)
 	 * TODO: If the device is not connected but bonded, send the
 	 * notification/indication when it becomes connected.
 	 */
-	if (!(ccc->value & 0x0002)) {
+	if (ccc->value & 0x0001) {
 		DBG("GATT server sending notification");
 		bt_gatt_server_send_notification(server,
 					notify->handle, notify->value,
-- 
2.43.0


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

* Re: [PATCH BlueZ] gatt-client: Add PreferredNotifyType property
  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-14 10:34   ` Simon Mikuda
  1 sibling, 0 replies; 6+ messages in thread
From: Simon Mikuda @ 2026-06-14 10:34 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

I send a patch that will use notifications by default, when both 
indications+notifications are enabled.

I am not sure what is meant by this:

 > when an acknowledgment is not required by a higher-layer specification

in that case we should send indication, which was previous behavior...

On 6/8/26 17:21, Luiz Augusto von Dentz wrote:
> Hi Simon,
>
> On Mon, Jun 8, 2026 at 10:31 AM Simon Mikuda
> <simon.mikuda@streamunlimited.com> wrote:
>> 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.
> Well the spec seems to have changed to allow setting both notify and
> indicate simultaneously as well:
>
> If both the Notification and Indication bits are set, then the server
> shall use the notification procedure (see Section 4.10) when an
> acknowledgment is not required by a higher-layer specification or
> shall use the indication procedure (see Section 4.11) when an
> acknowledgment is required. The server should not use both procedures
> to send the same characteristic value.
>
> So perhaps we need to handle that as well, that said I don't know if
> it wouldn't be more efficient to add another method which would enable
> passing the value directly rather modifying the
> StartNotify/AcquireNotify behavior, but then we would need to create a
> different interface name in order to break the existing methods, so
> maybe going wit this is better and we just need to handle 0x0003 as
> being both enabled and the server decides what to do with it.
>
>> 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	[flat|nested] 6+ messages in thread

* Re: [PATCH BlueZ 2/2] gatt-database: Prefer notifications over indications
  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
  0 siblings, 0 replies; 6+ messages in thread
From: patchwork-bot+bluetooth @ 2026-06-15 15:20 UTC (permalink / raw)
  To: Simon Mikuda; +Cc: linux-bluetooth

Hello:

This patch was applied to bluetooth/bluez.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:

On Sun, 14 Jun 2026 12:29:31 +0200 you wrote:
> When both notifications and indications are enabled (CCC value=0x0003)
> we will send notifications by default.
> ---
>  src/gatt-database.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)

Here is the summary with links:
  - [BlueZ,2/2] gatt-database: Prefer notifications over indications
    https://git.kernel.org/pub/scm/bluetooth/bluez.git/?id=9c36e4189e32

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html



^ permalink raw reply	[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