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.
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.
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:
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 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.
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.
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
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.
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 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:
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:
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:
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:
All permissions are independent from each other and can be freely combined by the server, which stores them in a per-attribute basis.
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.
Read only, no security
Read only, no security
Read/write, authorization required
“a readable UTF-8 string”
Write only, no security
Read/write, authenticated encryption required
Read only, no security
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
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.
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.
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.
UUIDprimary service or UUIDsecondary service
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.
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.
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.
Properties, value handle (
5, 7, or 19 bytes
All GATT characteristics are always part of a service, and can therefore always be found enclosed in one.
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|
A bitfield listing the permitted operations on this characteristic
Characteristic Value Handle
The handle of the attribute containing the characteristic value
2, 4, or 16
The UUID for this particular characteristic
These three fields are contained in a characteristic declaration attribute value:
Write without response
Signed Write Command
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.
0xNNNN+1) to the one containing the declaration.
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.
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.
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:
The following sections describe some of the most commonly used descriptors defined by GATT.
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.”
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:
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.
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:
This attribute contains the service declaration (see Services) for the Heart Rate Service. These are the service declaration attribute’s fields:
This attribute contains the characteristic declaration (see Characteristic declaration attribute) for the Heart Rate Measurement characteristic. These are the characteristic declaration attribute’s fields:
0x0027, and the characteristic value UUID is the UUID for Heart Rate Measurement (
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:
This attribute contains a CCCD (a key descriptor described in Client Characteristic Configuration Descriptor). These are the CCCD attribute’s fields:
0x0001, denoting that notifications are enabled for this particular HRM characteristic.
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:
0x002C, and the characteristic value UUID is the UUID for the Body Sensor Location (
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:
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.
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.
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:
GATT Service provides more details about using the Service Changed characteristic.
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|
2, 4, or 16
The actual UUID identifying the data
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.
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.
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.
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:
0x0001-0xFFFFas the handle range to implement this feature, covering the full attribute range of the server.
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.
All the features in this section can be performed over open, unsecured connections, because discovery is allowed for all clients without any restrictions.
ATT_MTU-1bytes 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).
Additionally, and only applicable for characteristic values, these features are available:
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).
ATT_MTU-3bytes, 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.
ATT_MTU-3bytes 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:
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 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:
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 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:
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.
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.
Affected handle range
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.