All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] shared/gatt: Allow register_notify without CCC
@ 2015-02-04  3:58 Arman Uguray
  2015-02-04 14:26 ` Luiz Augusto von Dentz
  0 siblings, 1 reply; 6+ messages in thread
From: Arman Uguray @ 2015-02-04  3:58 UTC (permalink / raw)
  To: linux-bluetooth; +Cc: Arman Uguray

Certain peripherals can expose a GATT characteristic with the
notify/indicate property but with no Client Characteristic Configuration
descriptor. Since bt_gatt_client_register_notify enforces a CCC as a
requirement, it returns an error for such characteristics.

This patch fixes this behavior so that bt_gatt_client_register_notify
immediately registers the callback and returns success for
characteristics with no CCC.
---
 src/shared/gatt-client.c | 43 +++++++++++++++++++++++++------------------
 1 file changed, 25 insertions(+), 18 deletions(-)

diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 5edb991..d0fc054 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -88,6 +88,7 @@ struct bt_gatt_client {
 	 * the remote peripheral.
 	 */
 	unsigned int svc_chngd_ind_id;
+	bool svc_chngd_registered;
 	struct queue *svc_chngd_queue;  /* Queued service changed events */
 	bool in_svc_chngd;
 
@@ -234,12 +235,6 @@ static struct notify_chrc *notify_chrc_create(struct bt_gatt_client *client,
 							&properties, NULL))
 			return NULL;
 
-	/* Find the CCC characteristic */
-	ccc = NULL;
-	gatt_db_service_foreach_desc(attr, find_ccc, &ccc);
-	if (!ccc)
-		return NULL;
-
 	chrc = new0(struct notify_chrc, 1);
 	if (!chrc)
 		return NULL;
@@ -250,8 +245,17 @@ static struct notify_chrc *notify_chrc_create(struct bt_gatt_client *client,
 		return NULL;
 	}
 
+	/*
+	 * Find the CCC characteristic. Some characteristics that allow
+	 * notifications may not have a CCC descriptor. We treat these as
+	 * automatically successful.
+	 */
+	ccc = NULL;
+	gatt_db_service_foreach_desc(attr, find_ccc, &ccc);
+	if (ccc)
+		chrc->ccc_handle = gatt_db_attribute_get_handle(ccc);
+
 	chrc->value_handle = value_handle;
-	chrc->ccc_handle = gatt_db_attribute_get_handle(ccc);
 	chrc->properties = properties;
 
 	queue_push_tail(client->notify_chrcs, chrc);
@@ -1020,6 +1024,7 @@ static void service_changed_reregister_cb(uint16_t att_ecode, void *user_data)
 		util_debug(client->debug_callback, client->debug_data,
 			"Re-registered handler for \"Service Changed\" after "
 			"change in GATT service");
+		client->svc_chngd_registered = true;
 		return;
 	}
 
@@ -1089,6 +1094,7 @@ static void service_changed_complete(struct discovery_op *op, bool success,
 	/* The GATT service was modified. Re-register the handler for
 	 * indications from the "Service Changed" characteristic.
 	 */
+	client->svc_chngd_registered = false;
 	client->svc_chngd_ind_id = bt_gatt_client_register_notify(client,
 					gatt_db_attribute_get_handle(attr),
 					service_changed_reregister_cb,
@@ -1197,6 +1203,7 @@ static void service_changed_register_cb(uint16_t att_ecode, void *user_data)
 		goto done;
 	}
 
+	client->svc_chngd_registered = true;
 	client->ready = true;
 	success = true;
 	util_debug(client->debug_callback, client->debug_data,
@@ -1239,13 +1246,16 @@ static void init_complete(struct discovery_op *op, bool success,
 					service_changed_register_cb,
 					service_changed_cb,
 					client, NULL);
-	client->ready = false;
+
+	if (!client->svc_chngd_registered)
+		client->ready = false;
 
 	if (client->svc_chngd_ind_id)
 		return;
 
 	util_debug(client->debug_callback, client->debug_data,
 			"Failed to register handler for \"Service Changed\"");
+	success = false;
 
 fail:
 	util_debug(client->debug_callback, client->debug_data,
@@ -1423,18 +1433,17 @@ static void complete_unregister_notify(void *data)
 	 */
 	if (notify_data->att_id) {
 		bt_att_cancel(notify_data->client->att, notify_data->att_id);
-		notify_data_unref(notify_data);
-		return;
+		goto done;
 	}
 
-	if (__sync_sub_and_fetch(&notify_data->chrc->notify_count, 1)) {
-		notify_data_unref(notify_data);
-		return;
-	}
+	if (__sync_sub_and_fetch(&notify_data->chrc->notify_count, 1) ||
+						!notify_data->chrc->ccc_handle)
+		goto done;
 
 	if (notify_data_write_ccc(notify_data, false, disable_ccc_callback))
 		return;
 
+done:
 	notify_data_unref(notify_data);
 }
 
@@ -2571,9 +2580,7 @@ unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
 	/* Add the handler to the bt_gatt_client's general list */
 	queue_push_tail(client->notify_list, notify_data);
 
-	/* Assign an ID to the handler and notify the caller that it was
-	 * successfully registered.
-	 */
+	/* Assign an ID to the handler. */
 	if (client->next_reg_id < 1)
 		client->next_reg_id = 1;
 
@@ -2591,7 +2598,7 @@ unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
 	/*
 	 * If the ref count is not zero, then notifications are already enabled.
 	 */
-	if (chrc->notify_count > 0) {
+	if (chrc->notify_count > 0 || !chrc->ccc_handle) {
 		complete_notify_request(notify_data);
 		return notify_data->id;
 	}
-- 
2.2.0.rc0.207.ga3a616c


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

* Re: [PATCH] shared/gatt: Allow register_notify without CCC
  2015-02-04  3:58 [PATCH] shared/gatt: Allow register_notify without CCC Arman Uguray
@ 2015-02-04 14:26 ` Luiz Augusto von Dentz
  2015-02-04 22:05   ` Arman Uguray
  0 siblings, 1 reply; 6+ messages in thread
From: Luiz Augusto von Dentz @ 2015-02-04 14:26 UTC (permalink / raw)
  To: Arman Uguray; +Cc: linux-bluetooth

Hi Arman,

On Wed, Feb 4, 2015 at 5:58 AM, Arman Uguray <armansito@chromium.org> wrote:
> Certain peripherals can expose a GATT characteristic with the
> notify/indicate property but with no Client Characteristic Configuration
> descriptor. Since bt_gatt_client_register_notify enforces a CCC as a
> requirement, it returns an error for such characteristics.

Do you have some evidence of this? I don't mind not being too strict
while receiving but we need to document why we are doing these
changes, and btw this perhaps makes my argument of enabling
notification by default even stronger since if such devices really
exists they might be notifying us since the beginning.

The other problem I now see is that there is probably no way to rate
limit the notification, because they are profile specific, so if you
had a problem with too much traffic on D-Bus this may already happen
even if we register the notification on demand.

> This patch fixes this behavior so that bt_gatt_client_register_notify
> immediately registers the callback and returns success for
> characteristics with no CCC.
> ---
>  src/shared/gatt-client.c | 43 +++++++++++++++++++++++++------------------
>  1 file changed, 25 insertions(+), 18 deletions(-)
>
> diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
> index 5edb991..d0fc054 100644
> --- a/src/shared/gatt-client.c
> +++ b/src/shared/gatt-client.c
> @@ -88,6 +88,7 @@ struct bt_gatt_client {
>          * the remote peripheral.
>          */
>         unsigned int svc_chngd_ind_id;
> +       bool svc_chngd_registered;
>         struct queue *svc_chngd_queue;  /* Queued service changed events */
>         bool in_svc_chngd;
>
> @@ -234,12 +235,6 @@ static struct notify_chrc *notify_chrc_create(struct bt_gatt_client *client,
>                                                         &properties, NULL))
>                         return NULL;
>
> -       /* Find the CCC characteristic */
> -       ccc = NULL;
> -       gatt_db_service_foreach_desc(attr, find_ccc, &ccc);
> -       if (!ccc)
> -               return NULL;
> -
>         chrc = new0(struct notify_chrc, 1);
>         if (!chrc)
>                 return NULL;
> @@ -250,8 +245,17 @@ static struct notify_chrc *notify_chrc_create(struct bt_gatt_client *client,
>                 return NULL;
>         }
>
> +       /*
> +        * Find the CCC characteristic. Some characteristics that allow
> +        * notifications may not have a CCC descriptor. We treat these as
> +        * automatically successful.
> +        */
> +       ccc = NULL;
> +       gatt_db_service_foreach_desc(attr, find_ccc, &ccc);
> +       if (ccc)
> +               chrc->ccc_handle = gatt_db_attribute_get_handle(ccc);
> +
>         chrc->value_handle = value_handle;
> -       chrc->ccc_handle = gatt_db_attribute_get_handle(ccc);
>         chrc->properties = properties;
>
>         queue_push_tail(client->notify_chrcs, chrc);
> @@ -1020,6 +1024,7 @@ static void service_changed_reregister_cb(uint16_t att_ecode, void *user_data)
>                 util_debug(client->debug_callback, client->debug_data,
>                         "Re-registered handler for \"Service Changed\" after "
>                         "change in GATT service");
> +               client->svc_chngd_registered = true;
>                 return;
>         }
>
> @@ -1089,6 +1094,7 @@ static void service_changed_complete(struct discovery_op *op, bool success,
>         /* The GATT service was modified. Re-register the handler for
>          * indications from the "Service Changed" characteristic.
>          */
> +       client->svc_chngd_registered = false;
>         client->svc_chngd_ind_id = bt_gatt_client_register_notify(client,
>                                         gatt_db_attribute_get_handle(attr),
>                                         service_changed_reregister_cb,
> @@ -1197,6 +1203,7 @@ static void service_changed_register_cb(uint16_t att_ecode, void *user_data)
>                 goto done;
>         }
>
> +       client->svc_chngd_registered = true;
>         client->ready = true;
>         success = true;
>         util_debug(client->debug_callback, client->debug_data,
> @@ -1239,13 +1246,16 @@ static void init_complete(struct discovery_op *op, bool success,
>                                         service_changed_register_cb,
>                                         service_changed_cb,
>                                         client, NULL);
> -       client->ready = false;
> +
> +       if (!client->svc_chngd_registered)
> +               client->ready = false;
>
>         if (client->svc_chngd_ind_id)
>                 return;
>
>         util_debug(client->debug_callback, client->debug_data,
>                         "Failed to register handler for \"Service Changed\"");
> +       success = false;
>
>  fail:
>         util_debug(client->debug_callback, client->debug_data,
> @@ -1423,18 +1433,17 @@ static void complete_unregister_notify(void *data)
>          */
>         if (notify_data->att_id) {
>                 bt_att_cancel(notify_data->client->att, notify_data->att_id);
> -               notify_data_unref(notify_data);
> -               return;
> +               goto done;
>         }
>
> -       if (__sync_sub_and_fetch(&notify_data->chrc->notify_count, 1)) {
> -               notify_data_unref(notify_data);
> -               return;
> -       }
> +       if (__sync_sub_and_fetch(&notify_data->chrc->notify_count, 1) ||
> +                                               !notify_data->chrc->ccc_handle)
> +               goto done;
>
>         if (notify_data_write_ccc(notify_data, false, disable_ccc_callback))
>                 return;
>
> +done:
>         notify_data_unref(notify_data);
>  }
>
> @@ -2571,9 +2580,7 @@ unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
>         /* Add the handler to the bt_gatt_client's general list */
>         queue_push_tail(client->notify_list, notify_data);
>
> -       /* Assign an ID to the handler and notify the caller that it was
> -        * successfully registered.
> -        */
> +       /* Assign an ID to the handler. */
>         if (client->next_reg_id < 1)
>                 client->next_reg_id = 1;
>
> @@ -2591,7 +2598,7 @@ unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
>         /*
>          * If the ref count is not zero, then notifications are already enabled.
>          */
> -       if (chrc->notify_count > 0) {
> +       if (chrc->notify_count > 0 || !chrc->ccc_handle) {
>                 complete_notify_request(notify_data);
>                 return notify_data->id;
>         }
> --
> 2.2.0.rc0.207.ga3a616c
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html



-- 
Luiz Augusto von Dentz

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

* Re: [PATCH] shared/gatt: Allow register_notify without CCC
  2015-02-04 14:26 ` Luiz Augusto von Dentz
