File: fast_pair_decoder.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (211 lines) | stat: -rw-r--r-- 7,373 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ash/quick_pair/common/fast_pair/fast_pair_decoder.h"

#include <optional>
#include <vector>

#include "ash/constants/ash_features.h"
#include "base/containers/span.h"
#include "base/strings/string_number_conversions.h"
#include "base/types/fixed_array.h"

namespace {

constexpr int kMinModelIdLength = 3;
constexpr int kHeaderIndex = 0;

// Format 2018 (0bVVVLLLLR)
constexpr int kHeaderLengthBitmask2018 = 0b00011110;
constexpr int kHeaderLengthOffset2018 = 1;
constexpr int kHeaderVersionBitmask2018 = 0b11100000;
constexpr int kHeaderVersionOffset2018 = 5;
constexpr int kMaxModelIdLength = 14;
const std::string k2018HeaderPrefix = "06";

// Format 2022 (0bVVVVFFFF)
constexpr int kHeaderVersionBitmask2022 = 0b11110000;
constexpr int kHeaderVersionOffset2022 = 4;
constexpr int kHeaderFlagsBitmask2022 = 0b00001111;

constexpr int kHeaderLength = 1;
constexpr int kExtraFieldHeaderLength = 1;
constexpr int kExtraFieldLengthBitmask = 0b11110000;
constexpr int kExtraFieldLengthOffset = 4;
constexpr int kExtraFieldTypeBitmask = 0b00001111;
constexpr int kExtraFieldTypeOffset = 0;
constexpr int kExtraFieldTypeModelId = 7;

}  // namespace

