提供商广告信号

广告:广告可被发现时

当提供程序设备处于 BR/EDR 可检测到状态(即处于配对模式下)时,它将通过 BLE 通告快速配对模型 ID 数据,并且 BLE 地址不得旋转。

通告间隔:当可检测到时

通告之间的时间间隔不应大于 100 毫秒 (10Hz)。快速速率可让查找器快速找到提供程序,即使在低功耗模式下扫描也是如此。

广告载荷:快速配对模型 ID 数据

广告应包含服务数据数据类型(同上、第 1.11 条。该 UUID 应为 0xFE2C 的快速配对服务 UUID。服务数据应包含以下内容:

八位字节 数据类型 说明
0 - 2 年 uint24 24 位模型 ID 各不相同

广告:无法被用户发现时

当无法被检测到(即未处于配对模式)时,提供者设备应按照以下准则播发快速配对帐号数据。

通过通告帐号数据,附近的搜索者可以识别出某个提供方属于其帐号并启动配对,而无需先强制该提供方重新进入配对模式(这是用户投诉的一个常见原因)。寻找者将为用户提供机会,让他们能够在不等待与提供程序配对或广播不相关(例如,如果用户已经配对)忽略此广播。发起者还会自动滤除明显不良的广播,例如在帐号数据配置错误时。

通告间隔:何时不可检测

通告之间的时间间隔不得超过 250 毫秒 (4Hz)。

广告载荷:快速配对帐号数据

广告应包含服务数据数据类型(同上),第 1.11 条。该 UUID 应为 0xFE2C 的快速配对服务 UUID。服务数据应包含以下内容:

八位字节 数据类型 说明
0 uint8 版本和标记
0bVVVVFFFF
  • V = 版本
  • F = 标记
0x00
(预留供日后使用)
1 - 各不相同 帐号关键数据 如果帐号密钥列表为空,则
0x00 不固定

帐号密钥数据包含:

八位字节 数据类型 说明
0 uint8 字段长度和类型
0bLLLLTTTT
  • L = 帐号密钥过滤器的长度(以字节为单位)
  • T = 类型
0bLLLL0000
  • length = 0bLLLL = 不定
  • type = 0b0000(显示界面指示)或 0b0010(隐藏界面指示)、帐号密钥过滤器
1 - 帐号密钥过滤器 各不相同
s + 1 uint8 字段长度和类型
0bLLLLTTTT
  • L = 长度(以字节为单位)
  • T = 类型
0b00100001
  • 长度 = 0b0010 = 2
  • type = 0b0001,Salt
s + 2 - s + 3 uint16 Salt 各不相同

帐号密钥过滤器

通过通告的帐号密钥过滤器,发起者可以快速检查提供商是否拥有某个帐号密钥(假正例概率较低,平均远低于 0.5%),然后再进行进一步互动。探索器可以在看到广播类型为 0 的过滤器(即显示界面指示,且可能包含其某个帐号密钥)时自动连接并尝试启动该过程,以便进一步降低假正例率。在某些情况下,提供程序可能想要在未准备好配对时被查找器识别。例如,当耳机放回充电盒内时,我们希望停止显示后续配对通知,因为耳机可能会拒绝该配对。

帐号密钥过滤条件是一个长度可变的 Bloom 过滤条件,具体构建方式如下:

  1. n 是持久保留的帐号密钥列表中的帐号密钥数量 (n >= 1)。
  2. s(过滤器的大小(以字节为单位))设置为 (1.2*n + 3) 截断。例如,如果保留 1 个键,则 s = 4 个字节。
    uint8_t s = (((uint8_t)(( float )1.2 * n)) + 3);
  3. 将过滤器 F 初始化为 s 字节数组,其中每个字节均设置为 0。
    uint8_t F[s] = {0};
  4. 对于保留的帐号密钥列表中的每个帐号密钥 K
    a. 将 V 设为 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. 使用 SHA256 哈希 V,得到 32 字节的值 H = {H0, ..., H31}。

    uint8_t H[32] = {0};
    SHA256_hash_function(V, H);
    

    c.将 H 分为 8 个采用大端字节序的 4 字节无符号整数,即 X = {X0, ..., X7},其中 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. 对于每个 Xi
    i. 以 MXi 对过滤器中的位数取 (s * 8) 的模数。
    ii. 获取 F 中位于索引 (M / 8) 处的字节向下舍入。
    iii. 在该字节中,将索引 (M % 8) 处的位设置为 1。
    iv. 也就是说:

        // 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));
        }
    

在广告数据中添加过滤器 F 作为帐号密钥过滤器字段。 请注意,此值没有“字节序”,因为没有更多或更少的有效字节,请勿更改字节顺序。

盐田

此盐是在构建 bloom 过滤条件时附加到帐号键的随机值。每次为 Provider 更新 RPA 时,都应重新生成此盐,以避免跨地址轮替进行跟踪。

如需使用盐生成帐号密钥过滤条件,请执行以下操作:

  1. 生成一个随机的 2 个字节 S。请注意,此值没有“字节序”,因为没有更多或更少的有效字节,请勿更改字节顺序。
  2. 使用 2 个字节的 S 作为盐。
  3. 在通告的快速配对帐号数据中,在帐号密钥过滤条件字段中添加生成的过滤条件,并在盐字段中添加 S