Provider Advertising signal
Advertising: When discoverable
When the Provider device is BR/EDR discoverable (that is, in pairing mode), it shall advertise Fast Pair Model ID Data over BLE, and the BLE address shall not be rotated.
Advertising interval: When discoverable
The interval between advertisements should be no larger than 100ms (10Hz). A fast rate allows the Seeker to quickly find the Provider, even when scanning in low-power mode.
Advertising payload: Fast Pair Model ID Data
The advertisement shall contain the Service Data data type, ibid., § 1.11. The
UUID shall be the Fast Pair Service UUID of 0xFE2C
. The service data shall
contain the following:
Octet | Data type | Description | Value |
---|---|---|---|
0-2 | uint24 |
24-bit model ID | varies |
Advertising: When not discoverable
When not discoverable (that is, not in pairing mode), the Provider device shall advertise Fast Pair Account Data, using the following guidelines.
Advertising the account data allows Seekers nearby to recognize when a provider belongs to their account and initiate pairing without having to force the provider back into pairing mode first, which is a common cause for user complaint. Seekers will provide the opportunity for users to be able to ignore this broadcast in the case where they do not wait to pair with the provider or the broadcast is not relevant (for example, if they have already paired). Seekers will also filter out obviously bad broadcasts automatically, such as when the account data is misconfigured.
Advertising interval: When not discoverable
The interval between advertisements should be at most 250ms (4Hz).
Advertising payload: Fast Pair Account Data
The advertisement shall contain the Service Data data type, Ibid., § 1.11. The
UUID shall be the Fast Pair Service UUID of 0xFE2C
. The service data shall
contain the following:
Octet | Data type | Description | Value |
---|---|---|---|
0 | uint8 |
Version and flags 0bVVVVFFFF
|
0x00 (reserved for future use) |
1 - varies | Account Key Data | varies or 0x00 if the Account Key List is empty |
The Account Key Data contains:
Octet | Data type | Description | Value |
---|---|---|---|
0 | uint8 |
Field length and type 0bLLLLTTTT
|
0bLLLL0000
|
1 - s | Account Key Filter | varies | |
s + 1 | uint8 |
Field length and type 0bLLLLTTTT
|
0b00100001
|
s + 2 - s + 3 | uint16 |
Salt | varies |
Account Key Filter
The advertised Account Key Filter allows a Seeker to quickly check whether a Provider might possess a certain account key (with a low false-positive probability, on average much less than 0.5%), before further interactions. The Seeker may automatically connect and attempt to start the procedure when it sees a filter being broadcast with type 0, i.e. showing UI indication, that potentially contains one of its account keys, so as to reduce the rate of false positives further. In some situations, the Provider may want to be recognized by the Seeker while not ready for pairing. One example is that when buds get put back into case, we want to stop showing the subsequent pairing notification since that pairing could be rejected by the headset.
The Account Key Filter is a variable-length Bloom filter constructed as follows:
- Let n be the number of account keys (n >= 1) in the persisted Account Key list.
- Let s, the size of the filter in bytes, be (1.2*n + 3) truncated. For
example, if 1 key is persisted, s = 4 bytes.
uint8_t s = (((uint8_t)(( float )1.2 * n)) + 3);
- Initialize the filter F as an array of s bytes, each set to 0.
uint8_t F[s] = {0};
For each account key K in the persisted Account Key list:
a. Let V be concat(K, Salt).// In the sample code, the size of salt is 2 bytes. #define SALT_SIZE 2 uint8_t V[FASTPAIR_ACCOUNT_KEY_SIZE + SALT_SIZE]; for (uint8_t keyIndex = 0; keyIndex < n; keyIndex++) { // concat (K, Salt) fastpair_get_account_key_by_index(keyIndex, V); uint8_t randomSalt = (uint8_t)rand(); V[FASTPAIR_ACCOUNT_KEY_SIZE] = randomSalt; ... }
b. Hash V using SHA256, obtaining a 32-byte value H = {H0, …, H31}.
uint8_t H[32] = {0}; SHA256_hash_function(V, H);
c. Divide H into eight 4-byte unsigned integers in big-endian, X = {X0, …, X7}, where X0 = 0xH0H1H2H3.
uint32_t X[8]; for (index = 0; index < 8; index++) { X[index] = (((uint32_t)(H[index * 4])) << 24) | (((uint32_t)(H[index * 4 + 1])) << 16) | (((uint32_t)(H[index * 4 + 2])) << 8) | (((uint32_t)(H[index * 4 + 3])) << 0); }
d. For each Xi:
i. Let M be Xi modulo the number of bits in the filter, (s * 8).
ii. Get the byte in F at index (M / 8), rounded down.
iii. Within the byte, set the bit at index (M % 8) to 1.
iv. In other words:// M = Xi % (s * 8) // F[M/8] = F[M/8] | (1 << (M % 8)) for (index = 0; index < 8; index++) { uint32_t M = X[index] % (s * 8); F[M / 8] = F[M / 8] | (1 << (M % 8)); }
Include the filter F as the Account Key Filter field, in the advertising data. Note that there is no "endianness" to this value, since there is no more or less significant byte—don't alter the byte order.
Salt field
The salt is a random value that is appended to account keys when building the bloom filter. This salt should be regenerated every time the RPA is updated for the Provider to avoid tracking across address rotation.
To generate the Account Key Filter using the salt:
- Generate a random 2-byte S. Note that there is no "endianness" to this value, since there is no more or less significant byte — don't alter the byte order.
- Use the 2-byte S as the Salt.
- In the advertised Fast Pair Account Data, include the generated filter in the Account Key Filter field, and S in the Salt field.