Data Parse for subsequent pairing non-discoverable advertisement

PiperOrigin-RevId: 534929577
This commit is contained in:
Qin Wang
2023-05-24 11:57:58 -07:00
committed by Copybara-Service
parent 1d5abfcc0f
commit 6b08c4dda7
9 changed files with 559 additions and 69 deletions
+2 -2
View File
@@ -15,6 +15,7 @@ cc_library(
":decoder",
"//fastpair/common",
"//fastpair/crypto",
"//internal/base:bluetooth_address",
"//internal/platform:base",
"//internal/platform:logging",
"@com_google_absl//absl/functional:any_invocable",
@@ -50,12 +51,11 @@ cc_test(
"//fastpair/common",
"//fastpair/crypto",
"//fastpair/testing",
"//internal/platform:logging",
"//internal/platform:types",
"//internal/platform/implementation/g3", # build_cleaner: keep
"@boringssl//:crypto",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/synchronization",
"@com_google_absl//absl/time",
"@com_google_googletest//:gtest_main",
],
+127 -2
View File
@@ -16,20 +16,38 @@
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <iterator>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "third_party/nearby//fastpair/crypto/fast_pair_decryption.h"
#include "fastpair/common/battery_notification.h"
#include "fastpair/common/constant.h"
#include "fastpair/common/non_discoverable_advertisement.h"
#include "fastpair/dataparser/fast_pair_decoder.h"
#include "internal/base/bluetooth_address.h"
#include "internal/platform/logging.h"
namespace nearby {
namespace fastpair {
namespace {
constexpr int kHeaderIndex = 0;
constexpr int kFieldTypeBitmask = 0b00001111;
constexpr int kFieldLengthBitmask = 0b11110000;
constexpr int kHeaderLength = 1;
constexpr int kFieldLengthOffset = 4;
constexpr int kFieldTypeAccountKeyFilter = 0;
constexpr int kFieldTypeAccountKeyFilterSalt = 1;
constexpr int kFieldTypeAccountKeyFilterNoNotification = 2;
constexpr int kFieldTypeBattery = 3;
constexpr int kFieldTypeBatteryNoNotification = 4;
constexpr int kAddressByteSize = 6;
constexpr int kMaxLengthOfSaltBytes = 2;
bool ValidateInputSizes(const std::vector<uint8_t>& aes_key_bytes,
const std::vector<uint8_t>& encrypted_bytes) {
if (aes_key_bytes.size() != kAesBlockByteSize) {
@@ -60,6 +78,19 @@ void ConvertVectorsToArrays(
out_encrypted_bytes.begin());
}
std::vector<uint8_t> CopyFieldBytesToVector(
const std::vector<uint8_t>& service_data,
const absl::flat_hash_map<size_t, std::pair<size_t, size_t>>& field_indices,
size_t key) {
std::vector<uint8_t> out;
DCHECK(field_indices.contains(key));
auto indices = field_indices.find(key)->second;
for (size_t i = indices.first; i < indices.second; i++) {
out.push_back(service_data[i]);
}
return out;
}
} // namespace
void FastPairDataParser::GetHexModelIdFromServiceData(
@@ -82,7 +113,6 @@ void FastPairDataParser::ParseDecryptedResponse(
std::array<uint8_t, kAesBlockByteSize> key;
std::array<uint8_t, kEncryptedDataByteSize> bytes;
ConvertVectorsToArrays(aes_key_bytes, encrypted_response_bytes, key, bytes);
callback(FastPairDecryption::ParseDecryptResponse(key, bytes));
}
@@ -102,5 +132,100 @@ void FastPairDataParser::ParseDecryptedPasskey(
callback(FastPairDecryption::ParseDecryptPasskey(key, bytes));
}
void FastPairDataParser::ParseNotDiscoverableAdvertisement(
absl::string_view fast_pair_service_data, absl::string_view address,
ParseNotDiscoverableAdvertisementCallback callback) {
std::vector<uint8_t> service_data;
std::move(std::begin(fast_pair_service_data),
std::end(fast_pair_service_data), std::back_inserter(service_data));
if (service_data.empty() || FastPairDecoder::GetVersion(&service_data) != 0) {
NEARBY_LOGS(WARNING) << " Invalid service data.";
callback(std::nullopt);
return;
}
absl::flat_hash_map<size_t, std::pair<size_t, size_t>> field_indices;
size_t headerIndex = kHeaderIndex + kHeaderLength +
FastPairDecoder::GetIdLength(&service_data);
while (headerIndex < service_data.size()) {
size_t type = service_data[headerIndex] & kFieldTypeBitmask;
size_t length =
(service_data[headerIndex] & kFieldLengthBitmask) >> kFieldLengthOffset;
size_t index = headerIndex + kHeaderLength;
size_t end = index + length;
if (end <= service_data.size()) {
field_indices[type] = std::make_pair(index, end);
}
headerIndex = end;
}
// Account key filter bytes
std::vector<uint8_t> account_key_filter_bytes;
NonDiscoverableAdvertisement::Type show_ui =
NonDiscoverableAdvertisement::Type::kNone;
if (field_indices.contains(kFieldTypeAccountKeyFilter)) {
account_key_filter_bytes = CopyFieldBytesToVector(
service_data, field_indices, kFieldTypeAccountKeyFilter);
show_ui = NonDiscoverableAdvertisement::Type::kShowUi;
} else if (field_indices.contains(kFieldTypeAccountKeyFilterNoNotification)) {
account_key_filter_bytes = CopyFieldBytesToVector(
service_data, field_indices, kFieldTypeAccountKeyFilterNoNotification);
show_ui = NonDiscoverableAdvertisement::Type::kHideUi;
}
if (account_key_filter_bytes.empty()) {
NEARBY_LOGS(WARNING) << " Service data doesn't contain account key filter.";
callback(std::nullopt);
return;
}
// Salt bytes
std::vector<uint8_t> salt_bytes;
if (field_indices.contains(kFieldTypeAccountKeyFilterSalt)) {
salt_bytes = CopyFieldBytesToVector(service_data, field_indices,
kFieldTypeAccountKeyFilterSalt);
}
// https://developers.devsite.corp.google.com/nearby/fast-pair/specifications/service/provider#AccountKeyFilter
if (salt_bytes.size() > kMaxLengthOfSaltBytes) {
NEARBY_LOGS(WARNING) << " Parsed a salt field larger than two bytes: "
<< salt_bytes.size();
callback(std::nullopt);
return;
}
if (salt_bytes.empty()) {
NEARBY_LOGS(INFO)
<< __func__
<< ": missing salt field from device. Using device address instead.";
std::array<uint8_t, kAddressByteSize> address_bytes;
device::ParseBluetoothAddress(
address, absl::MakeSpan(address_bytes.data(), kAddressByteSize));
salt_bytes =
std::vector<uint8_t>(address_bytes.begin(), address_bytes.end());
}
// Battery info bytes
std::vector<uint8_t> battery_bytes;
BatteryNotification::Type show_ui_for_battery =
BatteryNotification::Type::kNone;
if (field_indices.contains(kFieldTypeBattery)) {
battery_bytes =
CopyFieldBytesToVector(service_data, field_indices, kFieldTypeBattery);
show_ui_for_battery = BatteryNotification::Type::kShowUi;
} else if (field_indices.contains(kFieldTypeBatteryNoNotification)) {
battery_bytes = CopyFieldBytesToVector(service_data, field_indices,
kFieldTypeBatteryNoNotification);
show_ui_for_battery = BatteryNotification::Type::kHideUi;
}
callback(NonDiscoverableAdvertisement(
std::move(account_key_filter_bytes), show_ui, std::move(salt_bytes),
BatteryNotification::FromBytes(battery_bytes, show_ui_for_battery)));
}
} // namespace fastpair
} // namespace nearby
@@ -26,6 +26,7 @@
#include "absl/functional/any_invocable.h"
#include "absl/strings/string_view.h"
#include "fastpair/common/non_discoverable_advertisement.h"
#include "fastpair/crypto/decrypted_passkey.h"
#include "fastpair/crypto/decrypted_response.h"
@@ -45,6 +46,9 @@ class FastPairDataParser {
using ParseDecryptPasskeyCallback =
absl::AnyInvocable<void(std::optional<DecryptedPasskey>)>;
using ParseNotDiscoverableAdvertisementCallback =
absl::AnyInvocable<void(std::optional<NonDiscoverableAdvertisement>)>;
public:
// Gets the hex string representation of the device's model ID from the
// service data.
@@ -65,6 +69,13 @@ class FastPairDataParser {
const std::vector<uint8_t>& aes_key_bytes,
const std::vector<uint8_t>& encrypted_passkey_bytes,
ParseDecryptPasskeyCallback callback);
// Parses a 'Non Discoverable' advertisement from |service_data|.
// If the advertisement does not contain information about salt, use the
// |address| as salt instead.
static void ParseNotDiscoverableAdvertisement(
absl::string_view fast_pair_service_data, absl::string_view address,
ParseNotDiscoverableAdvertisementCallback callback);
};
} // namespace fastpair
+400 -48
View File
@@ -22,56 +22,66 @@
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "gtest/gtest.h"
#include "absl/strings/escaping.h"
#include "absl/strings/string_view.h"
#include "absl/synchronization/notification.h"
#include "absl/time/time.h"
#include "fastpair/common/constant.h"
#include "fastpair/common/non_discoverable_advertisement.h"
#include "fastpair/crypto/fast_pair_encryption.h"
#include "fastpair/testing/fast_pair_service_data_creator.h"
#include "internal/platform/count_down_latch.h"
namespace nearby {
namespace fastpair {
namespace {
// Test data comes from:
// https://developers.google.com/nearby/fast-pair/specifications/appendix/testcases#test_cases
constexpr absl::Duration kWaitTimeout = absl::Milliseconds(200);
constexpr absl::string_view kModelId("aabbcc");
constexpr absl::string_view kInvalidModelId("aabb");
constexpr absl::string_view kDeviceAddress("11:12:13:14:15:16");
constexpr absl::string_view kAccountKeyFilter("112233445566");
constexpr absl::string_view kSalt("01");
constexpr absl::string_view kBattery("01048F");
constexpr int kBatteryHeader = 0b00110011;
constexpr std::array<uint8_t, kAesBlockByteSize> kAeskeyarray = {
0xA0, 0xBA, 0xF0, 0xBB, 0x95, 0x1F, 0xF7, 0xB6,
0xCF, 0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32, 0x1D};
constexpr int kNotDiscoverableAdvHeader = 0b00000110;
constexpr int kAccountKeyFilterHeader = 0b01100000;
constexpr int kSaltHeader = 0b00010001;
TEST(FastPairDataParserTest, GetHexModelIdFromServiceDataUnsucessfully) {
const std::vector<uint8_t> service_data =
FastPairServiceDataCreator::Builder()
.SetModelId(std::string(kInvalidModelId))
.SetModelId(kInvalidModelId)
.Build()
->CreateServiceData();
absl::Notification notification;
CountDownLatch latch(1);
FastPairDataParser::GetHexModelIdFromServiceData(
service_data, [&notification](std::optional<absl::string_view> model_id) {
service_data, [&](std::optional<absl::string_view> model_id) {
EXPECT_EQ(model_id, std::nullopt);
notification.Notify();
latch.CountDown();
});
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout));
latch.Await();
}
TEST(FastPairDataParserTest, GetHexModelIdFromServiceDataSuccessfully) {
const std::vector<uint8_t> service_data =
FastPairServiceDataCreator::Builder()
.SetModelId(std::string(kModelId))
.SetModelId(kModelId)
.Build()
->CreateServiceData();
absl::Notification notification;
CountDownLatch latch(1);
FastPairDataParser::GetHexModelIdFromServiceData(
service_data, [&notification](std::optional<absl::string_view> model_id) {
service_data, [&](std::optional<absl::string_view> model_id) {
EXPECT_EQ(model_id, kModelId);
notification.Notify();
latch.CountDown();
});
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout));
latch.Await();
}
TEST(FastPairDataParserTest, DecryptResponseUnsuccessfullyWithInvalidAesKey) {
@@ -104,14 +114,14 @@ TEST(FastPairDataParserTest, DecryptResponseUnsuccessfullyWithInvalidAesKey) {
std::vector<uint8_t> encrypted_bytes(encrypted_bytes_array.begin(),
encrypted_bytes_array.end());
absl::Notification notification;
CountDownLatch latch(1);
FastPairDataParser::ParseDecryptedResponse(
kAesKeyBytes, encrypted_bytes,
[&notification](std::optional<DecryptedResponse> response) {
[&](std::optional<DecryptedResponse> response) {
EXPECT_FALSE(response.has_value());
notification.Notify();
latch.CountDown();
});
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout));
latch.Await();
}
TEST(FastPairDataParserTest, DecryptResponseUnsuccessfullyWithInvalidResponse) {
@@ -144,14 +154,14 @@ TEST(FastPairDataParserTest, DecryptResponseUnsuccessfullyWithInvalidResponse) {
std::vector<uint8_t> encrypted_bytes(encrypted_bytes_array.begin(),
encrypted_bytes_array.end() - 1);
absl::Notification notification;
CountDownLatch latch(1);
FastPairDataParser::ParseDecryptedResponse(
kAesKeyBytes, encrypted_bytes,
[&notification](std::optional<DecryptedResponse> response) {
[&](std::optional<DecryptedResponse> response) {
EXPECT_FALSE(response.has_value());
notification.Notify();
latch.CountDown();
});
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout));
latch.Await();
}
TEST(FastPairDataParserTest, DecryptResponseSuccessfully) {
@@ -183,19 +193,18 @@ TEST(FastPairDataParserTest, DecryptResponseSuccessfully) {
std::vector<uint8_t> encrypted_bytes(encrypted_bytes_array.begin(),
encrypted_bytes_array.end());
absl::Notification notification;
CountDownLatch latch(1);
FastPairDataParser::ParseDecryptedResponse(
kAesKeyBytes, encrypted_bytes,
[&notification, &kAddressBytes,
&kSalt](std::optional<DecryptedResponse> response) {
EXPECT_TRUE(response.has_value());
[&](std::optional<DecryptedResponse> response) {
ASSERT_TRUE(response.has_value());
EXPECT_EQ(response.value().message_type,
FastPairMessageType::kKeyBasedPairingResponse);
EXPECT_EQ(response.value().address_bytes, kAddressBytes);
EXPECT_EQ(response.value().salt, kSalt);
notification.Notify();
latch.CountDown();
});
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout));
latch.Await();
}
TEST(FastPairDataParserTest, DecryptPasskeyUnsuccessfullyWithInvalidAesKey) {
@@ -228,14 +237,14 @@ TEST(FastPairDataParserTest, DecryptPasskeyUnsuccessfullyWithInvalidAesKey) {
std::vector<uint8_t> encrypted_bytes(encrypted_bytes_array.begin(),
encrypted_bytes_array.end());
absl::Notification notification;
CountDownLatch latch(1);
FastPairDataParser::ParseDecryptedPasskey(
kAesKeyBytes, encrypted_bytes,
[&notification](std::optional<DecryptedPasskey> decrypted_passkey) {
[&](std::optional<DecryptedPasskey> decrypted_passkey) {
EXPECT_FALSE(decrypted_passkey.has_value());
notification.Notify();
latch.CountDown();
});
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout));
latch.Await();
}
TEST(FastPairDataParserTest, DecryptPasskeyUnsuccessfullyWithInvalidPasskey) {
@@ -267,14 +276,14 @@ TEST(FastPairDataParserTest, DecryptPasskeyUnsuccessfullyWithInvalidPasskey) {
std::vector<uint8_t> encrypted_bytes(encrypted_bytes_array.begin(),
encrypted_bytes_array.end() - 1);
absl::Notification notification;
CountDownLatch latch(1);
FastPairDataParser::ParseDecryptedPasskey(
kAesKeyBytes, encrypted_bytes,
[&notification](std::optional<DecryptedPasskey> decrypted_passkey) {
[&](std::optional<DecryptedPasskey> decrypted_passkey) {
EXPECT_FALSE(decrypted_passkey.has_value());
notification.Notify();
latch.CountDown();
});
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout));
latch.Await();
}
TEST(FastPairDataParserTest, DecryptSeekerPasskeySuccessfully) {
@@ -305,19 +314,18 @@ TEST(FastPairDataParserTest, DecryptSeekerPasskeySuccessfully) {
std::vector<uint8_t> encrypted_bytes(encrypted_bytes_array.begin(),
encrypted_bytes_array.end());
absl::Notification notification;
CountDownLatch latch(1);
FastPairDataParser::ParseDecryptedPasskey(
kAesKeyBytes, encrypted_bytes,
[&notification, &kPasskey,
&kSalt](std::optional<DecryptedPasskey> decrypted_passkey) {
EXPECT_TRUE(decrypted_passkey.has_value());
[&](std::optional<DecryptedPasskey> decrypted_passkey) {
ASSERT_TRUE(decrypted_passkey.has_value());
EXPECT_EQ(decrypted_passkey.value().message_type,
FastPairMessageType::kSeekersPasskey);
EXPECT_EQ(decrypted_passkey.value().passkey, kPasskey);
EXPECT_EQ(decrypted_passkey.value().salt, kSalt);
notification.Notify();
latch.CountDown();
});
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout));
latch.Await();
}
TEST(FastPairDataParserTest, DecryptProviderPasskeySuccessfully) {
@@ -348,19 +356,363 @@ TEST(FastPairDataParserTest, DecryptProviderPasskeySuccessfully) {
std::vector<uint8_t> encrypted_bytes(encrypted_bytes_array.begin(),
encrypted_bytes_array.end());
absl::Notification notification;
CountDownLatch latch(1);
FastPairDataParser::ParseDecryptedPasskey(
kAesKeyBytes, encrypted_bytes,
[&notification, &kPasskey,
&kSalt](std::optional<DecryptedPasskey> decrypted_passkey) {
EXPECT_TRUE(decrypted_passkey.has_value());
[&](std::optional<DecryptedPasskey> decrypted_passkey) {
ASSERT_TRUE(decrypted_passkey.has_value());
EXPECT_EQ(decrypted_passkey.value().message_type,
FastPairMessageType::kProvidersPasskey);
EXPECT_EQ(decrypted_passkey.value().passkey, kPasskey);
EXPECT_EQ(decrypted_passkey.value().salt, kSalt);
notification.Notify();
latch.CountDown();
});
EXPECT_TRUE(notification.WaitForNotificationWithTimeout(kWaitTimeout));
latch.Await();
}
TEST(FastPairDataParserTest, ParseNotDiscoverableAdvertisementEmpty) {
CountDownLatch latch(1);
FastPairDataParser::ParseNotDiscoverableAdvertisement(
"", kDeviceAddress,
[&](const std::optional<NonDiscoverableAdvertisement> advertisement) {
EXPECT_FALSE(advertisement.has_value());
latch.CountDown();
});
latch.Await();
}
TEST(FastPairDataParserTest,
ParseNotDiscoverableAdvertisementNoApplicibleData) {
std::vector<uint8_t> bytes = FastPairServiceDataCreator::Builder()
.SetHeader(kNotDiscoverableAdvHeader)
.Build()
->CreateServiceData();
CountDownLatch latch(1);
FastPairDataParser::ParseNotDiscoverableAdvertisement(
std::string(bytes.begin(), bytes.end()), kDeviceAddress,
[&](const std::optional<NonDiscoverableAdvertisement> advertisement) {
EXPECT_FALSE(advertisement.has_value());
latch.CountDown();
});
latch.Await();
}
TEST(FastPairDataParserTest,
ParseNotDiscoverableAdvertisementAccountKeyFilter) {
const std::vector<uint8_t> kSaltBytes = {0x01};
std::vector<uint8_t> bytes = FastPairServiceDataCreator::Builder()
.SetHeader(kNotDiscoverableAdvHeader)
.SetModelId(kModelId)
.AddExtraFieldHeader(kAccountKeyFilterHeader)
.AddExtraField(kAccountKeyFilter)
.AddExtraFieldHeader(kSaltHeader)
.AddExtraField(kSalt)
.Build()
->CreateServiceData();
CountDownLatch latch(1);
FastPairDataParser::ParseNotDiscoverableAdvertisement(
std::string(bytes.begin(), bytes.end()), kDeviceAddress,
[&](const std::optional<NonDiscoverableAdvertisement> advertisement) {
ASSERT_TRUE(advertisement.has_value());
EXPECT_EQ(absl::BytesToHexString(
std::string(advertisement->account_key_filter.begin(),
advertisement->account_key_filter.end())),
kAccountKeyFilter);
EXPECT_EQ(advertisement->salt, kSaltBytes);
EXPECT_EQ(advertisement->type,
NonDiscoverableAdvertisement::Type::kShowUi);
EXPECT_FALSE(advertisement->battery_notification.has_value());
latch.CountDown();
});
latch.Await();
}
TEST(FastPairDataParserTest,
ParseNotDiscoverableAdvertisementAccountKeyFilterNoNotification) {
const std::vector<uint8_t> kSaltBytes = {0x01};
std::vector<uint8_t> bytes =
FastPairServiceDataCreator::Builder()
.SetHeader(kNotDiscoverableAdvHeader)
.SetModelId(kModelId)
.AddExtraFieldHeader(
/*accountKeyFilterNoNotificationHeader*/ 0b01100010)
.AddExtraField(kAccountKeyFilter)
.AddExtraFieldHeader(kSaltHeader)
.AddExtraField(kSalt)
.Build()
->CreateServiceData();
CountDownLatch latch(1);
FastPairDataParser::ParseNotDiscoverableAdvertisement(
std::string(bytes.begin(), bytes.end()), kDeviceAddress,
[&](const std::optional<NonDiscoverableAdvertisement> advertisement) {
ASSERT_TRUE(advertisement.has_value());
EXPECT_EQ(absl::BytesToHexString(
std::string(advertisement->account_key_filter.begin(),
advertisement->account_key_filter.end())),
kAccountKeyFilter);
EXPECT_EQ(advertisement->salt, kSaltBytes);
EXPECT_EQ(advertisement->type,
NonDiscoverableAdvertisement::Type::kHideUi);
EXPECT_FALSE(advertisement->battery_notification.has_value());
latch.CountDown();
});
latch.Await();
}
TEST(FastPairDataParserTest, ParseNotDiscoverableAdvertisementWrongVersion) {
std::vector<uint8_t> bytes = FastPairServiceDataCreator::Builder()
.SetHeader(/*InvalidHeader*/ 0b00100000)
.Build()
->CreateServiceData();
CountDownLatch latch(1);
FastPairDataParser::ParseNotDiscoverableAdvertisement(
std::string(bytes.begin(), bytes.end()), kDeviceAddress,
[&](const std::optional<NonDiscoverableAdvertisement> advertisement) {
EXPECT_FALSE(advertisement.has_value());
latch.CountDown();
});
latch.Await();
}
TEST(FastPairDataParserTest,
ParseNotDiscoverableAdvertisementZeroLengthExtraField) {
std::vector<uint8_t> bytes = FastPairServiceDataCreator::Builder()
.SetHeader(kNotDiscoverableAdvHeader)
.SetModelId(kModelId)
.AddExtraFieldHeader(kAccountKeyFilterHeader)
.AddExtraField("")
.AddExtraFieldHeader(kSaltHeader)
.AddExtraField(kSalt)
.Build()
->CreateServiceData();
CountDownLatch latch(1);
FastPairDataParser::ParseNotDiscoverableAdvertisement(
std::string(bytes.begin(), bytes.end()), kDeviceAddress,
[&](const std::optional<NonDiscoverableAdvertisement> advertisement) {
EXPECT_FALSE(advertisement.has_value());
latch.CountDown();
});
latch.Await();
}
TEST(FastPairDataParserTest, ParseNotDiscoverableAdvertisementWrongType) {
std::vector<uint8_t> bytes =
FastPairServiceDataCreator::Builder()
.SetHeader(kNotDiscoverableAdvHeader)
.SetModelId(kModelId)
.AddExtraFieldHeader /*InvalidType*/ (0b01100001)
.AddExtraField(kAccountKeyFilter)
.AddExtraFieldHeader(kSaltHeader)
.AddExtraField(kSalt)
.Build()
->CreateServiceData();
CountDownLatch latch(1);
FastPairDataParser::ParseNotDiscoverableAdvertisement(
std::string(bytes.begin(), bytes.end()), kDeviceAddress,
[&](const std::optional<NonDiscoverableAdvertisement> advertisement) {
EXPECT_FALSE(advertisement.has_value());
latch.CountDown();
});
latch.Await();
}
TEST(FastPairDataParserTest, ParseNotDiscoverableAdvertisementSaltTwoBytes) {
const std::vector<uint8_t> kLargeSaltBytes = {0xC7, 0xC8};
std::vector<uint8_t> bytes =
FastPairServiceDataCreator::Builder()
.SetHeader(kNotDiscoverableAdvHeader)
.SetModelId(kModelId)
.AddExtraFieldHeader(kAccountKeyFilterHeader)
.AddExtraField(kAccountKeyFilter)
.AddExtraFieldHeader(/*SaltTwoBytes*/ 0b00100001)
.AddExtraField(/*LargeSalt*/ "C7C8")
.Build()
->CreateServiceData();
CountDownLatch latch(1);
FastPairDataParser::ParseNotDiscoverableAdvertisement(
std::string(bytes.begin(), bytes.end()), kDeviceAddress,
[&](const std::optional<NonDiscoverableAdvertisement> advertisement) {
ASSERT_TRUE(advertisement.has_value());
EXPECT_EQ(absl::BytesToHexString(
std::string(advertisement->account_key_filter.begin(),
advertisement->account_key_filter.end())),
kAccountKeyFilter);
EXPECT_EQ(advertisement->salt, kLargeSaltBytes);
latch.CountDown();
});
latch.Await();
}
TEST(FastPairDataParserTest, ParseNotDiscoverableAdvertisementSaltTooLarge) {
std::vector<uint8_t> bytes = FastPairServiceDataCreator::Builder()
.SetHeader(kNotDiscoverableAdvHeader)
.SetModelId(kModelId)
.AddExtraFieldHeader(kAccountKeyFilterHeader)
.AddExtraField(kAccountKeyFilter)
.AddExtraFieldHeader(0b00110001)
.AddExtraField(/*invalidSalt*/ "C7C8C9")
.Build()
->CreateServiceData();
CountDownLatch latch(1);
FastPairDataParser::ParseNotDiscoverableAdvertisement(
std::string(bytes.begin(), bytes.end()), kDeviceAddress,
[&](const std::optional<NonDiscoverableAdvertisement> advertisement) {
EXPECT_FALSE(advertisement.has_value());
latch.CountDown();
});
latch.Await();
}
TEST(FastPairDataParserTest, ParseNotDiscoverableAdvertisementWithBattery) {
const std::vector<uint8_t> kSaltBytes = {0x01};
std::vector<uint8_t> bytes = FastPairServiceDataCreator::Builder()
.SetHeader(kNotDiscoverableAdvHeader)
.SetModelId(kModelId)
.AddExtraFieldHeader(kAccountKeyFilterHeader)
.AddExtraField(kAccountKeyFilter)
.AddExtraFieldHeader(kSaltHeader)
.AddExtraField(kSalt)
.AddExtraFieldHeader(kBatteryHeader)
.AddExtraField(kBattery)
.Build()
->CreateServiceData();
CountDownLatch latch(1);
FastPairDataParser::ParseNotDiscoverableAdvertisement(
std::string(bytes.begin(), bytes.end()), kDeviceAddress,
[&](const std::optional<NonDiscoverableAdvertisement> advertisement) {
ASSERT_TRUE(advertisement.has_value());
EXPECT_EQ(absl::BytesToHexString(
std::string(advertisement->account_key_filter.begin(),
advertisement->account_key_filter.end())),
kAccountKeyFilter);
EXPECT_EQ(advertisement->salt, kSaltBytes);
EXPECT_EQ(advertisement->type,
NonDiscoverableAdvertisement::Type::kShowUi);
ASSERT_TRUE(advertisement->battery_notification.has_value());
EXPECT_EQ(advertisement->battery_notification->type,
BatteryNotification::Type::kShowUi);
EXPECT_FALSE(advertisement->battery_notification->battery_infos.at(0)
.is_charging);
EXPECT_EQ(
advertisement->battery_notification->battery_infos.at(0).percentage,
1);
EXPECT_FALSE(advertisement->battery_notification->battery_infos.at(1)
.is_charging);
EXPECT_EQ(
advertisement->battery_notification->battery_infos.at(1).percentage,
4);
ASSERT_TRUE(advertisement->battery_notification->battery_infos.at(2)
.is_charging);
EXPECT_EQ(
advertisement->battery_notification->battery_infos.at(2).percentage,
15);
latch.CountDown();
});
latch.Await();
}
TEST(FastPairDataParserTest, ParseNotDiscoverableAdvertisementMissingSalt) {
const std::vector<uint8_t> kDeviceAddressBytes = {17, 18, 19, 20, 21, 22};
std::vector<uint8_t> bytes = FastPairServiceDataCreator::Builder()
.SetHeader(kNotDiscoverableAdvHeader)
.SetModelId(kModelId)
.AddExtraFieldHeader(kAccountKeyFilterHeader)
.AddExtraField(kAccountKeyFilter)
.AddExtraFieldHeader(kBatteryHeader)
.AddExtraField(kBattery)
.Build()
->CreateServiceData();
CountDownLatch latch(1);
FastPairDataParser::ParseNotDiscoverableAdvertisement(
std::string(bytes.begin(), bytes.end()), kDeviceAddress,
[&](const std::optional<NonDiscoverableAdvertisement> advertisement) {
ASSERT_TRUE(advertisement.has_value());
EXPECT_EQ(absl::BytesToHexString(
std::string(advertisement->account_key_filter.begin(),
advertisement->account_key_filter.end())),
kAccountKeyFilter);
EXPECT_EQ(advertisement->salt, kDeviceAddressBytes);
EXPECT_EQ(advertisement->type,
NonDiscoverableAdvertisement::Type::kShowUi);
ASSERT_TRUE(advertisement->battery_notification.has_value());
EXPECT_EQ(advertisement->battery_notification->type,
BatteryNotification::Type::kShowUi);
EXPECT_FALSE(advertisement->battery_notification->battery_infos.at(0)
.is_charging);
EXPECT_EQ(
advertisement->battery_notification->battery_infos.at(0).percentage,
1);
EXPECT_FALSE(advertisement->battery_notification->battery_infos.at(1)
.is_charging);
EXPECT_EQ(
advertisement->battery_notification->battery_infos.at(1).percentage,
4);
ASSERT_TRUE(advertisement->battery_notification->battery_infos.at(2)
.is_charging);
EXPECT_EQ(
advertisement->battery_notification->battery_infos.at(2).percentage,
15);
latch.CountDown();
});
latch.Await();
}
TEST(FastPairDataParserTest, ParseNotDiscoverableAdvertisementWithBatteryNoUi) {
const std::vector<uint8_t> kSaltBytes = {0x01};
std::vector<uint8_t> bytes =
FastPairServiceDataCreator::Builder()
.SetHeader(kNotDiscoverableAdvHeader)
.SetModelId(kModelId)
.AddExtraFieldHeader(kAccountKeyFilterHeader)
.AddExtraField(kAccountKeyFilter)
.AddExtraFieldHeader(kSaltHeader)
.AddExtraField(kSalt)
.AddExtraFieldHeader(/*batterHeaderNoNotification*/ 0b00110100)
.AddExtraField(kBattery)
.Build()
->CreateServiceData();
CountDownLatch latch(1);
FastPairDataParser::ParseNotDiscoverableAdvertisement(
std::string(bytes.begin(), bytes.end()), kDeviceAddress,
[&](const std::optional<NonDiscoverableAdvertisement> advertisement) {
ASSERT_TRUE(advertisement.has_value());
EXPECT_EQ(absl::BytesToHexString(
std::string(advertisement->account_key_filter.begin(),
advertisement->account_key_filter.end())),
kAccountKeyFilter);
EXPECT_EQ(advertisement->salt, kSaltBytes);
EXPECT_EQ(advertisement->type,
NonDiscoverableAdvertisement::Type::kShowUi);
ASSERT_TRUE(advertisement->battery_notification.has_value());
EXPECT_EQ(advertisement->battery_notification->type,
BatteryNotification::Type::kHideUi);
EXPECT_FALSE(advertisement->battery_notification->battery_infos.at(0)
.is_charging);
EXPECT_EQ(
advertisement->battery_notification->battery_infos.at(0).percentage,
1);
EXPECT_FALSE(advertisement->battery_notification->battery_infos.at(1)
.is_charging);
EXPECT_EQ(
advertisement->battery_notification->battery_infos.at(1).percentage,
4);
ASSERT_TRUE(advertisement->battery_notification->battery_infos.at(2)
.is_charging);
EXPECT_EQ(
advertisement->battery_notification->battery_infos.at(2).percentage,
15);
latch.CountDown();
});
latch.Await();
}
} // namespace
+8 -10
View File
@@ -27,7 +27,6 @@ namespace nearby {
namespace fastpair {
namespace {
constexpr int kHeaderIndex = 0;
constexpr int kHeaderLength = 1;
constexpr int kHeaderLengthBitmask = 0b00011110;
@@ -36,28 +35,27 @@ constexpr int kHeaderVersionBitmask = 0b11100000;
constexpr int kHeaderVersionOffset = 5;
constexpr int kMinModelIdLength = 3;
constexpr int kMaxModelIdLength = 14;
} // namespace
int GetIdLength(const std::vector<uint8_t>* service_data) {
int FastPairDecoder::GetIdLength(const std::vector<uint8_t>* service_data) {
return service_data->size() == kMinModelIdLength
? kMinModelIdLength
: ((*service_data)[kHeaderIndex] & kHeaderLengthBitmask) >>
kHeaderLengthOffset;
}
bool IsIdLengthValid(const std::vector<uint8_t>* service_data) {
int id_length = GetIdLength(service_data);
return kMinModelIdLength <= id_length && id_length <= kMaxModelIdLength &&
id_length + kHeaderLength <= static_cast<int>(service_data->size());
}
int GetVersion(const std::vector<uint8_t>* service_data) {
int FastPairDecoder::GetVersion(const std::vector<uint8_t>* service_data) {
return service_data->size() == kMinModelIdLength
? 0
: ((*service_data)[kHeaderIndex] & kHeaderVersionBitmask) >>
kHeaderVersionOffset;
}
} // namespace
bool IsIdLengthValid(const std::vector<uint8_t>* service_data) {
int id_length = FastPairDecoder::GetIdLength(service_data);
return kMinModelIdLength <= id_length && id_length <= kMaxModelIdLength &&
id_length + kHeaderLength <= static_cast<int>(service_data->size());
}
bool FastPairDecoder::HasModelId(const std::vector<uint8_t>* service_data) {
return service_data != nullptr &&
+2
View File
@@ -25,6 +25,8 @@ namespace fastpair {
class FastPairDecoder {
public:
static int GetVersion(const std::vector<uint8_t>* service_data);
static int GetIdLength(const std::vector<uint8_t>* service_data);
static bool HasModelId(const std::vector<uint8_t>* service_data);
static std::optional<std::string> GetHexModelIdFromServiceData(
@@ -43,7 +43,7 @@ class FakeBlePeripheral : public api::BlePeripheral {
name_ = std::string(name);
const std::vector<uint8_t> service_data =
FastPairServiceDataCreator::Builder()
.SetModelId(std::string(model_id))
.SetModelId(model_id)
.Build()
->CreateServiceData();
ByteArray advertisement_bytes(
@@ -20,6 +20,7 @@
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
#include "absl/strings/escaping.h"
@@ -38,8 +39,8 @@ FastPairServiceDataCreator::Builder::SetHeader(uint8_t byte) {
}
FastPairServiceDataCreator::Builder&
FastPairServiceDataCreator::Builder::SetModelId(std::string model_id) {
model_id_ = model_id;
FastPairServiceDataCreator::Builder::SetModelId(absl::string_view model_id) {
model_id_ = std::string(model_id);
return *this;
}
@@ -50,8 +51,8 @@ FastPairServiceDataCreator::Builder::AddExtraFieldHeader(uint8_t header) {
}
FastPairServiceDataCreator::Builder&
FastPairServiceDataCreator::Builder::AddExtraField(std::string field) {
extra_fields_.push_back(field);
FastPairServiceDataCreator::Builder::AddExtraField(absl::string_view field) {
extra_fields_.push_back(std::string(field));
return *this;
}
@@ -19,6 +19,7 @@
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <vector>
namespace nearby {
@@ -36,9 +37,9 @@ class FastPairServiceDataCreator {
~Builder();
Builder& SetHeader(uint8_t byte);
Builder& SetModelId(std::string model_id);
Builder& SetModelId(std::string_view model_id);
Builder& AddExtraFieldHeader(uint8_t header);
Builder& AddExtraField(std::string field);
Builder& AddExtraField(std::string_view field);
std::unique_ptr<FastPairServiceDataCreator> Build();
private: