#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;
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";
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 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);
}
return service_data->size() == kMinModelIdLength
? 0
: GetVersion2022(service_data);
}
int GetVersion2018(const std::vector<uint8_t>* service_data) {
return ((*service_data)[kHeaderIndex] & kHeaderVersionBitmask2018) >>
kHeaderVersionOffset2018;
}
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;
}
int GetIdLength(const std::vector<uint8_t>* service_data) {
return service_data->size() == kMinModelIdLength
? kMinModelIdLength
: ((*service_data)[kHeaderIndex] & kHeaderLengthBitmask2018) >>
kHeaderLengthOffset2018;
}
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());
}
bool HasModelId(const std::vector<uint8_t>* service_data) {
CHECK(!features::IsFastPairAdvertisingFormat2025Enabled());
return service_data != nullptr &&
(service_data->size() == kMinModelIdLength ||
(service_data->size() > kMinModelIdLength &&
GetVersion(service_data) == 0 && IsIdLengthValid(service_data)));
}
std::optional<std::string> GetExtraField(
const std::vector<uint8_t>* service_data,
int extra_field_type) {
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) {
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) {
return base::HexEncode(*service_data);
}
if (!features::IsFastPairAdvertisingFormat2025Enabled()) {
int id_index = 1;
int end = id_index + GetIdLength(service_data);
while ((*service_data)[id_index] == 0 &&
end - id_index > kMinModelIdLength) {
id_index++;
}
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;
}
if (GetVersion(service_data) == 0 && GetFlags(service_data) == 0) {
return GetExtraField(service_data, kExtraFieldTypeModelId);
}
return std::nullopt;
}
}
}
}