@ 2015-02-04 22:05   ` Arman Uguray
  2015-02-05  9:29     ` Luiz Augusto von Dentz
  0 siblings, 1 reply; 6+ messages in thread
From: Arman Uguray @ 2015-02-04 22:05 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

Hi Luiz,

> On Wed, Feb 4, 2015 at 6:26 AM, Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
> Hi Arman,
>
> On Wed, Feb 4, 2015 at 5:58 AM, Arman Uguray <armansito@chromium.org> wrote:
>> Certain peripherals can expose a GATT characteristic with the
>> notify/indicate property but with no Client Characteristic Configuration
>> descriptor. Since bt_gatt_client_register_notify enforces a CCC as a
>> requirement, it returns an error for such characteristics.
>
> Do you have some evidence of this? I don't mind not being too strict
> while receiving but we need to document why we are doing these
> changes,...

I came across this while testing with a Nexus 6 device running Android
L. The application exposes a "Service Changed" characteristic with no
CCC, which is causing bt_gatt_client's initialization sequence to fail
(since it assumes that there will be a CCC and treats the absence as
an error). This seemed strange to me, so I did some digging, and the
Bluetooth developer page actually lists "None" for "Descriptors"
(https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.generic_attribute.xml)
of this characteristic.

However, this seemed incorrect to me, so I checked the Core Spec v4.2,
Vol 3, Part G, Section 7.1 "Service Changed", which states: "This
[Service Changed] Characteristic Value shall be configured to be
indicated, using the Client Characteristic Configuration descriptor by
a client. Indications caused by changes to the Service Changed
Characteristic Value shall be considered lost if the client has not
enabled indications in the Client Configuration Characteristic
Descriptor." Which means that the CCC is certainly mandatory, so this
must be an error in the developer page.

Regardless, though, even if this is an error in the BlueDroid stack,
it means that some devices may have incorrectly implemented the spec
here, so we should probably tolerate this. BlueZ's server
implementation SHOULD regardless expose this descriptor, but the
client IMO shouldn't fail if the CCC generally doesn't exist.

> ...and btw this perhaps makes my argument of enabling
> notification by default even stronger since if such devices really
> exists they might be notifying us since the beginning.
>

Hmm, I don't know how this makes that argument any stronger.
bt_gatt_client already automatically registers for Service Changed,
and for external applications we still want to only notify if an app
is interested (which they can do now across connections).

