diff --git a/Package.swift b/Package.swift index 7f85f259..41d7d78b 100644 --- a/Package.swift +++ b/Package.swift @@ -391,6 +391,7 @@ let package = Package( "connections/implementation/analytics/BUILD", "connections/implementation/flags/BUILD", "connections/implementation/mediums/ble_v2/BUILD", + "connections/implementation/mediums/multiplex/BUILD", "connections/implementation/mediums/BUILD", "connections/implementation/BUILD", "connections/implementation/fuzzers", @@ -445,6 +446,7 @@ let package = Package( "connections/implementation/mediums/ble_v2/ble_utils_test.cc", "connections/implementation/mediums/ble_v2/discovered_peripheral_tracker_test.cc", "connections/implementation/mediums/ble_v2/instant_on_lost_advertisement_test.cc", + "connections/implementation/mediums/multiplex/multiplex_frames_test.cc", "connections/implementation/mediums/webrtc_peer_id_test.cc", "connections/implementation/mediums/wifi_lan_test.cc", "connections/implementation/mediums/bluetooth_classic_test.cc", diff --git a/connections/implementation/mediums/BUILD b/connections/implementation/mediums/BUILD index 219afcb7..4a39873e 100644 --- a/connections/implementation/mediums/BUILD +++ b/connections/implementation/mediums/BUILD @@ -49,6 +49,7 @@ cc_library( "//connections:core_types", "//connections/implementation/flags:connections_flags", "//connections/implementation/mediums/ble_v2", + "//connections/implementation/mediums/multiplex", "//connections/implementation/mediums/webrtc", "//connections/implementation/proto:offline_wire_formats_cc_proto", "//internal/flags:nearby_flags", @@ -92,6 +93,7 @@ cc_library( visibility = [ "//connections/implementation:__pkg__", "//connections/implementation/mediums/ble_v2:__subpackages__", + "//connections/implementation/mediums/multiplex:__pkg__", "//connections/implementation/mediums/webrtc:__pkg__", ], deps = [ diff --git a/connections/implementation/mediums/multiplex/BUILD b/connections/implementation/mediums/multiplex/BUILD new file mode 100644 index 00000000..e134fc26 --- /dev/null +++ b/connections/implementation/mediums/multiplex/BUILD @@ -0,0 +1,75 @@ +# Copyright 2024 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. +licenses(["notice"]) + +cc_library( + name = "multiplex", + srcs = [ + "multiplex_frames.cc", + ], + hdrs = [ + "multiplex_frames.h", + ], + copts = ["-DCORE_ADAPTER_DLL"], + visibility = [ + "//connections/implementation:__subpackages__", + ], + deps = [ + "//connections/implementation/flags:connections_flags", + "//connections/implementation/mediums:utils", + "//internal/flags:nearby_flags", + "//internal/platform:base", + "//internal/platform:comm", + "//internal/platform:types", + "//internal/platform:util", + "//internal/platform:uuid", + "//internal/platform/implementation:comm", + "//proto/mediums:multiplex_frames_cc_proto", + "@aappleby_smhasher//:libmurmur3", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/functional:any_invocable", + "@com_google_absl//absl/numeric:int128", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/time", + "@com_google_absl//absl/types:optional", + ], +) + +cc_test( + name = "multiplex_test", + srcs = [ + "multiplex_frames_test.cc", + ], + deps = [ + ":multiplex", + "//internal/platform:base", + "//internal/platform:comm", + "//internal/platform:test_util", + "//internal/platform:types", + "//internal/platform/implementation/g3", # buildcleaner: keep + "//proto/mediums:multiplex_frames_cc_proto", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_absl//absl/hash:hash_testing", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings:string_view", + "@com_google_absl//absl/time", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/connections/implementation/mediums/multiplex/multiplex_frames.cc b/connections/implementation/mediums/multiplex/multiplex_frames.cc new file mode 100644 index 00000000..028183d2 --- /dev/null +++ b/connections/implementation/mediums/multiplex/multiplex_frames.cc @@ -0,0 +1,214 @@ +// Copyright 2024 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/multiplex/multiplex_frames.h" + +#include +#include + +#include "connections/implementation/mediums/utils.h" +#include "internal/platform/base64_utils.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/exception.h" +#include "internal/platform/logging.h" + + +namespace nearby { +namespace connections { +namespace mediums { +namespace multiplex { + +using ::location::nearby::mediums::MultiplexFrame; +using ::location::nearby::mediums::MultiplexControlFrame; +using ::location::nearby::mediums::ConnectionResponseFrame; + +ByteArray GenerateServiceIdHash(const std::string& service_id) { + return Utils::Sha256Hash(service_id, kServiceIdHashLength); +} + +ByteArray GenerateServiceIdHashWithSalt(const std::string& service_id, + std::string salt) { + if (salt.empty()) { + return GenerateServiceIdHash(service_id); + } + + return Utils::Sha256Hash(service_id + salt, kServiceIdHashLength); +} + +std::string GenerateServiceIdHashKey(const ByteArray& service_id_hash) { + return Base64Utils::Encode(service_id_hash); +} + +std::string GenerateServiceIdHashKey(const std::string& service_id) { + return GenerateServiceIdHashKey(GenerateServiceIdHash(service_id)); +} + +std::string GenerateServiceIdHashKeyWithSalt(const std::string& service_id, + std::string salt) { + return GenerateServiceIdHashKey( + GenerateServiceIdHashWithSalt(service_id, salt)); +} + +ByteArray ToBytes(MultiplexFrame&& frame) { + ByteArray bytes(frame.ByteSizeLong()); + frame.SerializeToArray(bytes.data(), bytes.size()); + return bytes; +} + +ByteArray ForConnectionRequest(std::string service_id, + std::string service_id_hash_salt) { + MultiplexFrame frame; + + frame.set_frame_type(MultiplexFrame::CONTROL_FRAME); + auto* header = frame.mutable_header(); + header->set_salted_service_id_hash(std::string( + GenerateServiceIdHashWithSalt(service_id, service_id_hash_salt))); + header->set_service_id_hash_salt(service_id_hash_salt); + + auto* control_frame = frame.mutable_control_frame(); + control_frame->set_control_frame_type( + MultiplexControlFrame::CONNECTION_REQUEST); + + return ToBytes(std::move(frame)); +} + +ByteArray ForConnectionResponse( + ByteArray& salted_service_id_hash, std::string service_id_hash_salt, + ConnectionResponseFrame::ConnectionResponseCode response_code) { + MultiplexFrame frame; + + frame.set_frame_type(MultiplexFrame::CONTROL_FRAME); + auto* header = frame.mutable_header(); + header->set_salted_service_id_hash(std::string(salted_service_id_hash)); + header->set_service_id_hash_salt(service_id_hash_salt); + + auto* control_frame = frame.mutable_control_frame(); + control_frame->set_control_frame_type( + MultiplexControlFrame::CONNECTION_RESPONSE); + + auto* response_frame = control_frame->mutable_connection_response_frame(); + response_frame->set_connection_response_code(response_code); + + return ToBytes(std::move(frame)); +} + +ByteArray ForDisconnection(std::string service_id, + std::string service_id_hash_salt) { + MultiplexFrame frame; + + frame.set_frame_type(MultiplexFrame::CONTROL_FRAME); + auto* header = frame.mutable_header(); + header->set_salted_service_id_hash(std::string( + GenerateServiceIdHashWithSalt(service_id, service_id_hash_salt))); + header->set_service_id_hash_salt(service_id_hash_salt); + + auto* control_frame = frame.mutable_control_frame(); + control_frame->set_control_frame_type( + MultiplexControlFrame::DISCONNECTION); + + return ToBytes(std::move(frame)); +} + +ByteArray ForData(std::string service_id, std::string service_id_hash_salt, + bool should_pass_salt, ByteArray& data) { + MultiplexFrame frame; + + frame.set_frame_type(MultiplexFrame::DATA_FRAME); + auto* header = frame.mutable_header(); + header->set_salted_service_id_hash(std::string( + GenerateServiceIdHashWithSalt(service_id, service_id_hash_salt))); + if (should_pass_salt) { + header->set_service_id_hash_salt(service_id_hash_salt); + } + + auto* data_frame = frame.mutable_data_frame(); + data_frame->set_data(std::string(std::move(data))); + + return ToBytes(std::move(frame)); +} + +ExceptionOr FromBytes(const ByteArray& multiplex_frame_bytes){ + MultiplexFrame frame; + + if (frame.ParseFromString(std::string(multiplex_frame_bytes))) { + if (!IsValid(frame)) { + return ExceptionOr(Exception::kInvalidProtocolBuffer); + } + return ExceptionOr(std::move(frame)); + } else { + return ExceptionOr(Exception::kInvalidProtocolBuffer); + } +} + +bool IsControlFrame(MultiplexFrame::MultiplexFrameType frame_type) { + return frame_type == MultiplexFrame::CONTROL_FRAME; +} + +bool IsDataFrame(MultiplexFrame::MultiplexFrameType frame_type) { + return frame_type == MultiplexFrame::DATA_FRAME; +} + +bool IsValid(const MultiplexFrame& frame) { + switch (frame.frame_type()) { + case MultiplexFrame::CONTROL_FRAME: + return IsValidControlFrame(frame); + case MultiplexFrame::DATA_FRAME: + return IsValidDataFrame(frame); + default: + return false; + } +} + +bool IsValidControlFrame(const MultiplexFrame& frame) { + if (!frame.has_control_frame()) { + return false; + } + + switch (frame.control_frame().control_frame_type()) { + case MultiplexControlFrame::CONNECTION_REQUEST: + case MultiplexControlFrame::CONNECTION_RESPONSE: + case MultiplexControlFrame::DISCONNECTION: + if (frame.header().salted_service_id_hash().size() == + kServiceIdHashLength) { + return true; + } + break; + default: + break; + } + + return false; +} + +bool IsValidDataFrame(const MultiplexFrame& frame) { + return frame.has_data_frame() && + frame.header().salted_service_id_hash().size() == kServiceIdHashLength; +} + +bool IsMultiplexFrame(const ByteArray& data) { + ExceptionOr frame = FromBytes(data); + if (!frame.ok()) { + return false; + } else { + NEARBY_LOGS(INFO) << "Checked data is a multiplex frame. Is Control ? " + << frame.result().has_control_frame() << ", is data ? " + << frame.result().has_data_frame(); + return true; + } +} + +} // namespace multiplex +} // namespace mediums +} // namespace connections +} // namespace nearby diff --git a/connections/implementation/mediums/multiplex/multiplex_frames.h b/connections/implementation/mediums/multiplex/multiplex_frames.h new file mode 100644 index 00000000..9f03f5b5 --- /dev/null +++ b/connections/implementation/mediums/multiplex/multiplex_frames.h @@ -0,0 +1,109 @@ + +// Copyright 2024 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_MULTIPLEX_MULTIPLEX_FRAMES_H_ +#define CORE_INTERNAL_MEDIUMS_MULTIPLEX_MULTIPLEX_FRAMES_H_ + +#include + +#include "internal/platform/byte_array.h" +#include "internal/platform/exception.h" +#include "proto/mediums/multiplex_frames.pb.h" + +namespace nearby { +namespace connections { +namespace mediums { +namespace multiplex { + +constexpr int kServiceIdHashLength = 4; + +// Serialize/Deserialize MultiplexFrame messages. + +// Parses incoming MultiplexFrame message. +// Returns MultiplexFrame if parser was able to understand it, or +// Exception::kInvalidProtocolBuffer, if parser failed. + +// Generates a service ID hash bytes with {@link +// MultiplexFrames#SERVICE_ID_HASH_LENGTH}. +ByteArray GenerateServiceIdHash(const std::string& service_id); + +// Generates a service ID hash bytes with salt and {@link +// MultiplexFrames#SERVICE_ID_HASH_LENGTH}. +ByteArray GenerateServiceIdHashWithSalt(const std::string& service_id, + std::string salt); + +// Converts the service Id hash bytes to a Base64 encoded string to be used as a +// {@code Map} key. +std::string GenerateServiceIdHashKey(const ByteArray& service_id_hash); + +// Generates a service ID hash bytes with {@link +// MultiplexFrames#SERVICE_ID_HASH_LENGTH} and converts to a Base64 encoded +// string to be used as a {@code Map} key. +std::string GenerateServiceIdHashKey(const std::string& service_id); + +// Generates a service ID hash bytes with salt and {@link +// MultiplexFrames#SERVICE_ID_HASH_LENGTH} and converts to a Base64 encoded +// string to be used as a { @code Map } key. +std::string GenerateServiceIdHashKeyWithSalt(const std::string& service_id, + std::string salt); + +// Build a MultiplexFrame Connection Request frame Bytes stream. +// @param service_id The service ID of the connection. +// @param service_id_hash_salt The salt used to generate the service ID hash. +ByteArray ForConnectionRequest(std::string service_id, + std::string service_id_hash_salt); + +// Build a MultiplexFrame Connection Response frame Bytes stream. +// @param salted_service_id_hash The salted service ID hash. +// @param service_id_hash_salt The salt used to generate the service ID hash. +// @param response_code The response code of the connection. +ByteArray ForConnectionResponse( + ByteArray& salted_service_id_hash, std::string service_id_hash_salt, + location::nearby::mediums::ConnectionResponseFrame::ConnectionResponseCode + response_code); + +// Build a MultiplexFrame Disconnection frame Bytes stream. +// @param service_id The service ID of the connection. +// @param service_id_hash_salt The salt used to generate the service ID hash. +ByteArray ForDisconnection(std::string service_id, + std::string service_id_hash_salt); + +// Build a MultiplexFrame Data frame Bytes stream. +// @param service_id The service ID of the connection. +// @param service_id_hash_salt The salt used to generate the service ID hash. +// @param should_pass_salt Whether to pass the salt in the data frame. +// @param data The data to send. +ByteArray ForData(std::string service_id, std::string service_id_hash_salt, + bool should_pass_salt, ByteArray& data); + +ExceptionOr FromBytes( + const ByteArray& multiplex_frame_bytes); + +bool IsControlFrame( + location::nearby::mediums::MultiplexFrame::MultiplexFrameType frame_type); +bool IsDataFrame( + location::nearby::mediums::MultiplexFrame::MultiplexFrameType frame_type); +bool IsValid(const location::nearby::mediums::MultiplexFrame& frame); +bool IsValidControlFrame( + const location::nearby::mediums::MultiplexFrame& frame); +bool IsValidDataFrame(const location::nearby::mediums::MultiplexFrame& frame); +bool IsMultiplexFrame(const ByteArray& data); + +} // namespace multiplex +} // namespace mediums +} // namespace connections +} // namespace nearby + +#endif // CORE_INTERNAL_MEDIUMS_MULTIPLEX_MULTIPLEX_FRAMES_H_ diff --git a/connections/implementation/mediums/multiplex/multiplex_frames_test.cc b/connections/implementation/mediums/multiplex/multiplex_frames_test.cc new file mode 100644 index 00000000..ad0b7665 --- /dev/null +++ b/connections/implementation/mediums/multiplex/multiplex_frames_test.cc @@ -0,0 +1,170 @@ +// Copyright 2024 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/multiplex/multiplex_frames.h" +#include +#include + +#include "gtest/gtest.h" +#include "absl/strings/string_view.h" +#include "internal/platform/byte_array.h" + +namespace nearby { +namespace connections { +namespace mediums { +namespace multiplex { + +using ::location::nearby::mediums::MultiplexFrame; +using ::location::nearby::mediums::MultiplexControlFrame; +using ::location::nearby::mediums::ConnectionResponseFrame; + +constexpr absl::string_view kServiceId_1 = "serviceId_1"; +constexpr absl::string_view kServiceId_2 = "serviceId_2"; + +TEST(MultiplexFrameTest, FrameValidation) { + const ByteArray data("abcdefghijklmnopqrstuvwxyz"); + MultiplexFrame frame; + EXPECT_FALSE(IsValid(frame)); + frame.set_frame_type(MultiplexFrame::CONTROL_FRAME); + EXPECT_FALSE(IsValidControlFrame(frame)); + + auto* control_frame = frame.mutable_control_frame(); + control_frame->set_control_frame_type( + MultiplexControlFrame::UNKNOWN_CONTROL_FRAME_TYPE); + EXPECT_FALSE(IsValidControlFrame(frame)); + auto* header = frame.mutable_header(); + header->set_salted_service_id_hash(std::string( + GenerateServiceIdHashWithSalt(std::string(kServiceId_1), "1234"))); + control_frame->set_control_frame_type( + MultiplexControlFrame::CONNECTION_REQUEST); + EXPECT_TRUE(IsValidControlFrame(frame)); + EXPECT_TRUE(IsValid(frame)); + control_frame->set_control_frame_type( + MultiplexControlFrame::CONNECTION_RESPONSE); + EXPECT_TRUE(IsValidControlFrame(frame)); + EXPECT_TRUE(IsValid(frame)); + control_frame->set_control_frame_type( + MultiplexControlFrame::DISCONNECTION); + EXPECT_TRUE(IsValidControlFrame(frame)); + EXPECT_TRUE(IsValid(frame)); + + EXPECT_FALSE(IsValidDataFrame(frame)); + frame.set_frame_type(MultiplexFrame::DATA_FRAME); + auto* data_frame = frame.mutable_data_frame(); + data_frame->set_data(std::string(std::move(data))); + EXPECT_TRUE(IsValidDataFrame(frame)); + EXPECT_TRUE(IsValid(frame)); + + frame.set_frame_type(MultiplexFrame::UNKNOWN_FRAME_TYPE); + EXPECT_FALSE(IsValid(frame)); + + frame.set_frame_type(MultiplexFrame::DATA_FRAME); + auto serialized_bytes = ByteArray(frame.SerializeAsString()); + EXPECT_TRUE(IsMultiplexFrame(std::move(serialized_bytes))); + + EXPECT_TRUE(IsControlFrame(MultiplexFrame::CONTROL_FRAME)); + EXPECT_FALSE(IsControlFrame(MultiplexFrame::DATA_FRAME)); + EXPECT_TRUE(IsDataFrame(MultiplexFrame::DATA_FRAME)); + EXPECT_FALSE(IsDataFrame(MultiplexFrame::UNKNOWN_FRAME_TYPE)); +} + +TEST(MultiplexFrameTest, HashValidtion) { + auto service_id_hash_1 = GenerateServiceIdHash(std::string(kServiceId_1)); + EXPECT_EQ(service_id_hash_1.size(), kServiceIdHashLength); + auto service_id_hash_2 = GenerateServiceIdHash(std::string(kServiceId_2)); + EXPECT_NE(service_id_hash_1, service_id_hash_2); + + auto hash_key_1 = GenerateServiceIdHashKey(service_id_hash_1); + auto hash_key_2 = GenerateServiceIdHashKey(service_id_hash_2); + EXPECT_NE(hash_key_1, hash_key_2); + + auto service_id_hash_with_salt_1 = + GenerateServiceIdHashWithSalt(std::string(kServiceId_1), "1234"); + EXPECT_EQ(service_id_hash_with_salt_1.size(), kServiceIdHashLength); + auto service_id_hash_with_salt_2 = + GenerateServiceIdHashWithSalt(std::string(kServiceId_2), "1234"); + EXPECT_NE(service_id_hash_with_salt_1, service_id_hash_with_salt_2); + service_id_hash_with_salt_2 = + GenerateServiceIdHashWithSalt(std::string(kServiceId_1), "abcd"); + EXPECT_NE(service_id_hash_with_salt_1, service_id_hash_with_salt_2); + + auto hash_key_with_salt_1 = + GenerateServiceIdHashKeyWithSalt(std::string(kServiceId_1), "1234"); + auto hash_key_with_salt_2 = + GenerateServiceIdHashKeyWithSalt(std::string(kServiceId_2), "1234"); + EXPECT_NE(hash_key_with_salt_1, hash_key_with_salt_2); +} + +TEST(MultiplexFrameTest, CanGenerateConnectionRequest) { + ByteArray bytes = ForConnectionRequest(std::string(kServiceId_1), "1234"); + auto response = FromBytes(bytes); + ASSERT_TRUE(response.ok()); + auto frame = response.result(); + EXPECT_EQ(frame.control_frame().control_frame_type(), + MultiplexControlFrame::CONNECTION_REQUEST); + EXPECT_EQ(frame.header().salted_service_id_hash(), + std::string(GenerateServiceIdHashWithSalt(std::string(kServiceId_1), + "1234"))); +} + +TEST(MultiplexFrameTest, CanGenerateConnectionRespons) { + auto service_id_hash_with_salt_2 = + GenerateServiceIdHashWithSalt(std::string(kServiceId_2), "1234"); + ByteArray bytes = + ForConnectionResponse(service_id_hash_with_salt_2, "1234", + ConnectionResponseFrame::CONNECTION_ACCEPTED); + auto response = FromBytes(bytes); + ASSERT_TRUE(response.ok()); + auto frame = response.result(); + EXPECT_EQ(frame.control_frame().control_frame_type(), + MultiplexControlFrame::CONNECTION_RESPONSE); + EXPECT_EQ(frame.header().salted_service_id_hash(), + std::string(service_id_hash_with_salt_2)); + EXPECT_EQ(frame.control_frame() + .connection_response_frame() + .connection_response_code(), + ConnectionResponseFrame::CONNECTION_ACCEPTED); +} + +TEST(MultiplexFrameTest, CanGenerateDisconnection) { + ByteArray bytes = ForDisconnection(std::string(kServiceId_1), "1234"); + auto response = FromBytes(bytes); + ASSERT_TRUE(response.ok()); + auto frame = response.result(); + EXPECT_EQ(frame.control_frame().control_frame_type(), + MultiplexControlFrame::DISCONNECTION); + EXPECT_EQ(frame.header().salted_service_id_hash(), + std::string(GenerateServiceIdHashWithSalt(std::string(kServiceId_1), + "1234"))); +} + +TEST(MultiplexFrameTest, CanGenerateData) { + ByteArray data("abcdefghijklmnopqrstuvwxyz"); + ByteArray bytes = + ForData(std::string(kServiceId_1), "1234", true, data); + auto response = FromBytes(bytes); + ASSERT_TRUE(response.ok()); + auto frame = response.result(); + EXPECT_EQ(frame.frame_type(), MultiplexFrame::DATA_FRAME); + EXPECT_EQ(frame.header().salted_service_id_hash(), + std::string(GenerateServiceIdHashWithSalt(std::string(kServiceId_1), + "1234"))); + EXPECT_EQ(frame.data_frame().data(), + std::string("abcdefghijklmnopqrstuvwxyz")); +} + +} // namespace multiplex +} // namespace mediums +} // namespace connections +} // namespace nearby diff --git a/proto/mediums/BUILD b/proto/mediums/BUILD index eb05f144..125e8cf4 100644 --- a/proto/mediums/BUILD +++ b/proto/mediums/BUILD @@ -26,6 +26,12 @@ proto_library( ], ) +cc_proto_library( + name = "multiplex_frames_cc_proto", + visibility = ["//:__subpackages__"], + deps = [":multiplex_frames_proto"], +) + proto_library( name = "nfc_frames_proto", srcs = [