From: Luiz Augusto von Dentz <luiz.von.dentz@xxxxxxxxx> This detects when a client becomes change-unware returning DB Out of Sync error when that happens. --- src/device.c | 2 +- src/gatt-database.c | 75 ++++++++++++++++++++++++++++++++++++++++++--- src/gatt-database.h | 4 +-- 3 files changed, 74 insertions(+), 7 deletions(-) diff --git a/src/device.c b/src/device.c index 79ede3729..060251f66 100644 --- a/src/device.c +++ b/src/device.c @@ -4922,7 +4922,7 @@ static void gatt_server_init(struct btd_device *device, bt_att_set_enc_key_size(device->att, device->ltk_enc_size); bt_gatt_server_set_debug(device->server, gatt_debug, NULL, NULL); - btd_gatt_database_att_connected(database, device->att); + btd_gatt_database_server_connected(database, device->server); } static bool local_counter(uint32_t *sign_cnt, void *user_data) diff --git a/src/gatt-database.c b/src/gatt-database.c index d72f2c781..aecfa0e43 100644 --- a/src/gatt-database.c +++ b/src/gatt-database.c @@ -180,6 +180,8 @@ struct device_state { uint8_t bdaddr_type; unsigned int disc_id; uint8_t cli_feat[1]; + bool change_aware; + bool out_of_sync; struct queue *ccc_states; struct notify *pending; }; @@ -328,6 +330,7 @@ static void att_disconnected(int err, void *user_data) DBG(""); state->disc_id = 0; + state->out_of_sync = false; device = btd_adapter_find_device(state->db->adapter, &state->bdaddr, state->bdaddr_type); @@ -1119,10 +1122,13 @@ static void cli_feat_write_cb(struct gatt_db_attribute *attrib, /* Shall we reallocate the feat array if bigger? */ len = MIN(sizeof(state->cli_feat), len); while (len) { - state->cli_feat[len - 1] |= value[len -1]; + state->cli_feat[len - 1] |= value[len - 1]; len--; } + state->cli_feat[0] &= BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING; + state->change_aware = true; + done: gatt_db_attribute_write_result(attrib, id, ecode); } @@ -1134,12 +1140,22 @@ static void db_hash_read_cb(struct gatt_db_attribute *attrib, { struct btd_gatt_database *database = user_data; const uint8_t *hash; + struct device_state *state; + bdaddr_t bdaddr; + uint8_t bdaddr_type; DBG("Database Hash read"); hash = gatt_db_get_hash(database->db); gatt_db_attribute_read_result(attrib, id, 0, hash, 16); + + if (!get_dst_info(att, &bdaddr, &bdaddr_type)) + return; + + state = find_device_state(database, &bdaddr, bdaddr_type); + if (state) + state->change_aware = true; } static void populate_gatt_service(struct btd_gatt_database *database) @@ -1197,7 +1213,14 @@ static void conf_cb(void *user_data) static void service_changed_conf(void *user_data) { + struct device_state *state = user_data; + DBG(""); + + if (!state) + return; + + state->change_aware = true; } static void state_set_pending(struct device_state *state, struct notify *notify) @@ -1239,6 +1262,14 @@ static void send_notification_to_device(void *data, void *user_data) struct btd_device *device; struct bt_gatt_server *server; + if (notify->conf == service_changed_conf) { + if (device_state->cli_feat[0] & + BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING) { + device_state->change_aware = false; + notify->user_data = device_state; + } + } + ccc = find_ccc_state(device_state, notify->ccc_handle); if (!ccc) return; @@ -3352,6 +3383,40 @@ static const GDBusMethodTable manager_methods[] = { { } }; +static uint8_t server_authorize(struct bt_att *att, uint8_t opcode, + uint16_t handle, void *user_data) +{ + struct btd_gatt_database *database = user_data; + struct device_state *state; + bdaddr_t bdaddr; + uint8_t bdaddr_type; + + if (!get_dst_info(att, &bdaddr, &bdaddr_type)) + return 0; + + /* Skip if there is no device state */ + state = find_device_state(database, &bdaddr, bdaddr_type); + if (!state) + return 0; + + /* Skip if client doesn't support Robust Caching */ + if (!(state->cli_feat[0] & BT_GATT_CHRC_CLI_FEAT_ROBUST_CACHING)) + return 0; + + if (state->change_aware) + return 0; + + if (state->out_of_sync) { + state->out_of_sync = false; + state->change_aware = true; + return 0; + } + + state->out_of_sync = true; + + return BT_ATT_ERROR_DB_OUT_OF_SYNC; +} + struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter) { struct btd_gatt_database *database; @@ -3413,7 +3478,6 @@ struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter) if (!database->db_id) goto fail; - return database; fail: @@ -3442,9 +3506,10 @@ struct gatt_db *btd_gatt_database_get_db(struct btd_gatt_database *database) return database->db; } -void btd_gatt_database_att_connected(struct btd_gatt_database *database, - struct bt_att *att) +void btd_gatt_database_server_connected(struct btd_gatt_database *database, + struct bt_gatt_server *server) { + struct bt_att *att = bt_gatt_server_get_att(server); struct device_state *state; bdaddr_t bdaddr; uint8_t bdaddr_type; @@ -3452,6 +3517,8 @@ void btd_gatt_database_att_connected(struct btd_gatt_database *database, if (!get_dst_info(att, &bdaddr, &bdaddr_type)) return; + bt_gatt_server_set_authorize(server, server_authorize, database); + state = find_device_state(database, &bdaddr, bdaddr_type); if (!state || !state->pending) return; diff --git a/src/gatt-database.h b/src/gatt-database.h index a77a0fb20..154d3243f 100644 --- a/src/gatt-database.h +++ b/src/gatt-database.h @@ -23,9 +23,9 @@ struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter); void btd_gatt_database_destroy(struct btd_gatt_database *database); struct gatt_db *btd_gatt_database_get_db(struct btd_gatt_database *database); -void btd_gatt_database_att_connected(struct btd_gatt_database *database, - struct bt_att *att); void btd_gatt_database_att_disconnected(struct btd_gatt_database *database, struct btd_device *device); +void btd_gatt_database_server_connected(struct btd_gatt_database *database, + struct bt_gatt_server *server); void btd_gatt_database_restore_svc_chng_ccc(struct btd_gatt_database *database); -- 2.17.2