Generally, I want to treat this as a corner-case, since it's not
really spec compliant behavior. Either way, it's probably not that bad
to have this fix so that we don't bork out immediately with devices
that behave incorrectly. Actually, it might be even more spec
compliant for us to simply ignore the Service Changed characteristic
if it's discovered but doesn't have a CCC, since the spec says
"changes to the Service Changed Characteristic Value shall be
considered lost if the client has not enabled indications in the
Client Configuration Characteristic Descriptor", which would end up
being a simpler fix on our end.

Either way, I'll wait for your response to determine what the right
behavior is here.

> The other problem I now see is that there is probably no way to rate
> limit the notification, because they are profile specific, so if you
> had a problem with too much traffic on D-Bus this may already happen
> even if we register the notification on demand.
>

Right, though I guess this is a separate issue that can be dealt with
at the D-Bus API level. shared/gatt-client has to still send all
notifications up, otherwise things like HoG wouldn't work well with a
rate limit. Anyway, we can do this at a D-Bus API level separately.
I'll add this to the general TODO list.

>> This patch fixes this behavior so that bt_gatt_client_register_notify
>> immediately registers the callback and returns success for
>> characteristics with no CCC.
>> ---
>>  src/shared/gatt-client.c | 43 +++++++++++++++++++++++++------------------
>>  1 file changed, 25 insertions(+), 18 deletions(-)
>>
>> diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
>> index 5edb991..d0fc054 100644
>> --- a/src/shared/gatt-client.c
>> +++ b/src/shared/gatt-client.c
>> @@ -88,6 +88,7 @@ struct bt_gatt_client {
>>          * the remote peripheral.
>>          */
>>         unsigned int svc_chngd_ind_id;
>> +       bool svc_chngd_registered;
>>         struct queue *svc_chngd_queue;  /* Queued service changed events */
>>         bool in_svc_chngd;
>>
>> @@ -234,12 +235,6 @@ static struct notify_chrc *notify_chrc_create(struct bt_gatt_client *client,
>>                                                         &properties, NULL))
>>                         return NULL;
>>
>> -       /* Find the CCC characteristic */
>> -       ccc = NULL;
>> -       gatt_db_service_foreach_desc(attr, find_ccc, &ccc);
>> -       if (!ccc)
>> -               return NULL;
>> -
>>         chrc = new0(struct notify_chrc, 1);
>>         if (!chrc)
>>                 return NULL;
>> @@ -250,8 +245,17 @@ static struct notify_chrc *notify_chrc_create(struct bt_gatt_client *client,
>>                 return NULL;
>>         }
>>
>> +       /*
>> +        * Find the CCC characteristic. Some characteristics that allow
>> +        * notifications may not have a CCC descriptor. We treat these as
>> +        * automatically successful.
>> +        */
>> +       ccc = NULL;
>> +       gatt_db_service_foreach_desc(attr, find_ccc, &ccc);
>> +       if (ccc)
>> +               chrc->ccc_handle = gatt_db_attribute_get_handle(ccc);
>> +
>>         chrc->value_handle = value_handle;
>> -       chrc->ccc_handle = gatt_db_attribute_get_handle(ccc);
>>         chrc->properties = properties;
>>
>>         queue_push_tail(client->notify_chrcs, chrc);
>> @@ -1020,6 +1024,7 @@ static void service_changed_reregister_cb(uint16_t att_ecode, void *user_data)
>>                 util_debug(client->debug_callback, client->debug_data,
>>                         "Re-registered handler for \"Service Changed\" after "
>>                         "change in GATT service");
>> +               client->svc_chngd_registered = true;
>>                 return;
>>         }
>>
>> @@ -1089,6 +1094,7 @@ static void service_changed_complete(struct discovery_op *op, bool success,
>>         /* The GATT service was modified. Re-register the handler for
>>          * indications from the "Service Changed" characteristic.
>>          */
>> +       client->svc_chngd_registered = false;
>>         client->svc_chngd_ind_id = bt_gatt_client_register_notify(client,
>>                                         gatt_db_attribute_get_handle(attr),
>>                                         service_changed_reregister_cb,
>> @@ -1197,6 +1203,7 @@ static void service_changed_register_cb(uint16_t att_ecode, void *user_data)
>>                 goto done;
>>         }
>>
>> +       client->svc_chngd_registered = true;
>>         client->ready = true;
>>         success = true;
>>         util_debug(client->debug_callback, client->debug_data,
>> @@ -1239,13 +1246,16 @@ static void init_complete(struct discovery_op *op, bool success,
>>                                         service_changed_register_cb,
>>                                         service_changed_cb,
>>                                         client, NULL);
>> -       client->ready = false;
>> +
>> +       if (!client->svc_chngd_registered)
>> +               client->ready = false;
>>
>>         if (client->svc_chngd_ind_id)
>>                 return;
>>
>>         util_debug(client->debug_callback, client->debug_data,
>>                         "Failed to register handler for \"Service Changed\"");
>> +       success = false;
>>
>>  fail:
>>         util_debug(client->debug_callback, client->debug_data,
>> @@ -1423,18 +1433,17 @@ static void complete_unregister_notify(void *data)
>>          */
>>         if (notify_data->att_id) {
>>                 bt_att_cancel(notify_data->client->att, notify_data->att_id);
>> -               notify_data_unref(notify_data);
>> -               return;
>> +               goto done;
>>         }
>>
>> -       if (__sync_sub_and_fetch(&notify_data->chrc->notify_count, 1)) {
>> -               notify_data_unref(notify_data);
>> -               return;
>> -       }
>> +       if (__sync_sub_and_fetch(&notify_data->chrc->notify_count, 1) ||
>> +                                               !notify_data->chrc->ccc_handle)
>> +               goto done;
>>
>>         if (notify_data_write_ccc(notify_data, false, disable_ccc_callback))
>>                 return;
>>
>> +done:
>>         notify_data_unref(notify_data);
>>  }
>>
>> @@ -2571,9 +2580,7 @@ unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
>>         /* Add the handler to the bt_gatt_client's general list */
>>         queue_push_tail(client->notify_list, notify_data);
>>
>> -       /* Assign an ID to the handler and notify the caller that it was
>> -        * successfully registered.
>> -        */
>> +       /* Assign an ID to the handler. */
>>         if (client->next_reg_id < 1)
>>                 client->next_reg_id = 1;
>>
>> @@ -2591,7 +2598,7 @@ unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
>>         /*
>>          * If the ref count is not zero, then notifications are already enabled.
>>          */
>> -       if (chrc->notify_count > 0) {
>> +       if (chrc->notify_count > 0 || !chrc->ccc_handle) {
>>                 complete_notify_request(notify_data);
>>                 return notify_data->id;
>>         }
>> --
>> 2.2.0.rc0.207.ga3a616c
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
>
>
> --
> Luiz Augusto von Dentz

Thanks,
Arman

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

* Re: [PATCH] shared/gatt: Allow register_notify without CCC
  2015-02-04 22:05   ` Arman Uguray
@ 2015-02-05  9:29     ` Luiz Augusto von Dentz
  2015-02-06 14:19       ` Arman Uguray
  0 siblings, 1 reply; 6+ messages in thread
From: Luiz Augusto von Dentz @ 2015-02-05  9:29 UTC (permalink / raw)
  To: Arman Uguray; +Cc: linux-bluetooth

Hi Arman,

On Thu, Feb 5, 2015 at 12:05 AM, Arman Uguray <armansito@chromium.org> wrote:
> Hi Luiz,
>
>> On Wed, Feb 4, 2015 at 6:26 AM, Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
>> Hi Arman,
>>
>> On Wed, Feb 4, 2015 at 5:58 AM, Arman Uguray <armansito@chromium.org> wrote:
>>> Certain peripherals can expose a GATT characteristic with the
>>> notify/indicate property but with no Client Characteristic Configuration
>>> descriptor. Since bt_gatt_client_register_notify enforces a CCC as a
>>> requirement, it returns an error for such characteristics.
>>
>> Do you have some evidence of this? I don't mind not being too strict
>> while receiving but we need to document why we are doing these
>> changes,...
>
> I came across this while testing with a Nexus 6 device running Android
> L. The application exposes a "Service Changed" characteristic with no
> CCC, which is causing bt_gatt_client's initialization sequence to fail
> (since it assumes that there will be a CCC and treats the absence as
> an error). This seemed strange to me, so I did some digging, and the
> Bluetooth developer page actually lists "None" for "Descriptors"
> (https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.generic_attribute.xml)
> of this characteristic.
>
> However, this seemed incorrect to me, so I checked the Core Spec v4.2,
> Vol 3, Part G, Section 7.1 "Service Changed", which states: "This
> [Service Changed] Characteristic Value shall be configured to be
> indicated, using the Client Characteristic Configuration descriptor by
> a client. Indications caused by changes to the Service Changed
> Characteristic Value shall be considered lost if the client has not
> enabled indications in the Client Configuration Characteristic
> Descriptor." Which means that the CCC is certainly mandatory, so this
> must be an error in the developer page.

It is probably a bug, and in this case the device might not even be
able to qualify since service changed has 2 tests: TP/GAS/CL/BV-01-C
and TP/GAS/SR/BV-01-C. We should probably communicate that the
documentation needs fixing and the TS should probably be updated as
well to reflect CCC is mandatory in this case.

> Regardless, though, even if this is an error in the BlueDroid stack,
> it means that some devices may have incorrectly implemented the spec
> here, so we should probably tolerate this. BlueZ's server
> implementation SHOULD regardless expose this descriptor, but the
> client IMO shouldn't fail if the CCC generally doesn't exist.

Provided the SIG don't come with invalid tests for service changed
that is probably okay for the client, for the server I don't how it
would pass TP/GAS/SR/BV-01-C without CCC except if PTS is ignoring the
lack of CCC which IMO defeats the purpose of the test. So the argument
for service changed is probably not valid here, but for custom
attributes where there is no tests to validate the implemenation
perhaps you have a point, but note that it still will need to set
BT_GATT_CHRC_PROP_NOTIFY and BT_GATT_CHRC_PROP_INDICATE, btw such
policies perhaps should be done directly in shared/gatt-client.c so we
don't need have duplicated logic for Android.

>
>> ...and btw this perhaps makes my argument of enabling
>> notification by default even stronger since if such devices really
>> exists they might be notifying us since the beginning.
>>
>
> Hmm, I don't know how this makes that argument any stronger.
> bt_gatt_client already automatically registers for Service Changed,
> and for external applications we still want to only notify if an app
> is interested (which they can do now across connections).
>
> Generally, I want to treat this as a corner-case, since it's not
> really spec compliant behavior. Either way, it's probably not that bad
> to have this fix so that we don't bork out immediately with devices
> that behave incorrectly. Actually, it might be even more spec
> compliant for us to simply ignore the Service Changed characteristic
> if it's discovered but doesn't have a CCC, since the spec says
> "changes to the Service Changed Characteristic Value shall be
> considered lost if the client has not enabled indications in the
> Client Configuration Characteristic Descriptor", which would end up
> being a simpler fix on our end.
>
> Either way, I'll wait for your response to determine what the right
> behavior is here.

For service changed perhaps, but since we are using
bt_gatt_client_register_notify for any notification we would need to
have a special case for service changed alone so I would not bother
since broken stacks in this respect will probably not pass the
qualification if the test spec where implemented properly, for any
other char I would not be so strict since some OS may allow adding a
characteristic with notifiy/indicate bits sets but without a CCC to
enable them, actually Android probably allows that to happen already,
sadly I think even our BlueZ for Android currently allows that.

>> The other problem I now see is that there is probably no way to rate
>> limit the notification, because they are profile specific, so if you
>> had a problem with too much traffic on D-Bus this may already happen
>> even if we register the notification on demand.
>>
>
> Right, though I guess this is a separate issue that can be dealt with
> at the D-Bus API level. shared/gatt-client has to still send all
> notifications up, otherwise things like HoG wouldn't work well with a
> rate limit. Anyway, we can do this at a D-Bus API level separately.
> I'll add this to the general TODO list.
>
>>> This patch fixes this behavior so that bt_gatt_client_register_notify
>>> immediately registers the callback and returns success for
>>> characteristics with no CCC.
>>> ---
>>>  src/shared/gatt-client.c | 43 +++++++++++++++++++++++++------------------
>>>  1 file changed, 25 insertions(+), 18 deletions(-)
>>>
>>> diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
>>> index 5edb991..d0fc054 100644
>>> --- a/src/shared/gatt-client.c
>>> +++ b/src/shared/gatt-client.c
>>> @@ -88,6 +88,7 @@ struct bt_gatt_client {
>>>          * the remote peripheral.
>>>          */
>>>         unsigned int svc_chngd_ind_id;
>>> +       bool svc_chngd_registered;
>>>         struct queue *svc_chngd_queue;  /* Queued service changed events */
>>>         bool in_svc_chngd;
>>>
>>> @@ -234,12 +235,6 @@ static struct notify_chrc *notify_chrc_create(struct bt_gatt_client *client,
>>>                                                         &properties, NULL))
>>>                         return NULL;
>>>
>>> -       /* Find the CCC characteristic */
>>> -       ccc = NULL;
>>> -       gatt_db_service_foreach_desc(attr, find_ccc, &ccc);
>>> -       if (!ccc)
>>> -               return NULL;
>>> -
>>>         chrc = new0(struct notify_chrc, 1);
>>>         if (!chrc)
>>>                 return NULL;
>>> @@ -250,8 +245,17 @@ static struct notify_chrc *notify_chrc_create(struct bt_gatt_client *client,
>>>                 return NULL;
>>>         }
>>>
>>> +       /*
>>> +        * Find the CCC characteristic. Some characteristics that allow
>>> +        * notifications may not have a CCC descriptor. We treat these as
>>> +        * automatically successful.
>>> +        */
>>> +       ccc = NULL;
>>> +       gatt_db_service_foreach_desc(attr, find_ccc, &ccc);
>>> +       if (ccc)
>>> +               chrc->ccc_handle = gatt_db_attribute_get_handle(ccc);
>>> +
>>>         chrc->value_handle = value_handle;
>>> -       chrc->ccc_handle = gatt_db_attribute_get_handle(ccc);
>>>         chrc->properties = properties;
>>>
>>>         queue_push_tail(client->notify_chrcs, chrc);
>>> @@ -1020,6 +1024,7 @@ static void service_changed_reregister_cb(uint16_t att_ecode, void *user_data)
>>>                 util_debug(client->debug_callback, client->debug_data,
>>>                         "Re-registered handler for \"Service Changed\" after "
>>>                         "change in GATT service");
>>> +               client->svc_chngd_registered = true;
>>>                 return;
>>>         }
>>>
>>> @@ -1089,6 +1094,7 @@ static void service_changed_complete(struct discovery_op *op, bool success,
>>>         /* The GATT service was modified. Re-register the handler for
>>>          * indications from the "Service Changed" characteristic.
>>>          */
>>> +       client->svc_chngd_registered = false;
>>>         client->svc_chngd_ind_id = bt_gatt_client_register_notify(client,
>>>                                         gatt_db_attribute_get_handle(attr),
>>>                                         service_changed_reregister_cb,
>>> @@ -1197,6 +1203,7 @@ static void service_changed_register_cb(uint16_t att_ecode, void *user_data)
>>>                 goto done;
>>>         }
>>>
>>> +       client->svc_chngd_registered = true;
>>>         client->ready = true;
>>>         success = true;
>>>         util_debug(client->debug_callback, client->debug_data,
>>> @@ -1239,13 +1246,16 @@ static void init_complete(struct discovery_op *op, bool success,
>>>                                         service_changed_register_cb,
>>>                                         service_changed_cb,
>>>                                         client, NULL);
>>> -       client->ready = false;
>>> +
>>> +       if (!client->svc_chngd_registered)
>>> +               client->ready = false;
>>>
>>>         if (client->svc_chngd_ind_id)
>>>                 return;
>>>
>>>         util_debug(client->debug_callback, client->debug_data,
>>>                         "Failed to register handler for \"Service Changed\"");
>>> +       success = false;
>>>
>>>  fail:
>>>         util_debug(client->debug_callback, client->debug_data,
>>> @@ -1423,18 +1433,17 @@ static void complete_unregister_notify(void *data)
>>>          */
>>>         if (notify_data->att_id) {
>>>                 bt_att_cancel(notify_data->client->att, notify_data->att_id);
>>> -               notify_data_unref(notify_data);
>>> -               return;
>>> +               goto done;
>>>         }
>>>
>>> -       if (__sync_sub_and_fetch(&notify_data->chrc->notify_count, 1)) {
>>> -               notify_data_unref(notify_data);
>>> -               return;
>>> -       }
>>> +       if (__sync_sub_and_fetch(&notify_data->chrc->notify_count, 1) ||
>>> +                                               !notify_data->chrc->ccc_handle)
>>> +               goto done;
>>>
>>>         if (notify_data_write_ccc(notify_data, false, disable_ccc_callback))
>>>                 return;
>>>
>>> +done:
>>>         notify_data_unref(notify_data);
>>>  }
>>>
>>> @@ -2571,9 +2580,7 @@ unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
>>>         /* Add the handler to the bt_gatt_client's general list */
>>>         queue_push_tail(client->notify_list, notify_data);
>>>
>>> -       /* Assign an ID to the handler and notify the caller that it was
>>> -        * successfully registered.
>>> -        */
>>> +       /* Assign an ID to the handler. */
>>>         if (client->next_reg_id < 1)
>>>                 client->next_reg_id = 1;
>>>
>>> @@ -2591,7 +2598,7 @@ unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
>>>         /*
>>>          * If the ref count is not zero, then notifications are already enabled.
>>>          */
>>> -       if (chrc->notify_count > 0) {
>>> +       if (chrc->notify_count > 0 || !chrc->ccc_handle) {
>>>                 complete_notify_request(notify_data);
>>>                 return notify_data->id;
>>>         }
>>> --
>>> 2.2.0.rc0.207.ga3a616c
>>>
>>> --
>>> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
>>> the body of a message to majordomo@vger.kernel.org
>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>
>>
>>
>> --
>> Luiz Augusto von Dentz
>
> Thanks,
> Arman



-- 
Luiz Augusto von Dentz

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

* Re: [PATCH] shared/gatt: Allow register_notify without CCC
  2015-02-05  9:29     ` Luiz Augusto von Dentz
@ 2015-02-06 14:19       ` Arman Uguray
  2015-02-06 14:58         ` Luiz Augusto von Dentz
  0 siblings, 1 reply; 6+ messages in thread
From: Arman Uguray @ 2015-02-06 14:19 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

Hi Luiz,

> On Thu, Feb 5, 2015 at 1:29 AM, Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
> Hi Arman,
>
> On Thu, Feb 5, 2015 at 12:05 AM, Arman Uguray <armansito@chromium.org> wrote:
>> Hi Luiz,
>>
>>> On Wed, Feb 4, 2015 at 6:26 AM, Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
>>> Hi Arman,
>>>
>>> On Wed, Feb 4, 2015 at 5:58 AM, Arman Uguray <armansito@chromium.org> wrote:
>>>> Certain peripherals can expose a GATT characteristic with the
>>>> notify/indicate property but with no Client Characteristic Configuration
>>>> descriptor. Since bt_gatt_client_register_notify enforces a CCC as a
>>>> requirement, it returns an error for such characteristics.
>>>
>>> Do you have some evidence of this? I don't mind not being too strict
>>> while receiving but we need to document why we are doing these
>>> changes,...
>>
>> I came across this while testing with a Nexus 6 device running Android
>> L. The application exposes a "Service Changed" characteristic with no
>> CCC, which is causing bt_gatt_client's initialization sequence to fail
>> (since it assumes that there will be a CCC and treats the absence as
>> an error). This seemed strange to me, so I did some digging, and the
>> Bluetooth developer page actually lists "None" for "Descriptors"
>> (https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.generic_attribute.xml)
>> of this characteristic.
>>
>> However, this seemed incorrect to me, so I checked the Core Spec v4.2,
>> Vol 3, Part G, Section 7.1 "Service Changed", which states: "This
>> [Service Changed] Characteristic Value shall be configured to be
>> indicated, using the Client Characteristic Configuration descriptor by
>> a client. Indications caused by changes to the Service Changed
>> Characteristic Value shall be considered lost if the client has not
>> enabled indications in the Client Configuration Characteristic
>> Descriptor." Which means that the CCC is certainly mandatory, so this
>> must be an error in the developer page.
>
> It is probably a bug, and in this case the device might not even be
> able to qualify since service changed has 2 tests: TP/GAS/CL/BV-01-C
> and TP/GAS/SR/BV-01-C. We should probably communicate that the
> documentation needs fixing and the TS should probably be updated as
> well to reflect CCC is mandatory in this case.
>
>> Regardless, though, even if this is an error in the BlueDroid stack,
>> it means that some devices may have incorrectly implemented the spec
>> here, so we should probably tolerate this. BlueZ's server
>> implementation SHOULD regardless expose this descriptor, but the
>> client IMO shouldn't fail if the CCC generally doesn't exist.
>
> Provided the SIG don't come with invalid tests for service changed
> that is probably okay for the client, for the server I don't how it
> would pass TP/GAS/SR/BV-01-C without CCC except if PTS is ignoring the
> lack of CCC which IMO defeats the purpose of the test. So the argument
> for service changed is probably not valid here, but for custom
> attributes where there is no tests to validate the implemenation
> perhaps you have a point, but note that it still will need to set
> BT_GATT_CHRC_PROP_NOTIFY and BT_GATT_CHRC_PROP_INDICATE, btw such
> policies perhaps should be done directly in shared/gatt-client.c so we
> don't need have duplicated logic for Android.
>
>>
>>> ...and btw this perhaps makes my argument of enabling
>>> notification by default even stronger since if such devices really
>>> exists they might be notifying us since the beginning.
>>>
>>
>> Hmm, I don't know how this makes that argument any stronger.
>> bt_gatt_client already automatically registers for Service Changed,
>> and for external applications we still want to only notify if an app
>> is interested (which they can do now across connections).
>>
>> Generally, I want to treat this as a corner-case, since it's not
>> really spec compliant behavior. Either way, it's probably not that bad
>> to have this fix so that we don't bork out immediately with devices
>> that behave incorrectly. Actually, it might be even more spec
>> compliant for us to simply ignore the Service Changed characteristic
>> if it's discovered but doesn't have a CCC, since the spec says
>> "changes to the Service Changed Characteristic Value shall be
>> considered lost if the client has not enabled indications in the
>> Client Configuration Characteristic Descriptor", which would end up
>> being a simpler fix on our end.
>>
>> Either way, I'll wait for your response to determine what the right
>> behavior is here.
>
> For service changed perhaps, but since we are using
> bt_gatt_client_register_notify for any notification we would need to
> have a special case for service changed alone so I would not bother
> since broken stacks in this respect will probably not pass the
> qualification if the test spec where implemented properly, for any
> other char I would not be so strict since some OS may allow adding a
> characteristic with notifiy/indicate bits sets but without a CCC to
> enable them, actually Android probably allows that to happen already,
> sadly I think even our BlueZ for Android currently allows that.
>
>>> The other problem I now see is that there is probably no way to rate
>>> limit the notification, because they are profile specific, so if you
>>> had a problem with too much traffic on D-Bus this may already happen
>>> even if we register the notification on demand.
>>>
>>
>> Right, though I guess this is a separate issue that can be dealt with
>> at the D-Bus API level. shared/gatt-client has to still send all
>> notifications up, otherwise things like HoG wouldn't work well with a
>> rate limit. Anyway, we can do this at a D-Bus API level separately.
>> I'll add this to the general TODO list.
>>
>>>> This patch fixes this behavior so that bt_gatt_client_register_notify
>>>> immediately registers the callback and returns success for
>>>> characteristics with no CCC.
>>>> ---
>>>>  src/shared/gatt-client.c | 43 +++++++++++++++++++++++++------------------
>>>>  1 file changed, 25 insertions(+), 18 deletions(-)
>>>>
>>>> diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
>>>> index 5edb991..d0fc054 100644
>>>> --- a/src/shared/gatt-client.c
>>>> +++ b/src/shared/gatt-client.c
>>>> @@ -88,6 +88,7 @@ struct bt_gatt_client {
>>>>          * the remote peripheral.
>>>>          */
>>>>         unsigned int svc_chngd_ind_id;
>>>> +       bool svc_chngd_registered;
>>>>         struct queue *svc_chngd_queue;  /* Queued service changed events */
>>>>         bool in_svc_chngd;
>>>>
>>>> @@ -234,12 +235,6 @@ static struct notify_chrc *notify_chrc_create(struct bt_gatt_client *client,
>>>>                                                         &properties, NULL))
>>>>                         return NULL;
>>>>
>>>> -       /* Find the CCC characteristic */
>>>> -       ccc = NULL;
>>>> -       gatt_db_service_foreach_desc(attr, find_ccc, &ccc);
>>>> -       if (!ccc)
>>>> -               return NULL;
>>>> -
>>>>         chrc = new0(struct notify_chrc, 1);
>>>>         if (!chrc)
>>>>                 return NULL;
>>>> @@ -250,8 +245,17 @@ static struct notify_chrc *notify_chrc_create(struct bt_gatt_client *client,
>>>>                 return NULL;
>>>>         }
>>>>
>>>> +       /*
>>>> +        * Find the CCC characteristic. Some characteristics that allow
>>>> +        * notifications may not have a CCC descriptor. We treat these as
>>>> +        * automatically successful.
>>>> +        */
>>>> +       ccc = NULL;
>>>> +       gatt_db_service_foreach_desc(attr, find_ccc, &ccc);
>>>> +       if (ccc)
>>>> +               chrc->ccc_handle = gatt_db_attribute_get_handle(ccc);
>>>> +
>>>>         chrc->value_handle = value_handle;
>>>> -       chrc->ccc_handle = gatt_db_attribute_get_handle(ccc);
>>>>         chrc->properties = properties;
>>>>
>>>>         queue_push_tail(client->notify_chrcs, chrc);
>>>> @@ -1020,6 +1024,7 @@ static void service_changed_reregister_cb(uint16_t att_ecode, void *user_data)
>>>>                 util_debug(client->debug_callback, client->debug_data,
>>>>                         "Re-registered handler for \"Service Changed\" after "
>>>>                         "change in GATT service");
>>>> +               client->svc_chngd_registered = true;
>>>>                 return;
>>>>         }
>>>>
>>>> @@ -1089,6 +1094,7 @@ static void service_changed_complete(struct discovery_op *op, bool success,
>>>>         /* The GATT service was modified. Re-register the handler for
>>>>          * indications from the "Service Changed" characteristic.
>>>>          */
>>>> +       client->svc_chngd_registered = false;
>>>>         client->svc_chngd_ind_id = bt_gatt_client_register_notify(client,
>>>>                                         gatt_db_attribute_get_handle(attr),
>>>>                                         service_changed_reregister_cb,
>>>> @@ -1197,6 +1203,7 @@ static void service_changed_register_cb(uint16_t att_ecode, void *user_data)
>>>>                 goto done;
>>>>         }
>>>>
>>>> +       client->svc_chngd_registered = true;
>>>>         client->ready = true;
>>>>         success = true;
>>>>         util_debug(client->debug_callback, client->debug_data,
>>>> @@ -1239,13 +1246,16 @@ static void init_complete(struct discovery_op *op, bool success,
>>>>                                         service_changed_register_cb,
>>>>                                         service_changed_cb,
>>>>                                         client, NULL);
>>>> -       client->ready = false;
>>>> +
>>>> +       if (!client->svc_chngd_registered)
>>>> +               client->ready = false;
>>>>
>>>>         if (client->svc_chngd_ind_id)
>>>>                 return;
>>>>
>>>>         util_debug(client->debug_callback, client->debug_data,
>>>>                         "Failed to register handler for \"Service Changed\"");
>>>> +       success = false;
>>>>
>>>>  fail:
>>>>         util_debug(client->debug_callback, client->debug_data,
>>>> @@ -1423,18 +1433,17 @@ static void complete_unregister_notify(void *data)
>>>>          */
>>>>         if (notify_data->att_id) {
>>>>                 bt_att_cancel(notify_data->client->att, notify_data->att_id);
>>>> -               notify_data_unref(notify_data);
>>>> -               return;
>>>> +               goto done;
>>>>         }
>>>>
>>>> -       if (__sync_sub_and_fetch(&notify_data->chrc->notify_count, 1)) {
>>>> -               notify_data_unref(notify_data);
>>>> -               return;
>>>> -       }
>>>> +       if (__sync_sub_and_fetch(&notify_data->chrc->notify_count, 1) ||
>>>> +                                               !notify_data->chrc->ccc_handle)
>>>> +               goto done;
>>>>
>>>>         if (notify_data_write_ccc(notify_data, false, disable_ccc_callback))
>>>>                 return;
>>>>
>>>> +done:
>>>>         notify_data_unref(notify_data);
>>>>  }
>>>>
>>>> @@ -2571,9 +2580,7 @@ unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
>>>>         /* Add the handler to the bt_gatt_client's general list */
>>>>         queue_push_tail(client->notify_list, notify_data);
>>>>
>>>> -       /* Assign an ID to the handler and notify the caller that it was
>>>> -        * successfully registered.
>>>> -        */
>>>> +       /* Assign an ID to the handler. */
>>>>         if (client->next_reg_id < 1)
>>>>                 client->next_reg_id = 1;
>>>>
>>>> @@ -2591,7 +2598,7 @@ unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
>>>>         /*
>>>>          * If the ref count is not zero, then notifications are already enabled.
>>>>          */
>>>> -       if (chrc->notify_count > 0) {
>>>> +       if (chrc->notify_count > 0 || !chrc->ccc_handle) {
>>>>                 complete_notify_request(notify_data);
>>>>                 return notify_data->id;
>>>>         }
>>>> --
>>>> 2.2.0.rc0.207.ga3a616c
>>>>
>>>> --
>>>> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
>>>> the body of a message to majordomo@vger.kernel.org
>>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>
>>>
>>>
>>> --
>>> Luiz Augusto von Dentz
>>
>> Thanks,
>> Arman
>
>
>
> --
> Luiz Augusto von Dentz

ping

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

* Re: [PATCH] shared/gatt: Allow register_notify without CCC
  2015-02-06 14:19       ` Arman Uguray
@ 2015-02-06 14:58         ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 6+ messages in thread
From: Luiz Augusto von Dentz @ 2015-02-06 14:58 UTC (permalink / raw)
  To: Arman Uguray; +Cc: linux-bluetooth

Hi Arman,

On Fri, Feb 6, 2015 at 4:19 PM, Arman Uguray <armansito@chromium.org> wrote:
> Hi Luiz,
>
>> On Thu, Feb 5, 2015 at 1:29 AM, Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
>> Hi Arman,
>>
>> On Thu, Feb 5, 2015 at 12:05 AM, Arman Uguray <armansito@chromium.org> wrote:
>>> Hi Luiz,
>>>
>>>> On Wed, Feb 4, 2015 at 6:26 AM, Luiz Augusto von Dentz <luiz.dentz@gmail.com> wrote:
>>>> Hi Arman,
>>>>
>>>> On Wed, Feb 4, 2015 at 5:58 AM, Arman Uguray <armansito@chromium.org> wrote:
>>>>> Certain peripherals can expose a GATT characteristic with the
>>>>> notify/indicate property but with no Client Characteristic Configuration
>>>>> descriptor. Since bt_gatt_client_register_notify enforces a CCC as a
>>>>> requirement, it returns an error for such characteristics.
>>>>
>>>> Do you have some evidence of this? I don't mind not being too strict
>>>> while receiving but we need to document why we are doing these
>>>> changes,...
>>>
>>> I came across this while testing with a Nexus 6 device running Android
>>> L. The application exposes a "Service Changed" characteristic with no
>>> CCC, which is causing bt_gatt_client's initialization sequence to fail
>>> (since it assumes that there will be a CCC and treats the absence as
>>> an error). This seemed strange to me, so I did some digging, and the
>>> Bluetooth developer page actually lists "None" for "Descriptors"
>>> (https://developer.bluetooth.org/gatt/services/Pages/ServiceViewer.aspx?u=org.bluetooth.service.generic_attribute.xml)
>>> of this characteristic.
>>>
>>> However, this seemed incorrect to me, so I checked the Core Spec v4.2,
>>> Vol 3, Part G, Section 7.1 "Service Changed", which states: "This
>>> [Service Changed] Characteristic Value shall be configured to be
>>> indicated, using the Client Characteristic Configuration descriptor by
>>> a client. Indications caused by changes to the Service Changed
>>> Characteristic Value shall be considered lost if the client has not
>>> enabled indications in the Client Configuration Characteristic
>>> Descriptor." Which means that the CCC is certainly mandatory, so this
>>> must be an error in the developer page.
>>
>> It is probably a bug, and in this case the device might not even be
>> able to qualify since service changed has 2 tests: TP/GAS/CL/BV-01-C
>> and TP/GAS/SR/BV-01-C. We should probably communicate that the
>> documentation needs fixing and the TS should probably be updated as
>> well to reflect CCC is mandatory in this case.
>>
>>> Regardless, though, even if this is an error in the BlueDroid stack,
>>> it means that some devices may have incorrectly implemented the spec
>>> here, so we should probably tolerate this. BlueZ's server
>>> implementation SHOULD regardless expose this descriptor, but the
>>> client IMO shouldn't fail if the CCC generally doesn't exist.
>>
>> Provided the SIG don't come with invalid tests for service changed
>> that is probably okay for the client, for the server I don't how it
>> would pass TP/GAS/SR/BV-01-C without CCC except if PTS is ignoring the
>> lack of CCC which IMO defeats the purpose of the test. So the argument
>> for service changed is probably not valid here, but for custom
>> attributes where there is no tests to validate the implemenation
>> perhaps you have a point, but note that it still will need to set
>> BT_GATT_CHRC_PROP_NOTIFY and BT_GATT_CHRC_PROP_INDICATE, btw such
>> policies perhaps should be done directly in shared/gatt-client.c so we
>> don't need have duplicated logic for Android.
>>
>>>
>>>> ...and btw this perhaps makes my argument of enabling
>>>> notification by default even stronger since if such devices really
>>>> exists they might be notifying us since the beginning.
>>>>
>>>
>>> Hmm, I don't know how this makes that argument any stronger.
>>> bt_gatt_client already automatically registers for Service Changed,
>>> and for external applications we still want to only notify if an app
>>> is interested (which they can do now across connections).
>>>
>>> Generally, I want to treat this as a corner-case, since it's not
>>> really spec compliant behavior. Either way, it's probably not that bad
>>> to have this fix so that we don't bork out immediately with devices
>>> that behave incorrectly. Actually, it might be even more spec
>>> compliant for us to simply ignore the Service Changed characteristic
>>> if it's discovered but doesn't have a CCC, since the spec says
>>> "changes to the Service Changed Characteristic Value shall be
>>> considered lost if the client has not enabled indications in the
>>> Client Configuration Characteristic Descriptor", which would end up
>>> being a simpler fix on our end.
>>>
>>> Either way, I'll wait for your response to determine what the right
>>> behavior is here.
>>
>> For service changed perhaps, but since we are using
>> bt_gatt_client_register_notify for any notification we would need to
>> have a special case for service changed alone so I would not bother
>> since broken stacks in this respect will probably not pass the
>> qualification if the test spec where implemented properly, for any
>> other char I would not be so strict since some OS may allow adding a
>> characteristic with notifiy/indicate bits sets but without a CCC to
>> enable them, actually Android probably allows that to happen already,
>> sadly I think even our BlueZ for Android currently allows that.
>>
>>>> The other problem I now see is that there is probably no way to rate
>>>> limit the notification, because they are profile specific, so if you
>>>> had a problem with too much traffic on D-Bus this may already happen
>>>> even if we register the notification on demand.
>>>>
>>>
>>> Right, though I guess this is a separate issue that can be dealt with
>>> at the D-Bus API level. shared/gatt-client has to still send all
>>> notifications up, otherwise things like HoG wouldn't work well with a
>>> rate limit. Anyway, we can do this at a D-Bus API level separately.
>>> I'll add this to the general TODO list.
>>>
>>>>> This patch fixes this behavior so that bt_gatt_client_register_notify
>>>>> immediately registers the callback and returns success for
>>>>> characteristics with no CCC.
>>>>> ---
>>>>>  src/shared/gatt-client.c | 43 +++++++++++++++++++++++++------------------
>>>>>  1 file changed, 25 insertions(+), 18 deletions(-)
>>>>>
>>>>> diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
>>>>> index 5edb991..d0fc054 100644
>>>>> --- a/src/shared/gatt-client.c
>>>>> +++ b/src/shared/gatt-client.c
>>>>> @@ -88,6 +88,7 @@ struct bt_gatt_client {
>>>>>          * the remote peripheral.
>>>>>          */
>>>>>         unsigned int svc_chngd_ind_id;
>>>>> +       bool svc_chngd_registered;
>>>>>         struct queue *svc_chngd_queue;  /* Queued service changed events */
>>>>>         bool in_svc_chngd;
>>>>>
>>>>> @@ -234,12 +235,6 @@ static struct notify_chrc *notify_chrc_create(struct bt_gatt_client *client,
>>>>>                                                         &properties, NULL))
>>>>>                         return NULL;
>>>>>
>>>>> -       /* Find the CCC characteristic */
>>>>> -       ccc = NULL;
>>>>> -       gatt_db_service_foreach_desc(attr, find_ccc, &ccc);
>>>>> -       if (!ccc)
>>>>> -               return NULL;
>>>>> -
>>>>>         chrc = new0(struct notify_chrc, 1);
>>>>>         if (!chrc)
>>>>>                 return NULL;
>>>>> @@ -250,8 +245,17 @@ static struct notify_chrc *notify_chrc_create(struct bt_gatt_client *client,
>>>>>                 return NULL;
>>>>>         }
>>>>>
>>>>> +       /*
>>>>> +        * Find the CCC characteristic. Some characteristics that allow
>>>>> +        * notifications may not have a CCC descriptor. We treat these as
>>>>> +        * automatically successful.
>>>>> +        */
>>>>> +       ccc = NULL;
>>>>> +       gatt_db_service_foreach_desc(attr, find_ccc, &ccc);
>>>>> +       if (ccc)
>>>>> +               chrc->ccc_handle = gatt_db_attribute_get_handle(ccc);
>>>>> +
>>>>>         chrc->value_handle = value_handle;
>>>>> -       chrc->ccc_handle = gatt_db_attribute_get_handle(ccc);
>>>>>         chrc->properties = properties;
>>>>>
>>>>>         queue_push_tail(client->notify_chrcs, chrc);
>>>>> @@ -1020,6 +1024,7 @@ static void service_changed_reregister_cb(uint16_t att_ecode, void *user_data)
>>>>>                 util_debug(client->debug_callback, client->debug_data,
>>>>>                         "Re-registered handler for \"Service Changed\" after "
>>>>>                         "change in GATT service");
>>>>> +               client->svc_chngd_registered = true;
>>>>>                 return;
>>>>>         }
>>>>>
>>>>> @@ -1089,6 +1094,7 @@ static void service_changed_complete(struct discovery_op *op, bool success,
>>>>>         /* The GATT service was modified. Re-register the handler for
>>>>>          * indications from the "Service Changed" characteristic.
>>>>>          */
>>>>> +       client->svc_chngd_registered = false;
>>>>>         client->svc_chngd_ind_id = bt_gatt_client_register_notify(client,
>>>>>                                         gatt_db_attribute_get_handle(attr),
>>>>>                                         service_changed_reregister_cb,
>>>>> @@ -1197,6 +1203,7 @@ static void service_changed_register_cb(uint16_t att_ecode, void *user_data)
>>>>>                 goto done;
>>>>>         }
>>>>>
>>>>> +       client->svc_chngd_registered = true;
>>>>>         client->ready = true;
>>>>>         success = true;
>>>>>         util_debug(client->debug_callback, client->debug_data,
>>>>> @@ -1239,13 +1246,16 @@ static void init_complete(struct discovery_op *op, bool success,
>>>>>                                         service_changed_register_cb,
>>>>>                                         service_changed_cb,
>>>>>                                         client, NULL);
>>>>> -       client->ready = false;
>>>>> +
>>>>> +       if (!client->svc_chngd_registered)
>>>>> +               client->ready = false;
>>>>>
>>>>>         if (client->svc_chngd_ind_id)
>>>>>                 return;
>>>>>
>>>>>         util_debug(client->debug_callback, client->debug_data,
>>>>>                         "Failed to register handler for \"Service Changed\"");
>>>>> +       success = false;
>>>>>
>>>>>  fail:
>>>>>         util_debug(client->debug_callback, client->debug_data,
>>>>> @@ -1423,18 +1433,17 @@ static void complete_unregister_notify(void *data)
>>>>>          */
>>>>>         if (notify_data->att_id) {
>>>>>                 bt_att_cancel(notify_data->client->att, notify_data->att_id);
>>>>> -               notify_data_unref(notify_data);
>>>>> -               return;
>>>>> +               goto done;
>>>>>         }
>>>>>
>>>>> -       if (__sync_sub_and_fetch(&notify_data->chrc->notify_count, 1)) {
>>>>> -               notify_data_unref(notify_data);
>>>>> -               return;
>>>>> -       }
>>>>> +       if (__sync_sub_and_fetch(&notify_data->chrc->notify_count, 1) ||
>>>>> +                                               !notify_data->chrc->ccc_handle)
>>>>> +               goto done;
>>>>>
>>>>>         if (notify_data_write_ccc(notify_data, false, disable_ccc_callback))
>>>>>                 return;
>>>>>
>>>>> +done:
>>>>>         notify_data_unref(notify_data);
>>>>>  }
>>>>>
>>>>> @@ -2571,9 +2580,7 @@ unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
>>>>>         /* Add the handler to the bt_gatt_client's general list */
>>>>>         queue_push_tail(client->notify_list, notify_data);
>>>>>
>>>>> -       /* Assign an ID to the handler and notify the caller that it was
>>>>> -        * successfully registered.
>>>>> -        */
>>>>> +       /* Assign an ID to the handler. */
>>>>>         if (client->next_reg_id < 1)
>>>>>                 client->next_reg_id = 1;
>>>>>
>>>>> @@ -2591,7 +2598,7 @@ unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
>>>>>         /*
>>>>>          * If the ref count is not zero, then notifications are already enabled.
>>>>>          */
>>>>> -       if (chrc->notify_count > 0) {
>>>>> +       if (chrc->notify_count > 0 || !chrc->ccc_handle) {
>>>>>                 complete_notify_request(notify_data);
>>>>>                 return notify_data->id;
>>>>>         }
>>>>> --
>>>>> 2.2.0.rc0.207.ga3a616c
>>>>>
>>>>> --
>>>>> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
>>>>> the body of a message to majordomo@vger.kernel.org
>>>>> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>>>>
>>>>
>>>>
>>>> --
>>>> Luiz Augusto von Dentz
>>>
>>> Thanks,
>>> Arman
>>
>>
>>
>> --
>> Luiz Augusto von Dentz
>
> ping

This has been applied, I just forgot to comment.



-- 
Luiz Augusto von Dentz

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

end of thread, other threads:[~2015-02-06 14:58 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-02-04  3:58 [PATCH] shared/gatt: Allow register_notify without CCC Arman Uguray
2015-02-04 14:26 ` Luiz Augusto von Dentz
2015-02-04 22:05   ` Arman Uguray
2015-02-05  9:29     ` Luiz Augusto von Dentz
2015-02-06 14:19       ` Arman Uguray
2015-02-06 14:58         ` Luiz Augusto von Dentz

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.