Add BleL2capPacket

PiperOrigin-RevId: 736771387
This commit is contained in:
Mingshiou Wu
2025-03-14 00:34:14 -07:00
committed by Copybara-Service
parent f168d0c91b
commit 2ea692213d
7 changed files with 554 additions and 22 deletions
+1
View File
@@ -460,6 +460,7 @@ let package = Package(
"connections/implementation/mediums/advertisements/advertisement_util_test.cc",
"connections/implementation/mediums/ble_v2_test.cc",
"connections/implementation/mediums/ble_v2/bloom_filter_test.cc",
"connections/implementation/mediums/ble_v2/ble_l2cap_packet_test.cc",
"connections/implementation/mediums/ble_v2/ble_packet_test.cc",
"connections/implementation/mediums/ble_v2/ble_advertisement_test.cc",
"connections/implementation/mediums/ble_v2/advertisement_read_result_test.cc",
@@ -54,6 +54,7 @@ cc_library(
srcs = [
"advertisement_read_result.cc",
"ble_advertisement.cc",
"ble_l2cap_packet.cc",
"ble_packet.cc",
"ble_utils.cc",
"discovered_peripheral_tracker.cc",
@@ -63,6 +64,7 @@ cc_library(
hdrs = [
"advertisement_read_result.h",
"ble_advertisement.h",
"ble_l2cap_packet.h",
"ble_packet.h",
"ble_utils.h",
"discovered_peripheral_callback.h",
@@ -111,6 +113,7 @@ cc_test(
"advertisement_read_result_test.cc",
"ble_advertisement_header_test.cc",
"ble_advertisement_test.cc",
"ble_l2cap_packet_test.cc",
"ble_packet_test.cc",
"ble_utils_test.cc",
"bloom_filter_test.cc",
@@ -62,7 +62,25 @@ class BleAdvertisement {
static constexpr int kServiceIdHashLength = 3;
static constexpr int kDeviceTokenLength = 2;
static constexpr int kVersionLength = 1;
static constexpr int kVersionBitmask = 0x0E0;
static constexpr int kSocketVersionBitmask = 0x01C;
static constexpr int kFastAdvertisementFlagBitmask = 0x002;
static constexpr int kDataSizeLength = 4; // Length of one int.
static constexpr int kFastDataSizeLength = 1; // Length of one byte.
static constexpr int kMinAdvertisementLength =
kVersionLength + kServiceIdHashLength + kDataSizeLength;
// The maximum length for a Gatt characteristic value is 512 bytes, so make
// sure the entire advertisement is less than that. The data can take up
// whatever space is remaining after the bytes preceding it.
static constexpr int kMaxAdvertisementLength = 512;
static constexpr int kMinFastAdvertisementLegth =
kVersionLength + kFastDataSizeLength;
// The maximum length for the scan response is 31 bytes. However, with the
// required header that comes before the service data, this leaves the
// advertiser with 27 leftover bytes.
static constexpr int kMaxFastAdvertisementLength = 27;
static constexpr int kExtraFieldsMaskLength = 1;
// Hashable
bool operator==(const BleAdvertisement &rhs) const;
template <typename H>
@@ -160,26 +178,6 @@ class BleAdvertisement {
total_optional_length);
}
static constexpr int kVersionLength = 1;
static constexpr int kVersionBitmask = 0x0E0;
static constexpr int kSocketVersionBitmask = 0x01C;
static constexpr int kFastAdvertisementFlagBitmask = 0x002;
static constexpr int kDataSizeLength = 4; // Length of one int.
static constexpr int kFastDataSizeLength = 1; // Length of one byte.
static constexpr int kMinAdvertisementLength =
kVersionLength + kServiceIdHashLength + kDataSizeLength;
// The maximum length for a Gatt characteristic value is 512 bytes, so make
// sure the entire advertisement is less than that. The data can take up
// whatever space is remaining after the bytes preceding it.
static constexpr int kMaxAdvertisementLength = 512;
static constexpr int kMinFastAdvertisementLegth =
kVersionLength + kFastDataSizeLength;
// The maximum length for the scan response is 31 bytes. However, with the
// required header that comes before the service data, this leaves the
// advertiser with 27 leftover bytes.
static constexpr int kMaxFastAdvertisementLength = 27;
static constexpr int kExtraFieldsMaskLength = 1;
Version version_{Version::kUndefined};
SocketVersion socket_version_{SocketVersion::kUndefined};
bool fast_advertisement_ = false;
@@ -146,7 +146,7 @@ BleAdvertisementHeader::operator ByteArray() const {
// Convert psm_ value to 2-bytes.
ByteArray psm_bytes{kPsmValueByteLength};
char *data = psm_bytes.data();
data[0] = psm_ & 0xFF00;
data[0] = (psm_ & 0xFF00) >> 8;
data[1] = psm_ & 0x00FF;
// clang-format off
@@ -0,0 +1,192 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "connections/implementation/mediums/ble_v2/ble_l2cap_packet.h"
#include <string>
#include <utility>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "connections/implementation/mediums/ble_v2/ble_advertisement.h"
#include "connections/implementation/mediums/utils.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/logging.h"
#include "internal/platform/stream_reader.h"
namespace nearby {
namespace connections {
namespace mediums {
BleL2capPacket::BleL2capPacket(Command command,
const ByteArray* service_id_hash,
const ByteArray* data)
: command_(command) {
if (service_id_hash) {
service_id_hash_ = *service_id_hash;
}
if (data) {
advertisement_ = *data;
}
}
absl::StatusOr<BleL2capPacket> BleL2capPacket::CreateFromBytes(
const ByteArray& bytes) {
if (bytes.size() < kCommandLength) {
LOG(INFO) << "Cannot deserialize BleL2capPacket: expecting min "
<< kCommandLength << " bytes, got " << bytes.size();
return absl::InvalidArgumentError(
"Cannot deserialize BleL2capPacket: input bytes too short.");
}
StreamReader stream_reader(bytes);
// The first 1 byte is the command.
auto command_byte = stream_reader.ReadUint8();
if (!command_byte.has_value()) {
LOG(INFO)
<< "Cannot deserialize BleL2capPacket: command byte not available.";
return absl::InvalidArgumentError(
"Cannot deserialize BleL2capPacket: command byte not available.");
}
Command command_data = static_cast<Command>(*command_byte);
if (command_data == Command::kRequestAdvertisementFinish ||
command_data == Command::kRequestDataConnection ||
command_data == Command::kResponseServiceIdNotFound ||
command_data == Command::kResponseDataConnectionReady ||
command_data == Command::kResponseDataConnectionFailure) {
return BleL2capPacket(command_data, nullptr, nullptr);
}
if (!stream_reader.IsAvailable(kDataLength)) {
LOG(INFO) << "Cannot deserialize BleL2capPacket: data length bytes not "
"available.";
return absl::InvalidArgumentError(
"Cannot deserialize BleL2capPacket: data length bytes not available.");
}
int data_length = stream_reader.ReadUint16().value_or(0);
if (data_length == 0) {
LOG(INFO) << "Cannot deserialize BleL2capPacket: data length incorrect.";
return absl::InvalidArgumentError(
"Cannot deserialize BleL2capPacket: data length incorrect.");
}
if (command_data == Command::kRequestAdvertisement) {
if (data_length < BleAdvertisement::kServiceIdHashLength) {
LOG(INFO)
<< "Cannot deserialize BleL2capPacket: service id hash length, got "
<< data_length;
return absl::InvalidArgumentError(
"Cannot deserialize BleL2capPacket: service id hash length "
"incorrect.");
}
auto service_id_hash_data = stream_reader.ReadBytes(data_length);
if (!service_id_hash_data.has_value()) {
LOG(INFO) << "Cannot deserialize BleL2capPacket: service id hash not "
"available.";
return absl::InvalidArgumentError(
"Cannot deserialize BleL2capPacket: service id hash not available.");
}
return BleL2capPacket(command_data, &service_id_hash_data.value(), nullptr);
} else if (command_data == Command::kResponseAdvertisement) {
if (data_length > BleAdvertisement::kMaxAdvertisementLength) {
LOG(INFO) << "Cannot deserialize BleL2capPacket: advertisement length, "
"got "
<< data_length;
return absl::InvalidArgumentError(
"Cannot deserialize BleL2capPacket: advertisement length incorrect.");
}
auto advertisement_data = stream_reader.ReadBytes(data_length);
if (!advertisement_data.has_value()) {
LOG(INFO)
<< "Cannot deserialize BleL2capPacket: advertisement not available.";
return absl::InvalidArgumentError(
"Cannot deserialize BleL2capPacket: advertisement not available.");
}
return BleL2capPacket(command_data, nullptr, &advertisement_data.value());
} else {
LOG(INFO) << "Cannot deserialize BleL2capPacket: unsupported command "
<< static_cast<int>(command_data);
return absl::InvalidArgumentError(
"Cannot deserialize BleL2capPacket: unsupported command.");
}
}
absl::StatusOr<ByteArray> BleL2capPacket::ByteArrayForRequestAdvertisement(
const std::string& service_id) {
if (service_id.empty()) {
return absl::InvalidArgumentError(
"Cannot deserialize BleL2capPacket: service id is empty.");
}
ByteArray service_id_hash = GenerateServiceIdHash(service_id);
return ByteArrayForCommand(BleL2capPacket::Command::kRequestAdvertisement,
&service_id_hash);
}
absl::StatusOr<ByteArray> BleL2capPacket::ByteArrayForResponseAdvertisement(
const ByteArray& advertisement) {
if (advertisement.size() < BleAdvertisement::kMinAdvertisementLength) {
return absl::InvalidArgumentError(
"Cannot deserialize BleL2capPacket: advertisement size is too small.");
}
return ByteArrayForCommand(BleL2capPacket::Command::kResponseAdvertisement,
&advertisement);
}
ByteArray BleL2capPacket::ByteArrayForRequestAdvertisementFinish() {
return ByteArrayForCommand(
BleL2capPacket::Command::kRequestAdvertisementFinish, {});
}
ByteArray BleL2capPacket::ByteArrayForServiceIdNotFound() {
return ByteArrayForCommand(Command::kResponseServiceIdNotFound, {});
}
ByteArray BleL2capPacket::ByteArrayForRequestDataConnection() {
return ByteArrayForCommand(Command::kRequestDataConnection, {});
}
ByteArray BleL2capPacket::ByteArrayForDataConnectionReady() {
return ByteArrayForCommand(Command::kResponseDataConnectionReady, {});
}
ByteArray BleL2capPacket::ByteArrayForDataConnectionFailure() {
return ByteArrayForCommand(Command::kResponseDataConnectionFailure, {});
}
ByteArray BleL2capPacket::GenerateServiceIdHash(const std::string& service_id) {
return Utils::Sha256Hash(service_id, BleAdvertisement::kServiceIdHashLength);
}
ByteArray BleL2capPacket::ByteArrayForCommand(BleL2capPacket::Command command,
const ByteArray* data) {
std::string out;
if (data) {
ByteArray length_bytes{kDataLength};
char* length_bytes_data = length_bytes.data();
length_bytes_data[0] = (data->size() & 0xFF00) >> 8;
length_bytes_data[1] = data->size() & 0x00FF;
out = absl::StrCat(std::string(1, static_cast<char>(command)),
std::string(length_bytes), std::string(*data));
} else {
out = static_cast<char>(command);
}
return ByteArray{std::move(out)};
}
} // namespace mediums
} // namespace connections
} // namespace nearby
@@ -0,0 +1,135 @@
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_L2CAP_PACKET_H_
#define CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_L2CAP_PACKET_H_
#include <string>
#include "absl/status/statusor.h"
#include "connections/implementation/mediums/ble_v2/ble_advertisement.h"
#include "internal/platform/byte_array.h"
namespace nearby {
namespace connections {
namespace mediums {
// Represents the format of the BLE L2CAP packet used in L2CAP socket for
// requesting/responsing.
//
// [COMMAND] or [COMMAND][LENGTH][DATA]
//
class BleL2capPacket {
public:
enum class Command {
// Use to fetch advertisement from server with serviceId.
kRequestAdvertisement = 1,
// Use to notify the server have fetched all advertisements completely.
kRequestAdvertisementFinish = 2,
// Use to notify the server current L2CAP socket use for file transferring.
kRequestDataConnection = 3,
// 11-20 reserved
// Use to response the advertisement raw data to the client.
kResponseAdvertisement = 21,
// Use to response the queried service ID not exist in server side.
kResponseServiceIdNotFound = 22,
// Use to notify the data connection ready
kResponseDataConnectionReady = 23,
// Use to notify failure for requesting the data connection.
kResponseDataConnectionFailure = 24,
};
static constexpr int kCommandLength = 1;
static constexpr int kDataLength = 2;
// The maximum length of a BleL2capPacket is the length when carry the
// advertisement raw data.
static constexpr int kMaxLength =
kCommandLength + kDataLength + BleAdvertisement::kMaxAdvertisementLength;
// Creates a BleL2capPacket from the raw bytes.
static absl::StatusOr<BleL2capPacket> CreateFromBytes(const ByteArray& bytes);
// Creates a BleL2capPacket for requesting advertisement with service ID.
static absl::StatusOr<ByteArray> ByteArrayForRequestAdvertisement(
const std::string& service_id);
// Creates a BleL2capPacket for responding advertisement with raw data.
static absl::StatusOr<ByteArray> ByteArrayForResponseAdvertisement(
const ByteArray& advertisement);
// Creates a BleL2capPacket for requesting advertisement finish.
static ByteArray ByteArrayForRequestAdvertisementFinish();
// Creates a BleL2capPacket for responding service ID not found.
static ByteArray ByteArrayForServiceIdNotFound();
// Creates a BleL2capPacket for requesting data connection.
static ByteArray ByteArrayForRequestDataConnection();
// Creates a BleL2capPacket for responding data connection ready.
static ByteArray ByteArrayForDataConnectionReady();
// Creates a BleL2capPacket for responding data connection failure.
static ByteArray ByteArrayForDataConnectionFailure();
// Generates the service ID hash bytes from the service ID string.
static ByteArray GenerateServiceIdHash(const std::string& service_id);
// Checks if the length of the BleL2capPacket is valid.
static bool IsValidLength(int length) {
return length > 0 && length <= kMaxLength;
}
BleL2capPacket(Command command, const ByteArray* service_id_hash,
const ByteArray* data);
BleL2capPacket(const BleL2capPacket&) = default;
BleL2capPacket& operator=(const BleL2capPacket&) = default;
BleL2capPacket(BleL2capPacket&&) = default;
BleL2capPacket& operator=(BleL2capPacket&&) = default;
~BleL2capPacket() = default;
explicit operator ByteArray() const;
// The service ID hash bytes, only exist for command REQUEST_ADVERTISEMENT and
// Command.REQUEST_DATA_CONNECTION.
ByteArray GetServiceIdHash() const { return service_id_hash_; }
// The advertisement raw bytes, only exist for command RESPONSE_ADVERTISEMENT.
ByteArray GetAdvertisement() const { return advertisement_; }
bool IsErrorServiceIdNotFound() const {
return command_ == Command::kResponseServiceIdNotFound;
}
bool IsAdvertisementResponse() const {
return command_ == Command::kResponseAdvertisement;
}
bool IsFetchAdvertisementRequest() const {
return command_ == Command::kRequestAdvertisement;
}
bool IsFetchAdvertisementFinished() const {
return command_ == Command::kRequestAdvertisementFinish;
}
bool IsDataConnectionRequest() const {
return command_ == Command::kRequestDataConnection;
}
bool IsDataConnectionReadyResponse() const {
return command_ == Command::kResponseDataConnectionReady;
}
bool IsDataConnectionFailureResponse() const {
return command_ == Command::kResponseDataConnectionFailure;
}
private:
BleL2capPacket() = default;
static ByteArray ByteArrayForCommand(Command command, const ByteArray* data);
Command command_;
ByteArray service_id_hash_;
ByteArray advertisement_;
};
} // namespace mediums
} // namespace connections
} // namespace nearby
#endif // CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_L2CAP_PACKET_H_
@@ -0,0 +1,203 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "connections/implementation/mediums/ble_v2/ble_l2cap_packet.h"
#include <string>
#include "gtest/gtest.h"
#include "absl/strings/string_view.h"
#include "internal/platform/byte_array.h"
namespace nearby {
namespace connections {
namespace mediums {
namespace {
constexpr absl::string_view kInCorrectData = {"\x01\x02\x03\x04\x05"};
constexpr absl::string_view kServiceID = "1test_service_id";
constexpr absl::string_view kCorrectData = {
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00"};
TEST(BleL2capPacketTest, ByteArrayForRequestAdvertisementWithCorrectServiceID) {
auto byte_array =
BleL2capPacket::ByteArrayForRequestAdvertisement(std::string(kServiceID));
ASSERT_TRUE(byte_array.ok());
auto result = BleL2capPacket::CreateFromBytes(*byte_array);
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result.value().IsFetchAdvertisementRequest());
ASSERT_EQ(result.value().GetServiceIdHash(),
BleL2capPacket::GenerateServiceIdHash(std::string(kServiceID)));
}
TEST(BleL2capPacketTest, ByteArrayForRequestAdvertisementWithEmptyServiceID) {
auto byte_array = BleL2capPacket::ByteArrayForRequestAdvertisement("");
ASSERT_FALSE(byte_array.ok());
}
TEST(BleL2capPacketTest,
ByteArrayForResponseAdvertisementWithCorrectAdvertisement) {
ByteArray advertisement(kCorrectData.data(), kCorrectData.size());
auto byte_array =
BleL2capPacket::ByteArrayForResponseAdvertisement(advertisement);
ASSERT_TRUE(byte_array.ok());
auto result = BleL2capPacket::CreateFromBytes(*byte_array);
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result.value().IsAdvertisementResponse());
ASSERT_EQ(result.value().GetAdvertisement(), advertisement);
}
TEST(BleL2capPacketTest,
ByteArrayForResponseAdvertisementWithInCorrectAdvertisement) {
ByteArray advertisement(kInCorrectData.data(), kInCorrectData.size());
auto byte_array =
BleL2capPacket::ByteArrayForResponseAdvertisement(advertisement);
ASSERT_FALSE(byte_array.ok());
}
TEST(BleL2capPacketTest,
ByteArrayForResponseAdvertisementWithLargeAdvertisement) {
auto byte_array =
BleL2capPacket::ByteArrayForResponseAdvertisement(ByteArray(300));
ASSERT_TRUE(byte_array.ok());
auto result = BleL2capPacket::CreateFromBytes(byte_array.value());
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result.value().IsAdvertisementResponse());
ASSERT_EQ(result.value().GetAdvertisement().size(), 300);
}
TEST(BleL2capPacketTest, ByteArrayForRequestAdvertisementFinish) {
ByteArray byte_array =
BleL2capPacket::ByteArrayForRequestAdvertisementFinish();
auto result = BleL2capPacket::CreateFromBytes(byte_array);
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result.value().IsFetchAdvertisementFinished());
}
TEST(BleL2capPacketTest, ByteArrayForServiceIdNotFound) {
ByteArray byte_array = BleL2capPacket::ByteArrayForServiceIdNotFound();
auto result = BleL2capPacket::CreateFromBytes(byte_array);
ASSERT_TRUE(result.value().IsErrorServiceIdNotFound());
}
TEST(BleL2capPacketTest, ByteArrayForRequestDataConnection) {
ByteArray byte_array = BleL2capPacket::ByteArrayForRequestDataConnection();
auto result = BleL2capPacket::CreateFromBytes(byte_array);
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result.value().IsDataConnectionRequest());
}
TEST(BleL2capPacketTest, ByteArrayForDataConnectionReady) {
ByteArray byte_array = BleL2capPacket::ByteArrayForDataConnectionReady();
auto result = BleL2capPacket::CreateFromBytes(byte_array);
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result.value().IsDataConnectionReadyResponse());
}
TEST(BleL2capPacketTest, ByteArrayForDataConnectionFailure) {
ByteArray byte_array = BleL2capPacket::ByteArrayForDataConnectionFailure();
auto result = BleL2capPacket::CreateFromBytes(byte_array);
ASSERT_TRUE(result.ok());
ASSERT_TRUE(result.value().IsDataConnectionFailureResponse());
}
TEST(BleL2capPacketTest, CreateFromBytesWithEmptyData) {
auto byte_array = BleL2capPacket::CreateFromBytes(ByteArray());
ASSERT_FALSE(byte_array.ok());
}
TEST(BleL2capPacketTest, CreateFromBytesWithInvalidCommand) {
ByteArray byte_array(nullptr, 1);
auto result = BleL2capPacket::CreateFromBytes(byte_array);
ASSERT_FALSE(result.ok());
}
TEST(BleL2capPacketTest, CreateFromBytesWithoutDataLength) {
ByteArray byte_array{1};
char *byte_array_data = byte_array.data();
byte_array_data[0] =
static_cast<char>(BleL2capPacket::Command::kRequestAdvertisement);
auto result = BleL2capPacket::CreateFromBytes(byte_array);
ASSERT_FALSE(result.ok());
}
TEST(BleL2capPacketTest, CreateFromBytesWithEmptyDataLength) {
ByteArray byte_array{3};
char *byte_array_data = byte_array.data();
byte_array_data[0] =
static_cast<char>(BleL2capPacket::Command::kRequestAdvertisement);
byte_array_data[1] = 0x00;
byte_array_data[2] = 0x00;
auto result = BleL2capPacket::CreateFromBytes(byte_array);
ASSERT_FALSE(result.ok());
}
TEST(BleL2capPacketTest, CreateFromBytesWithInvalidServiceIdHashLength) {
ByteArray byte_array{3};
char *byte_array_data = byte_array.data();
byte_array_data[0] =
static_cast<char>(BleL2capPacket::Command::kRequestAdvertisement);
byte_array_data[1] = 0x00;
byte_array_data[2] = 0x01;
auto result = BleL2capPacket::CreateFromBytes(byte_array);
ASSERT_FALSE(result.ok());
}
TEST(BleL2capPacketTest, CreateFromBytesWithTooLongAdvertisementLength) {
ByteArray byte_array{3};
char *byte_array_data = byte_array.data();
byte_array_data[0] =
static_cast<char>(BleL2capPacket::Command::kResponseAdvertisement);
byte_array_data[1] = 0xFF;
byte_array_data[2] = 0xFF;
auto result = BleL2capPacket::CreateFromBytes(byte_array);
ASSERT_FALSE(result.ok());
}
TEST(BleL2capPacketTest, CreateFromBytesWithInvalidLengthAdvertisement) {
ByteArray byte_array{4};
char *byte_array_data = byte_array.data();
byte_array_data[0] =
static_cast<char>(BleL2capPacket::Command::kResponseAdvertisement);
byte_array_data[1] = 0x00;
byte_array_data[2] = 0x03;
byte_array_data[3] = 0x01;
auto result = BleL2capPacket::CreateFromBytes(byte_array);
ASSERT_FALSE(result.ok());
}
} // namespace
} // namespace mediums
} // namespace connections
} // namespace nearby