Chapter 4. GATT (Services and Characteristics)
The Generic Attribute Profile (GATT) establishes in detail how to exchange all profile and user data over a BLE connection. In contrast with GAP (Chapter 3), which defines the low-level interactions with devices, GATT deals only with actual data transfer procedures and formats.
GATT also provides the reference framework for all GATT-based profiles (discussed in SIG-defined GATT-based profiles), which cover precise use cases and ensure interoperability between devices from different vendors. All standard BLE profiles are therefore based on GATT and must comply with it to operate correctly. This makes GATT a key section of the BLE specification, because every single item of data relevant to applications and users must be formatted, packed, and sent according to its rules.
GATT uses the Attribute Protocol (detailed in Attribute Protocol (ATT)) as its transport protocol to exchange data between devices. This data is organized hierarchically in sections called services, which group conceptually related pieces of user data called characteristics. This determines many fundamental aspects of GATT discussed in this chapter.
Roles
As with any other protocol or profile in the Bluetooth specification, GATT starts by defining the roles that interacting devices can adopt:
- Client
- The GATT client corresponds to the ATT client discussed in Attribute Protocol (ATT). It sends requests to a server and receives responses (and server-initiated updates) from it. The GATT client does not know anything in advance about the serverâs attributes, so it must first inquire about the presence and nature of those attributes by performing service discovery. After completing service discovery, it can then start reading and writing attributes found in the server, as well as receiving server-initiated updates.
- Server
- The GATT server corresponds to the ATT server discussed in Attribute Protocol (ATT). It receives requests from a client and sends responses back. It also sends server-initiated updates when configured to do so, and it is the role responsible for storing and making the user data available to the client, organized in attributes. Every BLE device sold must include at least a basic GATT server that can respond to client requests, even if only to return an error response.
It is worth mentioning once more that GATT roles are both completely independent of GAP roles (see Roles) and also concurrently compatible with each other. That means that both a GAP central and a GAP peripheral can act as a GATT client or server, or even act as both at the same time.
UUIDs
A universally unique identifier (UUID) is a 128-bit (16 bytes) number that is guaranteed (or has a high probability) to be globally unique. UUIDs are used in many protocols and applications other than Bluetooth, and their format, usage, and generation is specified in ITU-T Rec. X.667, alternatively known as ISO/IEC 9834-8:2005.
For efficiency, and because 16 bytes would take a large chunk of the 27-byte data payload length of the Link Layer, the BLE specification adds two additional UUID formats: 16-bit and 32-bit UUIDs. These shortened formats can be used only with UUIDs that are defined in the Bluetooth specification (i.e., that are listed by the Bluetooth SIG as standard Bluetooth UUIDs).
To reconstruct the full 128-bit UUID from the shortened version, insert the 16- or 32-bit short value (indicated by xxxxxxxx
, including leading zeros) into the Bluetooth Base UUID:
xxxxxxxx-0000-1000-8000-00805F9B34FB
The SIG provides (shortened) UUIDs for all the types, services, and profiles that it defines and specifies. But if your application needs its own, either because the ones offered by the SIG do not cover your requirements or because you want to implement a new use case not previously considered in the profile specifications, you can generate them using the ITUâs UUID generation page.
Shortening is not available for UUIDs that are not derived from the Bluetooth Base UUID (commonly called vendor-specific UUIDs). In these cases, youâll need to use the full 128-bit UUID value at all times.
Attributes
Attributes are the smallest data entity defined by GATT (and ATT). They are addressable pieces of information that can contain relevant user data (or metadata) about the structure and grouping of the different attributes contained within the server. Both GATT and ATT can work only with attributes, so for clients and servers to interact, all information must be organized in this form.
Conceptually, attributes are always located on the server and accessed (and potentially modified) by the client. The specification defines attributes only conceptually, and it does not force the ATT and GATT implementations to use a particular internal storage format or mechanism. Because attributes contain both static definitions of invariable nature and also actual user (often sensor) data that is bound to change rapidly with time (as discussed in Attribute and Data Hierarchy), attributes are usually stored in a mixture of nonvolatile memory and RAM.
Each and every attribute contains information about the attribute itself and then the actual data, in the fields described in the following sections.
Handle
The attribute handle is a unique 16-bit identifier for each attribute on a particular GATT server. It is the part of each attribute that makes it addressable, and it is guaranteed not to change (with the caveats described in Attribute Caching) between transactions or, for bonded devices, even across connections. Because value 0x0000
denotes an invalid handle, the amount of handles available to every GATT server is 0xFFFE (65535)
, although in practice, the number of attributes in a server is typically closer to a few dozen.
Note
Whenever used in the context of attribute handles, the term handle range refers to all attributes with handles contained between two given boundaries. For example, handle range 0x0100-0x010A
would refer to any attribute with a handle between 0x0100
and 0x010A
.
Within a GATT server, the growing values of handles determine the ordered sequence of attributes that a client can access. But gaps between handles are allowed, so a client cannot rely on a contiguous sequence to guess the location of the next attribute. Instead, the client must use the discovery feature (Service and Characteristic Discovery) to obtain the handles of the attributes it is interested in.
Type
The attribute type is nothing other than a UUID (see UUIDs). This can be a 16-, 32-, or 128-bit UUID, taking up 2, 4, or 16 bytes, respectively. The type determines the kind of data present in the value of the attribute, and mechanisms are available to discover attributes based exclusively on their type (see Service and Characteristic Discovery).
Although the attribute type is always a UUID, many kinds of UUIDs can be used to fill in the type. They can be standard UUIDs that determine the layout of the GATT serverâs attribute hierarchy (further discussed in Attribute and Data Hierarchy), such as the service or characteristic UUIDs, profile UUIDs that specify the kind of data contained in the attribute, such as Heart Rate Measurement or Temperature, and even proprietary, vendor-specific UUIDs, the meaning of which is assigned by the vendor and depends on the implementation.
Permissions
Permissions are metadata that specify which ATT operations (see ATT operations) can be executed on each particular attribute and with which specific security requirements.
ATT and GATT define the following permissions:
- Access Permissions
Similar to file permissions, access permissions determine whether the client can read or write (or both) an attribute value (introduced in Value). Each attribute can have one of the following access permissions:
- Encryption
Determines whether a certain level of encryption is required for this attribute to be accessed by the client. (See Authentication, Security Modes and Procedures, and Security Modes for more information on authentication and encryption.) These are the allowed encryption permissions, as defined by GATT:
- No encryption required (Security Mode 1, Level 1)
- The attribute is accessible on a plain-text, non-encrypted connection.
- Unauthenticated encryption required (Security Mode 1, Level 2)
- The connection must be encrypted to access this attribute, but the encryption keys do not need to be authenticated (although they can be).
- Authenticated encryption required (Security Mode 1, Level 3)
- The connection must be encrypted with an authenticated key to access this attribute.
- Authorization
Determines whether user permission (also known as authorization, as discussed in Security Modes and Procedures) is required to access this attribute. An attribute can choose only between requiring or not requiring authorization:
- No authorization required
- Access to this attribute does not require authorization.
- Authorization required
- Access to this attribute requires authorization.
All permissions are independent from each other and can be freely combined by the server, which stores them in a per-attribute basis.
Value
The attribute value holds the actual data content of the attribute. There are no restrictions on the type of data it can contain (you can imagine it as a non-typed buffer that can be cast to whatever the actual type is, based on the attribute type), although its maximum length is limited to 512 bytes by the specification.
As discussed in Attribute and Data Hierarchy, depending on the attribute type, the value can hold additional information about attributes themselves or actual, useful, user-defined application data. This is the part of an attribute that a client can freely access (with the proper permissions permitting) to both read and write. All other entities make up the structure of the attribute and cannot be modified or accessed directly by the client (although the client uses the handle and UUID indirectly in most of the exchanges with the server).
You can think of the whole set of attributes contained in a GATT server as a table (such as Table 4-1), with each row representing a single attribute and each column representing the different parts that actually constitute an attribute.
Handle | Type | Permissions | Value | Value length |
0x0201 | UUID1 (16-bit) | Read only, no security |
| 2 |
0x0202 | UUID2 (16-bit) | Read only, no security |
| 2 |
0x0215 | UUID3 (16-bit) | Read/write, authorization required | âa readable UTF-8 stringâ | 23 |
0x030C | UUID4 (128-bit) | Write only, no security |
| 4 |
0x030D | UUID5 (128-bit) | Read/write, authenticated encryption required |
| 8 |
0x031A | UUID1 (16-bit) | Read only, no security |
| 2 |
In this ficticious GATT server, the attributes it contains are represented as rows of a simple table. This particular GATT server happens to host only five attributes (a rather low number when compared to real-world devices). Note that, as mentioned earlier in this section, the handles of the different attributes do not need to be immediately consecutive, but the ordinal sequence must progress increasingly, as in this example.
The Value column of the table is intended to mirror the high diversity of formats that attribute values can contain in the different GATT-based profiles. The attributes with handles 0x0201
, 0x0202
, and 0x031A
contain 16-bit integers in their respective value fields. The attribute with handle 0x0215
contains a UTF-8 string, 0x030C
contains a 4-byte buffer, and 0x030D
holds an IEEE-754 64-bit floating point number in its value field.
Attribute and Data Hierarchy
Athough the Bluetooth specification defines attributes in the ATT section, that is as far as ATT goes when it comes to them. ATT operates in attribute terms and relies on all the concepts exposed in Attributes to provide a series of precise protocol data units (PDUs, commonly known as packets) that permit a client to access the attributes on a server.
GATT goes further to establish a strict hierarchy to organize attributes in a reusable and practical manner, allowing the access and retrieval of information between client and server to follow a concise set of rules that together consitute the framework used by all GATT-based profiles.
Figure 4-1 illustrates the data hierarchy introduced by GATT.
The attributes in a GATT server are grouped into services, each of which can contain zero or more characteristics. These characteristics, in turn, can include zero or more descriptors. This hierarchy is strictly enforced for any device claiming GATT compatibility (essentially, all BLE devices sold), which means that all attributes in a GATT server are included in one of these three categories, with no exceptions. No dangling attributes can live outside of this hierarchy, as exchanging data between BLE devices depends on it.
For most types of data in the GATT hierarchy, it is important to differentiate between their definition (the whole group of attributes that make it up) and the declaration. The declaration is a single attribute that is always placed first (in increasing handle order) within the definition and that introduces most of the metadata about the data that follows. All declarations have read-only permissions with no security required, because they cannot contain sensitive data. They are only structural attributes that allow the client to find out and discover the layout and nature of the attributes on the server.
Services
GATT services group conceptually related attributes in one common section of the attribute information set in the GATT server. The specification refers to all the attributes within a single service as the service definition. Therefore, a GATT serverâs attributes are in fact a succession of service definitions, each one starting with a single attribute that marks the beginning of a service (aptly named a service declaration.) This attributeâs type and value format is strictly specified in GATT, as shown in Table 4-2.
Handle | Type | Permissions | Value | Value length |
| UUIDprimary service or UUIDsecondary service | Read Only | Service UUID | 2, 4, or 16 bytes |
In the declaration shown in Table 4-2, UUIDprimary service (0x2800
) and UUIDsecondary service (0x2801
) refer to standard, SIG-assigned UUIDs that are used as the exclusive type to introduce a service. They are naturally 16-bit UUIDs (because they are fundamental ones defined by the specfication).
The difference between primary and secondary services is important to note. A primary service is the standard type of GATT service that includes relevant, standard functionality exposed by the GATT server. A secondary service, on the other hand, is intended to be included only in other primary services and makes sense only as its modifier, having no real meaning on its own. In practice, secondary services are rarely used.
The value of the service declaration attribute itself contains a UUID (as mentioned in Value, the value of an attribute can be any data type), this time corresponding to the UUID of the actual service that this declaration introduces.
Although the service declaration must always be the first attribute of the service, many others can follow it before the next service declaration, usually in the form of characteristics and descriptors.
Conceptually, you could think of a GATT service as a class in any modern object-oriented language, complete with instantiation, because a service can be instantiated multiple times within a single GATT server (however, this is not a common occurrence and most services would therefore be akin to singletons).
Inside a service definition (that is to say, inside a service), you can add one or more references to another services, using include definitions. Include definitions consist of a single attribute (the include declaration) that contains all the details required for the client to reference the included service.
Included services can help avoid duplicating data in a GATT server. If a service will be referenced by other services, you can use this mechanism to save memory and simplify the layout of the GATT server. In the previous analogy with classes and objects, you could see include definitions as pointers or references to an existing object instance.
Table 4-3 shows the include declaration attribute with all its fields.
Handle | Type | Permissions | Value | Value length |
| UUIDinclude | Read only | Included service handle, end group handle, Included Service UUID | 6, 8, or 20 bytes |
Again, the UUIDinclude (0x2802
) is a special SIG-assigned UUID used exclusively in include declarations, and the value field in this case contains both the start and end handles of the include service, as well as its UUID.
Characteristics
You can understand characteristics as containers for user data. They always include at least two attributes: the characteristic declaration (which provides metadata about the actual user data) and the characteristic value (which is a full attribute that contains the user data in its value field).
Additionally, the characteristic value can be followed by descriptors, which further expand on the metadata contained in the characteristic declaration. The declaration, value, and any descriptors together form the characteristic definition, which is the bundle of attributes that make up a single characteristic.
Table 4-4 shows the structure of the first two attributes of every single characteristic.
Handle | Type | Permissions | Value | Value length |
| UUIDcharacteristic | Read only | Properties, value handle ( | 5, 7, or 19 bytes |
| Characteristic UUID | Any | Actual value | Variable |
All GATT characteristics are always part of a service, and can therefore always be found enclosed in one.
Characteristic declaration attribute
Once again, the characteristic declaration attributeâs type UUID (0x2803
) is a standardized, unique UUID used exclusively to denote the beginning of characteristics. As with all other declarations (such as service and include), this attribute has read-only permissions, because clients are allowed only to retrieve its value but in no case modify it.
Table 4-5 lists the different items concatenated within the characteristic declarationâs attribute value.
Name | Length in bytes | Description |
Characteristic Properties | 1 | A bitfield listing the permitted operations on this characteristic |
Characteristic Value Handle | 2 | The handle of the attribute containing the characteristic value |
Characteristic UUID | 2, 4, or 16 | The UUID for this particular characteristic |
These three fields are contained in a characteristic declaration attribute value:
- Characteristic Properties
- This 8-bit bitfield, along with the additional two bits in the extended properties descriptor (discussed in Extended Properties Descriptor), contains the operations and procedures that can be used with this characteristic. Each of those 10 properties is encoded as a single bit on the bitfield shown in Table 4-6.
The client can read those properties to find out which operations it is allowed to perform on the characteristic. This is particularly important for the Notify and Indicate properties, because these operations are initiated by the server (further detailed in Server-Initiated Updates) but require the client to enable them first by using the descriptor described in Client Characteristic Configuration Descriptor.
- Characteristic Value Handle
-
These two bytes contain the attribute handle of the attribute that contains the actual value of the characteristic. Although itâs often the case, you should never assume that this handle will be contiguous (i.e.,
0xNNNN+1
) to the one containing the declaration. - Characteristic UUID
- The UUID of the particular characteristic, this can be either a SIG-approved UUID (when making use of the dozens of characteristic types included in the standard profiles) or a 128-bit vendor specific UUID otherwise.
Continuing with the class and object-orientation analogy, characteristics are like individual fields or properties in that class, and a profile is like an application that makes use of one or more classes for a specific need or purpose.
Characteristic value attribute
Finally, the characteristic value attribute contains the actual user data that the client can read from and write to for practical information exchanges. The type for this attribute is always the same UUID found in the characteristicâs declaration value field (as shown in Characteristic declaration attribute). So, characteristic value attributes no longer have types of services or characteristics, but rather concrete, specific UUIDs that can refer to a sensorâs reading or a keypress on a keyboard.
The value of a characteristic value attribute can contain any type of data imaginable, from temperatures in celsius to key scan codes to display strings to speeds in miles per hourâanything that can be usefully transmitted over two BLE devices can fill in the contents of that value.
Characteristic Descriptors
GATT characteristic descriptors (commonly called simply descriptors) are mostly used to provide the client with metadata (additional information about the characteristic and its value). They are always placed within the characteristic definition and after the characteristic value attribute. Descriptors are always made of a single attribute, the characteristic descriptor declaration, whose UUID is always the descriptor type and whose value contains whatever is defined by that particular descriptor type.
You can find two types of descriptors in the different GATT characteristics:
- GATT-defined descriptors
- These are the fundamental, widely used descriptor types that simply add meta information about the characteristic. The following sections describe the most common ones.
- Profile or vendor-defined descriptors
- Regardless of whether a profile is specified and published by the SIG or by a particular vendor, these descriptors can contain all types of data, including additional information regarding the characteristic value, such as the encoding used to acquire the value from a sensor or any other particulars that complement the reading itself.
The following sections describe some of the most commonly used descriptors defined by GATT.
Extended Properties Descriptor
This descriptor, when present, simply contains the two additional property bits, covered in Characteristic declaration attribute and listed in Table 4-6.
Characteristic User Description Descriptor
As the name implies, this descriptor contains a user-readable description for the characteristic within which it is placed. This is a UTF-8 string that could read, for example, âTemperature in the living room.â
Client Characteristic Configuration Descriptor
This descriptor type (often abbreviated CCCD) is without a doubt the most important and commonly used, and it is essential for the operation of most of the profiles and use cases. Its function is simple: it acts as a switch, enabling or disabling server-initiated updates (covered in more detail in Server-Initiated Updates), but only for the characteristic in which it finds itself enclosed.
A CCCDâs value is nothing more than a two-bit bitfield, with one bit corresponding to notifications and the other to indications. A client can set and clear those bits at any time, and the server will check them every time the characteristic that encloses them has changed value and might be susceptible to an update over the air.
Every time a client wants to enable notifications or indications for a particular characteristic that supports them, it simply uses a Write Request ATT packet to set the corresponding bit to 1
. The server will then reply with a Write Response and start sending the appropriate packets whenever it wants to alert the client of a change in value.
Additionally, CCCDs have two special properties that separate them from other attributes:
- Their values are unique per connection
- In multi-connection scenarios, in which a central is connected to multiple peripherals and also acting as a GATT server, each peripheral will receive its own copy of the CCCDâs value when reading it with ATT.
- Their values are preserved across connections with bonded devices
- Attribute Caching discusses attribute caching in more detail, but that concerns only attribute handles. Values are typically not stored per-device and the GATT server can reset them between connections. This is not the case with CCCDs among bonded devices: the last value written by a client to a CCCD on the server is guaranteed to be restored upon reconnection, regardless of the time elapsed between connections.
Many protocol stacks have special mechanisms to deal with CCCDs, both from a clientâs and a serverâs perspective, because they are so critical to correct operation and to guarantee timely data updates between peers.
Example Service
This section presents an example of a particular service found in many commercial products today. The Heart Rate Service (HRS) exposes the userâs heart rate to a monitoring device.
Figure 4-2 illustrates an instance of the HRS on a fictitious server. This would not be the only service contained in the server, so you can see this as a partial slice of the complete set of attributes that a client could access.
Here is a handle-by-handle description of the HRS service illustrated in Figure 4-2:
-
Handle
0x0021
This attribute contains the service declaration (see Services) for the Heart Rate Service. These are the service declaration attributeâs fields:
- UUID
-
The UUID is the standard 16-bit UUID for a primary service declaration, UUIDprimary service (
0x2800
). - Value
-
The value is the 16-bit UUID for the Heart Rate Service, assigned by the SIG (
0x180D
).
-
Handle
0x0024
This attribute contains the characteristic declaration (see Characteristic declaration attribute) for the Heart Rate Measurement characteristic. These are the characteristic declaration attributeâs fields:
- UUID
-
The UUID is the standard 16-bit UUID for a characteristic declaration, UUIDcharacteristic (
0x2803
). - Value
-
The characteristic properties for this characteristic are notify only, the characteristic value handle is
0x0027
, and the characteristic value UUID is the UUID for Heart Rate Measurement (0x2A37
).
-
Handle
0x0027
This attribute contains the characteristic value (see Characteristic value attribute), in this case the heart rate measurement itself. These are the characteristic value attributeâs fields:
- UUID
- The same UUID present in the last two bytes of the characteristic definitionâs attribute value.
- Permissions
- This attributeâs value is neither readable nor writable: a client can obtain its value only through notifications sent by the server.
- Value
- The actual heart rate measurement (fictitiously represented in beats per minute for clarity).
-
Handle
0x0028
This attribute contains a CCCD (a key descriptor described in Client Characteristic Configuration Descriptor). These are the CCCD attributeâs fields:
- UUID
-
The UUID for any CCCD is always the standard 16-bit UUIDCCCD (
0x2902
). - Permissions
- A CCCD must always be readable and writable. The security level required to execute these operations is defined by the profile or application.
- Value
-
As already established, the CCCDâs value is a bitfield, in this case
0x0001
, denoting that notifications are enabled for this particular HRM characteristic.
-
Handle
0x002A
This attribute contains another characteristic declaration (see Characteristic declaration attribute), this time for Body Sensor Location characteristic. These are the characteristic declaration attributeâs fields:
- UUID
-
The UUID is the standard 16-bit UUID for a characteristic declaration, UUIDcharacteristic (
0x2803
). - Value
-
The characteristic properties for this characteristic are read only, the characteristic value handle is
0x002C
, and the characteristic value UUID is the UUID for the Body Sensor Location (0x2A38
).
-
Handle
0x002C
This attribute contains the characteristic value (see Characteristic value attribute), in this case the body sensor location. These are the characteristic value attributeâs fields:
- UUID
- The same UUID present in the last two bytes of the characteristics definitionâs attribute value.
- Permissions
- This attributeâs value is read only: a client can only check where the sensor is located, but not modify its location (that is up to the server).
- Value
- The actual body sensor location (fictitiously represented as âfingerâ for clarity).
Several tools exist to discover and display the different services on a server in a format similar to Figure 4-2, which can be useful during application development. Chapter 6 has more information on these tools.
Advanced Attribute Concepts
This section introduces additional concepts related to working with attributes that are also worth mentioning because their understanding is frequently required for many types of BLE applications.
Attribute Caching
Attributes discusses how attribute handles allow a client to individually address all available attributes on a server. Discovering the list of available handles and the contents of their respective attributes can be a time-consuming (and power-consuming) process detailed further in Service and Characteristic Discovery. But for now, this section describes what a client can do to avoid performing the discovery procedure every time it reconnects to a server, and under what circumstances it can do so.
Servers typically tend to maintain a stable set of attributes, and their basic structure does not, in most cases, change over the lifetime of a server device. But the implementation imposes no rigid restrictions in this regard, and a server is indeed free to completely overhaul its attributes and even replace them with a radically new set at any time (through a firmware update or perhaps with the installation of applications on the server device, for example). Certain rules and constraints are therefore required, so that a client can rely on the validity of previously discovered handles without risk of them having changed on the server and thus no longer being valid.
As a general rule, the specification recommends that clients cache (i.e., store for subsequent transactions and even connections) the handles of the attributes they are interested in. Attribute values, especially in the cases where they correspond to actual user data, are highly volatile, so it usually makes little sense to store them locally in the client for future use.
The specification provides the Service Changed characteristic (discussed in more detail in GATT Service) for servers to communicate to the client any potential changes in the contents of its attribute information. This is an optional characteristic, so its mere presence on the server already acts as an alert regarding the actual possibility of structural attribute changes.
Clients can ascertain if the result of discovery can be cached for future use by observing the following conditions:
- No Service Changed characteristic present on the server
- Clients can freely and permamently cache all handles found with no restrictions. The server guarantees they will not change during the lifetime of the device.
- Service Changed characteristic present on the server
- In this case, a client needs to subscribe to the server-initiated updates by writing into the corresponding CCCD enclosed within the Service Changed characteristic (see Characteristic Descriptors). This will allow the server to alert the client of any structural changes. If the client and the server are bonded as described in Security Modes and Procedures, the client can cache attribute handles across connections and expect them to remain identical. If the devices are not bonded, the client will need to perform discovery every time it reconnects to the server.
GATT Service provides more details about using the Service Changed characteristic.
GATT Attribute Data in Advertising Packets
Although GATT primarily relies on established connections between a central and a peripheral (as described in Roles), it is also possible to include portions of the attribute information hosted by the server inside advertising packets, rendering one or more server attributes available to any observer or central while scanning.
Table 3-3 discusses the Service Data AD Type, but that section does not describe the format it uses to enclose server attributes inside an advertising packet. The Core Specification Supplement in the Specification Adopted Documents page specifies the fields that the GATT server must insert in the payload of an advertising packet to make a particular serviceâs data available to scanners.
As shown in Table 4-7, to be able to broadcast service data, a GATT server must include two different fields in the advertising packetâs Service Data section.
Field | Length in bytes | Description |
UUID | 2, 4, or 16 | The actual UUID identifying the data |
Service Data | Variable | The data associated with the service identified by the UUID |
The contents of the Service Data field can correspond to the complete or partial value of a particular characteristic or descriptor within the corresponding service. It is up to each profile specification to define which, because only the profile has sufficient knowledge about the data to decide which pieces of information are the most relevant to be broadcasted.
Features
GATT features are strictly defined procedures that allow GATT-based data exchanges to take place. They are all based on the different operations that ATT provides (introduced in ATT operations).
To a certain extent, most of the features listed in this chapter are exposed in one way or another in most GATT APIs. GATT server APIs add the ability to populate the actual server with attributes, but that is heavily implementation dependant and beyond the scope of this chapter.
Exchange MTU
This succinct two-packet procedure allows each ATT peer to let the other end know about the maximum transmission unit (MTU, or effectively maximum packet length) it can hold in its buffers and can therefore accept.
This procedure is used only whenever either the client or the server (or both) can handle MTUs longer than the default ATT_MTU
of 23 bytes (see Logical Link Control and Adaptation Protocol (L2CAP)) and wants to inform the other end that it can send packets longer than the default values that the specification requires. L2CAP will then fragment these bigger packets into small Link Layers packets and recombine them from small Link Layers packets.
Service and Characteristic Discovery
As mentioned elsewhere in this chapter, the client has no knowledge about the attributes that might be present in a GATT server when it first connects to it. It is therefore essential for the client to begin by performing a series of packet exchanges to determine the amount, location, and nature of all the attributes that might be of interest. As discussed in Attribute Caching, procedures in this category can, in some cases, subsequently be skipped.
For primary service discovery, GATT offers the two following options:
- Discover all primary services
-
Using this feature, clients can retrieve a full list of all primary services (regardless of service UUIDs) from the remote server. This is commonly used when the client supports more than one service and therefore wants to find out about the full service support on the server side. Because the client can specify a handle range when issuing the required request, it must set
0x0001-0xFFFF
as the handle range to implement this feature, covering the full attribute range of the server. - Discover primary service by service UUID
-
Whenever the client knows which service it is looking for (usually because it supports only that single service itself), it can simply look for all instances of a particular service using this feature, also with the requirement of setting the handle range to
0x0001-0xFFFF
.
Each of these procedures yield handle ranges that refer to the attributes that belong to a single service. The discover all primary services feature also obtains the individual service UUIDs.
When the client has already found services on the server, it can proceed to perform relationship discovery (the discovery of any included services) with the following feature:
- Find included services
- This allows a client to query the server about any included services within a service. The handle range provided in such a query refers to the boundaries of an existing service, previously obtained using service discovery. As with service discovery, the client also receives a set of handle ranges, along with UUIDs when applicable.
In terms of characteristic discovery, GATT offers the following options:
- Discover all characteristics of a service
- Once a client has obtained the handle range for a service it might be interested in, it can then proceed to retrieve a full list of its characteristics. The only input is the handle range, and in exchange, the server returns both the handle and the value of all characteristic declaration attributes enclosed within that service (see Characteristic declaration attribute).
- Discover characteristics by UUID
- This procedure is identical to the previous one, except the client discards all responses that do not match the particular characteristic UUID it targets.
Once the boundaries (in terms of handles) of a target characteristic have been established, the client can go on to characteristic descriptor discovery:
- Discover all characteristic descriptors
- Now in possession of a set of handle ranges and UUIDs for some or all of the characteristics in a service, the client can use this feature to retrieve all the descriptors within a particular characteristic. The server replies with a list of UUID and handle pairs for the different descriptor declarations (see Characteristic Descriptors).
All the features in this section can be performed over open, unsecured connections, because discovery is allowed for all clients without any restrictions.
Reading Characteristics and Descriptors
To obtain the current value of a characteristic value or a descriptor, the client has the following choices:
- Read characteristic value or descriptor
-
This feature can be used to simply read the contents of a characteristic value or a descriptor by using its handle. Only the first
ATT_MTU-1
bytes of the contents can be read, because that is the maximum number of bytes that can fit in the response packet (1 byte is reserved for the ATT operation code). - Read long characteristic value or descriptor
- If the value is too long to be read with the previous feature, this feature includes an offset along with the handle in the request, so that the characteristic value or the descriptor contents can be read in successive chunks. Multiple request/response pairs might be required, depending on the length of the attribute value being read.
Additionally, and only applicable for characteristic values, these features are available:
- Read characteristic value using characteristic UUID
- Whenever a client does not know the specific handles for the characteristics it might be interested in, it can read the values of all the characteristics of a specific type. The client simply provides a handle range and a UUID and receives an array of values of characteristics enclosed in that range.
- Read mutiple characteristic values
- Conversely, if a client already has the handles for a set of characteristics it wants to obtain the value from, it can then send a request with this set of handles and subsequently receive the values for all corresponding characteristics.
Reading characteristics and descriptors is subject to the security permissions and the server can deny permission if the connectionâs security level does not match the established requirements (see Security).
Writing Characteristics and Descriptors
To write to the value of a characteristic value or a descriptor, the client has the following choices:
- Write characteristic value or descriptor
-
This feature is used to write to a characteristic value or descriptor. The client provides a handle and the contents of the value (up to
ATT_MTU-3
bytes, because the handle and the ATT operation code are included in the packet with the data) and the server will acknowledge the write operation with a response. - Write long characteristic value or descriptor
-
Similar to the read long characteristic value or descriptor feature, this permits a client to write more than
ATT_MTU-3
bytes of data into a serverâs characteristic value or descriptor. It works by queueing several prepare write operations, each of which includes an offset and the data itself, and then finally writing them all atomically with an execute write operation.
Additionally, and only applicable for characteristic values, these features are available:
- Write without response
- This feature is the converse equivalent of notifications (detailed in Server-Initiated Updates) and uses Write Command packets. Write Commands are unacknowledged packets that include a handle and a value, and they can be sent at any time and in any amount without any flow control mechanism kicking in (except of course for the native Link Layer flow control, since all traffic is subject to it). The server is free to silently discard them if it cannot process them or if the attribute permissions prevent it from accepting it. The client will never know, but that is by mutual agreement. The only way for the client to find out whether the value was written is to read it after the fact.
- Reliable writes
- Similar to the read multiple characteristic values feature, when a client wants to queue write operations to multiple characteristic values, it issues a final packet to commit the pending write operations and execute them.
Writing characteristics and descriptors is subject to the security permissions, and the server can deny permission if the connectionâs security level does not match the established requirements (see Security).
Server-Initiated Updates
Server-initiated updates are the only asynchronous (i.e., not as a response to a clientâs request) packets that can flow from the server to the client. These updates send timely alerts of changes in a characteristic value without the client having to regularly poll for them, saving both power and bandwidth.
There are two types of server-initiated updates:
- Characteristic Value Notification
- Notifications are packets that include the handle of a characteristic value attribute along with its current value. The client receives them and can choose to act upon them, but it sends no acknowledgement back to the server to confirm reception. Along with write without response, this is the only other packet that does not comply with the standard request/response flow control mechanism in ATT, as the server can send any number of these notifications at any time. This feature uses the handle value notification (HVN) ATT packet.
- Characteristic Value Indication
- Indications, on the other hand, follow the same handle/value format but require an explicit acknowledgment from the client in the form of a confirmation. Note that although the server cannot send further indications (even for different characteristics) until it receives confirmation from the client (because this flows in the opposite direction than the usual request/response pairs), an outstanding confirmation does not affect potential requests that the client might send in the meantime. This feature uses the handle value indication (HVI) and handle value confirmation (HVC) ATT packets.
The client must enable both types of server-initiated updates by writing to the corresponding CCCD before the server can start sending them. Client Characteristic Configuration Descriptor describes this process extensively.
Security
Security Modes and Procedures discusses how the GAP authentication procedure can be used to transition from one security mode to a higher, more secure one by using the different means available within the Security Manager and GAP. GATT transactions can act as triggers of such an authentication procedure. As discussed in Permissions, each attribute within a GATT server has fine-grained, independent permissions for both reading and writing, and these permissions are enforced at the ATT level.
Generally speaking, attributes that are declarations require no special security to be accessed. This is true for both service and characteristic declarations, but not for descriptor declarations, which contain the relevant data directly in them, rather than in a separate attribute. This is done so that clients that have not yet paired or bonded with a server can at least perform basic service and characteristic discovery, without having to resort to performing security procedures. The attribute layout and data hierarchy of a server is not considered to be sensitive information and is therefore freely available to all clients.
When accessing a characteristic value or a descriptor declaration (also called service request), however, a client can receive an error response ATT packet (see ATT operations), indicating that the connectionâs current security level is not high enough for the request to be executed. The following two error codes are commonly used for this purpose and placed in the error response packet:
- Insufficient Authentication
- Denotes that the link is not encrypted and that the server does not have a long-term key (LTK, first introduced in Security Keys) available to encrypt the link, or that the link is indeed encrypted, but the LTK used to perform the encryption procedure is not authenticated (generated with man-in-the-middle protection; see Authentication) while the permissions required authenticated encryption.
- Insufficient Encryption
- Denotes that the link is not encrypted but a suitable LTK is available.
GAP and GATT roles are not linked in any way and can be mixed and matched freely, but security procedures are always initiated by the GAP central (see Security Manager (SM)). Therefore, depending on which peer is acting as a central and which as a peripheral, it can be up to either the GATT client or the GATT server to initiate the pairing, bonding, or encryption procedure in order to raise the security level of the connection. Once the security level matches the one required by the attributeâs permissions, the client can send the request again to be executed on the server.
GATT Service
Just as GAP has its own SIG-specified service that is mandatory for all devices (described extensively in GAP Service), GATT also has its own service (containing up to one characteristic) that must be included in all GATT servers. The optional service changed characteristic (introduced briefly in Attribute Caching), cannot be read or written, and its value is communicated to the client only through characteristic value indications.
As shown in Table 4-8, the value consists only of a handle range, which delimits a particular area of attributes in the server. This is the area that has been affected by structural changes and needs to be rediscovered by the client. The client will have to perform service and characteristic discovery in that area, because the attributes it can have cached might no longer be valid.
Handle | Type | Permissions | Value | Value length |
| UUIDservice changed | None | Affected handle range | 4 |
A client must enable indications on the corresponding CCCD for this characteristic before doing anything else, so that it can become aware of any changes on the serverâs attribute structure.
If the server suffers a structural change in attribute layout, it will then immediately send a handle value indication to the client and wait for the corresponding confirmation. In this way, it can be sure that the client understands that cached attribute handles in that range might no longer be valid. If the attributes change outside the lifetime of a connection with a bonded device, the server will send the indication right after the connection is set up, so that the client has a chance to rediscover the affected area.
Get Getting Started with Bluetooth Low Energy now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.