Return-Path: MIME-Version: 1.0 In-Reply-To: References: Date: Wed, 19 Nov 2014 19:08:32 -0800 Message-ID: Subject: Re: RFC: Populating shared/gatt-client from storage. From: Arman Uguray To: BlueZ development Content-Type: text/plain; charset=UTF-8 Sender: linux-bluetooth-owner@vger.kernel.org List-ID: Hi, > On Wed, Nov 19, 2014 at 1:48 PM, Arman Uguray wrote: > Hi all, > > In the case where bluez is bonded with a remote device we don't want > to always perform service discovery, but instead to cache the > attribute data permanently and just use that on reconnections. > Currently shared/gatt-client automatically discovers all services when > created (via bt_gatt_client_new) so we need to add support to populate > a shared/gatt-client based on cached data in storage, since a > bt_gatt_client instance acts as our in-memory attribute cache for the > client role. > > I initially thought about storing everything in INI format and loading > things directly from a file, via some function like > bt_gatt_client_new_from_storage. The biggest problem with this > approach is parsing the INI format; since we don't want to pull in any > external dependencies into shared/gatt (e.g. glib) we would possibly > have to write our own key-value file parser for shared/ which seems > like overkill. We could possibly invent some simpler blob format for > shared/gatt-client but people brought to my attention on IRC that we > may want to leave the exact storage format to the upper-layer, e.g. > desktop and android versions of bluetoothd may want to use different > formats for their attribute cache that's consistent with the platform. > > So I decided to go with something generic instead. We would basically > add functions to manually populate a gatt-client and mark the client > as ready only after it's services have been populated. So we would > have these API changes: > > 1. A new argument to bt_gatt_client_new, to tell it not to perform > discovery. Something like "bool discover_services". > > /* Don't perform discovery */ > bt_gatt_client_new(att, mtu, false); > > This would basically just leave the structure in "init" state and not > become ready. > > 2. We would then add a new structure to populate a gatt-client. > > struct bt_gatt_populate { .. }; /* Probably something with a better name */ > > bool bt_gatt_populate_start(struct bt_gatt_populate *, struct > bt_gatt_client *); > bool bt_gatt_populate_add_service(....) > bool bt_gatt_populate_add_characteristic(...) > bool bt_gatt_populate_add_descriptor(....) > bool bt_gatt_populate_add_include(....) > bool bt_gatt_populate_cancel(...) > bool bt_gatt_populate_commit(struct bt_gatt_populate *); > > The start function would tell gatt-client that it's being populated so > it would prevent this from being called twice. All of the _add_* > functions would create a temporary tree owned by the populate > structure, using the same internal structures defined in > shared/gatt-client.c. Calling bt_gatt_populate_commit would then pass > the ownership of the services to the bt_gatt_client, and > bt_gatt_client would perform the MTU exchange, subscribe to > notifications from the "Service Changed" characteristic if one exists, > mark itself as ready and invoke the ready handler. > > If the upper-layer wants to store the current contents of gatt-client, > they can do so by iterating through its services and caching them to a > file directly. This way, shared/gatt-client would remain agnostic to > any storage format. > > Comments requested. > Arman I just sent out a patch as RFC that implements this. Cheers, Arman