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:

None
The attribute can neither be read nor written by a client.
Readable
The attribute can be read by a client.
Writable
The attribute can be written by a client.
Readable and writable
The attribute can be both read and written by the client.
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.

Table 4-1. Attributes represented as a table
Handle Type Permissions Value Value length

0x0201

UUID1 (16-bit)

Read only, no security

0x180A

2

0x0202

UUID2 (16-bit)

Read only, no security

0x2A29

2

0x0215

UUID3 (16-bit)

Read/write, authorization required

“a readable UTF-8 string”

23

0x030C

UUID4 (128-bit)

Write only, no security

{0xFF, 0xFF, 0x00, 0x00}

4

0x030D

UUID5 (128-bit)

Read/write, authenticated encryption required

36.43

8

0x031A

UUID1 (16-bit)

Read only, no security

0x1801

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.

GATT data hierarchy
Figure 4-1. GATT data hierarchy

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.

Table 4-2. Service Declaration attribute
Handle Type Permissions Value Value length

0xNNNN

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.

Table 4-3. Include Declaration attribute
Handle Type Permissions Value Value length

0xNNNN

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.

Table 4-4. Characteristic declaration and characteristic value attributes
Handle Type Permissions Value Value length

0xNNNN

UUIDcharacteristic

Read only

Properties, value handle (0xMMMM), characteristic UUID

5, 7, or 19 bytes

0xMMMM

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.

Table 4-5. Characteristic declaration 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.
Table 4-6. Characteristic properties
Property Location Description

Broadcast

Properties

If set, allows this characteristic value to be placed in advertising packets, using the Service Data AD Type (see )

Read

Properties

If set, allows clients to read this characteristic using any of the ATT read operations listed in

Write without response

Properties

If set, allows clients to use the Write Command ATT operation on this characteristic (see )

Write

Properties

If set, allows clients to use the Write Request/Response ATT operation on this characteristic (see )

Notify

Properties

If set, allows the server to use the Handle Value Notification ATT operation on this characteristic (see )

Indicate

Properties

If set, allows the server to use the Handle Value Indication/Confirmation ATT operation on this characteristic (see )

Signed Write Command

Properties

If set, allows clients to use the Signed Write Command ATT operation on this characteristic (see )

Queued Write

Extended Properties

If set, allows clients to use the Queued Writes ATT operations on this characteristic (see )

Writable Auxiliaries

Extended Properties

If set, a client can write to the descriptor described in

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.

Characteristic presentation format descriptor

When present, this descriptor type contains the actual format of the enclosing characteristic value in its seven-byte attribute value. The list of formats available include Booleans, strings, integers, floating-point, and even generic untyped buffers.

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.

GATT Heart Rate Service
Figure 4-2. GATT Heart Rate Service

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.

Table 4-7. Service Data AD Type
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.

Table 4-8. Service changed characteristic value
Handle Type Permissions Value Value length

0xNNNN

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.