namespace ash {
namespace quick_pair {
namespace fast_pair_decoder {

int GetVersion(const std::vector<uint8_t>* service_data) {
  if (!features::IsFastPairAdvertisingFormat2025Enabled()) {
    return service_data->size() == kMinModelIdLength
               ? 0
               : GetVersion2018(service_data);
  }

  // If feature flag enabled, only handle 2022 format advertisements.
  return service_data->size() == kMinModelIdLength
             ? 0
             : GetVersion2022(service_data);
}

/** For format 2018, get version from the first 3 bits (VVVLLLLR) */
int GetVersion2018(const std::vector<uint8_t>* service_data) {
  return ((*service_data)[kHeaderIndex] & kHeaderVersionBitmask2018) >>
         kHeaderVersionOffset2018;
}

/** For format 2022, get version from the first 4 bits (VVVVFFFF) */
int GetVersion2022(const std::vector<uint8_t>* service_data) {
  return ((*service_data)[kHeaderIndex] & kHeaderVersionBitmask2022) >>
         kHeaderVersionOffset2022;
}

int GetFlags(const std::vector<uint8_t>* service_data) {
  CHECK(features::IsFastPairAdvertisingFormat2025Enabled());

  return (*service_data)[kHeaderIndex] & kHeaderFlagsBitmask2022;
}

int GetExtraFieldLength(const std::vector<uint8_t>* service_data, int index) {
  return ((*service_data)[index] & kExtraFieldLengthBitmask) >>
         kExtraFieldLengthOffset;
}

int GetExtraFieldType(const std::vector<uint8_t>* service_data, int index) {
  return ((*service_data)[index] & kExtraFieldTypeBitmask) >>
         kExtraFieldTypeOffset;
}

// TODO(399163998): Deprecate this code after feature launch. This function is
// also used when parsing not discoverable advertisements for subsequent pair,
// so we'll have to audit that code as well.
int GetIdLength(const std::vector<uint8_t>* service_data) {
  return service_data->size() == kMinModelIdLength
             ? kMinModelIdLength
             : ((*service_data)[kHeaderIndex] & kHeaderLengthBitmask2018) >>
                   kHeaderLengthOffset2018;
}

// TODO(399163998): Remove deprecated code after feature launch.
bool IsIdLengthValid(const std::vector<uint8_t>* service_data) {
  CHECK(!features::IsFastPairAdvertisingFormat2025Enabled());

  int id_length = GetIdLength(service_data);
  return kMinModelIdLength <= id_length && id_length <= kMaxModelIdLength &&
         id_length + kHeaderLength <= static_cast<int>(service_data->size());
}

// TODO(399163998): Remove deprecated code after feature launch.
bool HasModelId(const std::vector<uint8_t>* service_data) {
  CHECK(!features::IsFastPairAdvertisingFormat2025Enabled());

  return service_data != nullptr &&
         (service_data->size() == kMinModelIdLength ||
          // Header byte exists. We support only format version 0. (A different
          // version indicates a breaking change in the format.)
          (service_data->size() > kMinModelIdLength &&
           GetVersion(service_data) == 0 && IsIdLengthValid(service_data)));
}

// Returns hex-encoded string of field with type |extra_field_type|, or
// std::nullopt if no match was found or fields are improperly formatted.
std::optional<std::string> GetExtraField(
    const std::vector<uint8_t>* service_data,
    int extra_field_type) {
  // Iterate through extra fields, which have the following format (including
  // overall header): Header (0bVVVVFFFF) LT V LT V
  // ...

  size_t headerIndex = kHeaderLength;
  while (headerIndex < service_data->size()) {
    size_t length = GetExtraFieldLength(service_data, headerIndex);
    int type = GetExtraFieldType(service_data, headerIndex);

    size_t start = headerIndex + kExtraFieldHeaderLength;
    size_t end = start + length;
    if (length < 1 || end > service_data->size()) {
      LOG(ERROR) << __func__ << ": Improper length for extra fields, aborting.";
      return std::nullopt;
    }

    if (type == extra_field_type) {
      // found it!
      // Extract extra field to new vector.
      std::vector<uint8_t> extra_field(length);

      for (size_t i = 0; i < length; i++) {
        extra_field[i] = (*service_data)[i + start];
      }
      return base::HexEncode(extra_field);
    }

    headerIndex = end;
  }

  LOG(ERROR) << __func__ << ": Extra field type not found.";
  return std::nullopt;
}

std::optional<std::string> GetHexModelIdFromServiceData(
    const std::vector<uint8_t>* service_data) {
  if (service_data == nullptr || service_data->size() < kMinModelIdLength) {
    return std::nullopt;
  }
  if (service_data->size() == kMinModelIdLength) {
    // If the size is 3, all the bytes are the ID,
    return base::HexEncode(*service_data);
  }

  // The following logic is used only to support the deprecated 2018 format.
  if (!features::IsFastPairAdvertisingFormat2025Enabled()) {
    // Otherwise, the first byte is a header which contains the length of the
    // big-endian model ID that follows. The model ID will be trimmed if it
    // contains leading zeros.
    int id_index = 1;
    int end = id_index + GetIdLength(service_data);

    // Ignore leading zeros.
    while ((*service_data)[id_index] == 0 &&
           end - id_index > kMinModelIdLength) {
      id_index++;
    }

    // Copy appropriate bytes to new array.
    int bytes_size = end - id_index;
    base::FixedArray<uint8_t> bytes(bytes_size);

    for (int i = 0; i < bytes_size; i++) {
      bytes[i] = (*service_data)[i + id_index];
    }

    return base::HexEncode(base::span<uint8_t>(bytes));
  }

  std::string service_data_str = base::HexEncode(*service_data);
  if (service_data_str.starts_with(k2018HeaderPrefix)) {
    LOG(WARNING) << __func__
                 << ": Ignoring deprecated 2018 advertising format.";
    return std::nullopt;
  }

  // As per go/spec_audio_sharing_payload, the 1st byte will be 00 for devices
  // that support LE audio sharing (introduced 2025).
  if (GetVersion(service_data) == 0 && GetFlags(service_data) == 0) {
    return GetExtraField(service_data, kExtraFieldTypeModelId);
  }

  return std::nullopt;
}

}  // namespace fast_pair_decoder
}  // namespace quick_pair
}  // namespace ash