[BLEREFACTOR]:Add CreateFromStream API to BleL2capPacket

PiperOrigin-RevId: 832082450
This commit is contained in:
Edwin Wu
2025-11-13 18:06:01 -08:00
committed by Copybara-Service
parent 8375b82a86
commit 8d18c91e86
5 changed files with 335 additions and 2 deletions
+1 -1
View File
@@ -184,12 +184,12 @@ cc_test(
"//internal/platform/implementation:comm",
"//internal/platform/implementation/g3", # buildcleaner: keep
"//internal/test",
"//proto/mediums:ble_frames_cc_proto",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/hash:hash_testing",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:string_view",
"@com_google_absl//absl/time",
"@com_google_googletest//:gtest_main",
@@ -14,6 +14,7 @@
#include "connections/implementation/mediums/ble/ble_l2cap_packet.h"
#include <cstdint>
#include <string>
#include <utility>
@@ -23,6 +24,8 @@
#include "connections/implementation/mediums/ble/ble_advertisement.h"
#include "connections/implementation/mediums/utils.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/exception.h"
#include "internal/platform/input_stream.h"
#include "internal/platform/logging.h"
#include "internal/platform/stream_reader.h"
@@ -42,6 +45,95 @@ BleL2capPacket::BleL2capPacket(Command command,
}
}
absl::StatusOr<BleL2capPacket> BleL2capPacket::CreateFromStream(
InputStream& input_stream) {
// The first 1 byte is the command.
ExceptionOr<ByteArray> command_byte = input_stream.Read(kCommandLength);
if (!command_byte.ok()) {
LOG(WARNING) << "Cannot read BleL2capPacket: command byte not available.";
return absl::InvalidArgumentError(
"Cannot read BleL2capPacket: command byte not available.");
}
Command command_data =
static_cast<Command>((int)command_byte.result().data()[0]);
VLOG(1) << "command_data: " << static_cast<int>(command_data);
int data_length = 0;
if (command_data == Command::kRequestAdvertisement ||
command_data == Command::kResponseAdvertisement) {
ExceptionOr<ByteArray> length_byte = input_stream.Read(2);
if (!length_byte.ok()) {
LOG(WARNING) << "Cannot read BleL2capPacket: length byte not available.";
return absl::InvalidArgumentError(
"Cannot read BleL2capPacket: length byte not available.");
}
data_length = (static_cast<uint8_t>(length_byte.result().data()[0]) << 8) |
(static_cast<uint8_t>(length_byte.result().data()[1]));
if (data_length == 0) {
LOG(WARNING) << "Cannot read BleL2capPacket: data length incorrect.";
return absl::InvalidArgumentError(
"Cannot read BleL2capPacket: data length incorrect.");
}
}
switch (command_data) {
case Command::kRequestAdvertisementFinish:
case Command::kRequestDataConnection:
case Command::kResponseServiceIdNotFound:
case Command::kResponseDataConnectionReady:
case Command::kResponseDataConnectionFailure:
return BleL2capPacket(command_data, nullptr, nullptr);
case Command::kRequestAdvertisement: {
if (data_length < BleAdvertisement::kServiceIdHashLength) {
LOG(WARNING)
<< "Cannot read BleL2capPacket: service id hash length, got "
<< data_length;
return absl::InvalidArgumentError(
"Cannot read BleL2capPacket: service id hash length "
"incorrect.");
}
ExceptionOr<ByteArray> service_id_hash_byte =
input_stream.Read(data_length);
if (!service_id_hash_byte.ok() ||
service_id_hash_byte.result().size() != data_length) {
LOG(WARNING) << "Cannot read BleL2capPacket: service id hash byte "
"not available.";
return absl::InvalidArgumentError(
"Cannot read BleL2capPacket: service id hash byte not "
"available.");
}
return BleL2capPacket(command_data, &service_id_hash_byte.result(),
nullptr);
}
case Command::kResponseAdvertisement: {
if (data_length > BleAdvertisement::kMaxAdvertisementLength) {
LOG(INFO) << "Cannot read BleL2capPacket: advertisement length, got "
<< data_length;
return absl::InvalidArgumentError(
"Cannot read BleL2capPacket: advertisement length incorrect.");
}
ExceptionOr<ByteArray> advertisement_data =
input_stream.Read(data_length);
if (!advertisement_data.ok() ||
advertisement_data.result().size() != data_length) {
LOG(WARNING)
<< "Cannot read BleL2capPacket: advertisement not available.";
return absl::InvalidArgumentError(
"Cannot read BleL2capPacket: advertisement not available.");
}
return BleL2capPacket(command_data, nullptr,
&advertisement_data.result());
}
default:
// fall through
break;
}
LOG(WARNING) << "Cannot read BleL2capPacket: unsupported command "
<< static_cast<int>(command_data);
return absl::InvalidArgumentError(
"Cannot read BleL2capPacket: unsupported command.");
}
absl::StatusOr<BleL2capPacket> BleL2capPacket::CreateFromBytes(
const ByteArray& bytes) {
if (bytes.size() < kCommandLength) {
@@ -182,7 +274,7 @@ ByteArray BleL2capPacket::ByteArrayForCommand(BleL2capPacket::Command command,
out = absl::StrCat(std::string(1, static_cast<char>(command)),
std::string(length_bytes), std::string(*data));
} else {
out = static_cast<char>(command);
out = std::string(1, static_cast<char>(command));
}
return ByteArray{std::move(out)};
}
@@ -20,6 +20,7 @@
#include "absl/status/statusor.h"
#include "connections/implementation/mediums/ble/ble_advertisement.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/input_stream.h"
namespace nearby {
namespace connections {
@@ -59,6 +60,9 @@ class BleL2capPacket {
// Creates a BleL2capPacket from the raw bytes.
static absl::StatusOr<BleL2capPacket> CreateFromBytes(const ByteArray& bytes);
// Creates a BleL2capPacket from the input stream.
static absl::StatusOr<BleL2capPacket> CreateFromStream(
InputStream& input_stream);
// Creates a BleL2capPacket for requesting advertisement with service ID.
static absl::StatusOr<ByteArray> ByteArrayForRequestAdvertisement(
const std::string& service_id);
@@ -15,10 +15,13 @@
#include "connections/implementation/mediums/ble/ble_l2cap_packet.h"
#include <string>
#include <utility>
#include "gtest/gtest.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "internal/platform/byte_array.h"
#include "internal/platform/pipe.h"
namespace nearby {
namespace connections {
@@ -197,6 +200,209 @@ TEST(BleL2capPacketTest, CreateFromBytesWithInvalidLengthAdvertisement) {
ASSERT_FALSE(result.ok());
}
TEST(BleL2capPacketTest, CreateFromStreamFailsReadCommand) {
auto [input, output] = nearby::CreatePipe();
output->Write(ByteArray{});
output->Close();
auto ble_l2cap_packet = BleL2capPacket::CreateFromStream(*input);
ASSERT_FALSE(ble_l2cap_packet.ok());
}
TEST(BleL2capPacketTest, CreateFromStreamRequestAdvertisement) {
auto [input, output] = nearby::CreatePipe();
auto byte_array =
BleL2capPacket::ByteArrayForRequestAdvertisement(std::string(kServiceID));
ASSERT_TRUE(byte_array.ok());
output->Write(*byte_array);
output->Close();
auto ble_l2cap_packet = BleL2capPacket::CreateFromStream(*input);
ASSERT_TRUE(ble_l2cap_packet.ok());
EXPECT_TRUE(ble_l2cap_packet->IsFetchAdvertisementRequest());
EXPECT_EQ(ble_l2cap_packet->GetServiceIdHash(),
BleL2capPacket::GenerateServiceIdHash(std::string(kServiceID)));
}
TEST(BleL2capPacketTest, CreateFromStreamZeroDataLength) {
auto [input, output] = nearby::CreatePipe();
std::string out = absl::StrCat(
std::string(
1, static_cast<char>(BleL2capPacket::Command::kRequestAdvertisement)),
std::string("\x00\x00", 2));
output->Write(ByteArray{std::move(out)});
output->Close();
auto ble_l2cap_packet = BleL2capPacket::CreateFromStream(*input);
ASSERT_FALSE(ble_l2cap_packet.ok());
}
TEST(BleL2capPacketTest,
CreateFromStreamRequestAdvertisementBadServiceIdHashLength) {
auto [input, output] = nearby::CreatePipe();
std::string out = absl::StrCat(
std::string(
1, static_cast<char>(BleL2capPacket::Command::kRequestAdvertisement)),
std::string("\x00\x01", 2));
output->Write(ByteArray{std::move(out)});
output->Close();
auto ble_l2cap_packet = BleL2capPacket::CreateFromStream(*input);
ASSERT_FALSE(ble_l2cap_packet.ok());
}
TEST(BleL2capPacketTest, CreateFromStreamFailsReadServiceIdHash) {
auto [input, output] = nearby::CreatePipe();
std::string out = absl::StrCat(
std::string(
1, static_cast<char>(BleL2capPacket::Command::kRequestAdvertisement)),
std::string("\x00\x03", 2));
output->Write(ByteArray{std::move(out)});
output->Close();
auto ble_l2cap_packet = BleL2capPacket::CreateFromStream(*input);
ASSERT_FALSE(ble_l2cap_packet.ok());
}
TEST(BleL2capPacketTest, CreateFromStreamRequestAdvertisementFinish) {
auto [input, output] = nearby::CreatePipe();
auto byte_array = BleL2capPacket::ByteArrayForRequestAdvertisementFinish();
output->Write(byte_array);
output->Close();
auto ble_l2cap_packet = BleL2capPacket::CreateFromStream(*input);
ASSERT_TRUE(ble_l2cap_packet.ok());
EXPECT_TRUE(ble_l2cap_packet->IsFetchAdvertisementFinished());
}
TEST(BleL2capPacketTest, CreateFromStreamResponseAdvertisement) {
auto [input, output] = nearby::CreatePipe();
ByteArray advertisement(kCorrectData.data(), kCorrectData.size());
auto byte_array =
BleL2capPacket::ByteArrayForResponseAdvertisement(advertisement);
ASSERT_TRUE(byte_array.ok());
output->Write(*byte_array);
output->Close();
auto ble_l2cap_packet = BleL2capPacket::CreateFromStream(*input);
ASSERT_TRUE(ble_l2cap_packet.ok());
EXPECT_TRUE(ble_l2cap_packet->IsAdvertisementResponse());
EXPECT_EQ(ble_l2cap_packet->GetAdvertisement(), advertisement);
}
TEST(BleL2capPacketTest,
CreateFromStreamResponseAdvertisementBadAdvertisementLength) {
auto [input, output] = nearby::CreatePipe();
// 0xFFFF is too large for advertisement length.
std::string out = absl::StrCat(
std::string(1, static_cast<char>(
BleL2capPacket::Command::kResponseAdvertisement)),
"\xFF\xFF");
output->Write(ByteArray{std::move(out)});
output->Close();
auto ble_l2cap_packet = BleL2capPacket::CreateFromStream(*input);
ASSERT_FALSE(ble_l2cap_packet.ok());
}
TEST(BleL2capPacketTest, CreateFromStreamFailsReadAdvertisement) {
auto [input, output] = nearby::CreatePipe();
std::string out = absl::StrCat(
std::string(1, static_cast<char>(
BleL2capPacket::Command::kResponseAdvertisement)),
std::string("\x00\x0a", 2));
output->Write(ByteArray{std::move(out)});
output->Close();
auto ble_l2cap_packet = BleL2capPacket::CreateFromStream(*input);
ASSERT_FALSE(ble_l2cap_packet.ok());
}
TEST(BleL2capPacketTest, CreateFromStreamResponseLargeAdvertisement) {
auto [input, output] = nearby::CreatePipe();
ByteArray advertisement(300);
auto byte_array =
BleL2capPacket::ByteArrayForResponseAdvertisement(advertisement);
ASSERT_TRUE(byte_array.ok());
output->Write(*byte_array);
output->Close();
auto ble_l2cap_packet = BleL2capPacket::CreateFromStream(*input);
ASSERT_TRUE(ble_l2cap_packet.ok());
EXPECT_TRUE(ble_l2cap_packet->IsAdvertisementResponse());
EXPECT_EQ(ble_l2cap_packet->GetAdvertisement(), advertisement);
EXPECT_EQ(ble_l2cap_packet->GetAdvertisement().size(), 300);
}
TEST(BleL2capPacketTest, CreateFromStreamServiceIdNotFound) {
auto [input, output] = nearby::CreatePipe();
auto byte_array = BleL2capPacket::ByteArrayForServiceIdNotFound();
output->Write(byte_array);
output->Close();
auto ble_l2cap_packet = BleL2capPacket::CreateFromStream(*input);
ASSERT_TRUE(ble_l2cap_packet.ok());
EXPECT_TRUE(ble_l2cap_packet->IsErrorServiceIdNotFound());
}
TEST(BleL2capPacketTest, CreateFromStreamRequestDataConnection) {
auto [input, output] = nearby::CreatePipe();
auto byte_array = BleL2capPacket::ByteArrayForRequestDataConnection();
output->Write(byte_array);
output->Close();
auto ble_l2cap_packet = BleL2capPacket::CreateFromStream(*input);
ASSERT_TRUE(ble_l2cap_packet.ok());
EXPECT_TRUE(ble_l2cap_packet->IsDataConnectionRequest());
}
TEST(BleL2capPacketTest, CreateFromStreamDataConnectionReady) {
auto [input, output] = nearby::CreatePipe();
auto byte_array = BleL2capPacket::ByteArrayForDataConnectionReady();
output->Write(byte_array);
output->Close();
auto ble_l2cap_packet = BleL2capPacket::CreateFromStream(*input);
ASSERT_TRUE(ble_l2cap_packet.ok());
EXPECT_TRUE(ble_l2cap_packet->IsDataConnectionReadyResponse());
}
TEST(BleL2capPacketTest, CreateFromStreamDataConnectionFailure) {
auto [input, output] = nearby::CreatePipe();
auto byte_array = BleL2capPacket::ByteArrayForDataConnectionFailure();
output->Write(byte_array);
output->Close();
auto ble_l2cap_packet = BleL2capPacket::CreateFromStream(*input);
ASSERT_TRUE(ble_l2cap_packet.ok());
EXPECT_TRUE(ble_l2cap_packet->IsDataConnectionFailureResponse());
}
TEST(BleL2capPacketTest, CreateFromStreamUnsupportedCommand) {
auto [input, output] = nearby::CreatePipe();
std::string out = absl::StrCat(std::string(1, static_cast<char>(0XFF)));
output->Write(ByteArray{std::move(out)});
output->Close();
auto ble_l2cap_packet = BleL2capPacket::CreateFromStream(*input);
ASSERT_FALSE(ble_l2cap_packet.ok());
}
} // namespace
} // namespace mediums
} // namespace connections
@@ -174,6 +174,37 @@ TEST(BlePacketTest, ConstructionFromSerializedShortLengthDataBytesFails) {
EXPECT_FALSE(short_ble_packet.IsValid());
}
TEST(BlePacketTest, ConstructionFromEmptyBytesIsInvalid) {
BlePacket ble_packet((ByteArray()));
EXPECT_FALSE(ble_packet.IsValid());
}
TEST(BlePacketTest, ConstructionFromSerializedControlBytesWorks) {
ByteArray service_id_hash{std::string(kServiceIDHash)};
absl::StatusOr<BlePacket> org_ble_packet_status_or =
BlePacket::CreateControlIntroductionPacket(service_id_hash);
ASSERT_OK(org_ble_packet_status_or);
ByteArray ble_packet_bytes(org_ble_packet_status_or.value());
BlePacket ble_packet(ble_packet_bytes);
EXPECT_TRUE(ble_packet.IsValid());
EXPECT_TRUE(ble_packet.IsControlPacket());
EXPECT_EQ(ble_packet.GetServiceIdHash(), service_id_hash);
EXPECT_EQ(ble_packet.GetControlFrameType(),
SocketControlFrame::INTRODUCTION);
ASSERT_OK_AND_ASSIGN(
SocketVersion version,
ble_packet.GetIntroductonSocketVersion());
EXPECT_EQ(version, SocketVersion::V2);
}
TEST(BlePacketTest, ConstructionFromInvalidControlPacketIsInvalid) {
ByteArray invalid_control_packet_bytes("\x00\x00\x00\x01\x02\x03", 6);
BlePacket ble_packet(invalid_control_packet_bytes);
EXPECT_FALSE(ble_packet.IsValid());
}
TEST(BlePacketTest, IsControlPacketBytes) {
EXPECT_TRUE(BlePacket::IsControlPacketBytes(ByteArray("\x00\x00\x00", 3)));
EXPECT_FALSE(