Release based on cl/313536507.

Signed-off-by: Alexey Polyudov <apolyudov@google.com>
Change-Id: I83ec7dee1a7ef6f4bdfd47482c94d03910525b7e
This commit is contained in:
Alexey Polyudov
2020-06-04 10:06:31 -07:00
parent c3a89bb894
commit 7e19ffbab7
321 changed files with 23019 additions and 1376 deletions
+91
View File
@@ -0,0 +1,91 @@
# 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.
cc_library(
name = "webrtc",
hdrs = [
"webrtc_socket.cc",
"webrtc_socket.h",
],
deps = [
"//platform:utils",
"//platform/api",
"//webrtc/api:libjingle_peerconnection_api",
],
)
cc_test(
name = "webrtc_test",
srcs = ["webrtc_socket_test.cc"],
deps = [
":webrtc",
"//platform:types",
"//platform/api",
"//platform/impl/g3", # buildcleaner: keep
"//testing/base/public:gunit_main",
"//webrtc/api:libjingle_peerconnection_api",
],
)
cc_library(
name = "peer_id",
srcs = ["peer_id.cc"],
hdrs = ["peer_id.h"],
deps = [
"//core/internal/mediums:utils",
"//platform:types",
"//platform/api",
"//platform/port:string",
"//absl/strings",
],
)
cc_library(
name = "signaling_frames",
srcs = ["signaling_frames.cc"],
hdrs = ["signaling_frames.h"],
deps = [
":peer_id",
"//platform:types",
"//location/nearby/mediums/proto:web_rtc_signaling_frames_cc_proto",
"//webrtc/api:libjingle_peerconnection_api",
],
)
cc_test(
name = "peer_id_test",
srcs = ["peer_id_test.cc"],
deps = [
":peer_id",
"//platform:types",
"//platform/api",
"//platform/impl/g3", # buildcleaner: keep
"//testing/base/public:gunit_main",
"//absl/strings",
],
)
cc_test(
name = "signaling_frames_test",
srcs = ["signaling_frames_test.cc"],
deps = [
":peer_id",
":signaling_frames",
"//platform:types",
"//platform/impl/g3", # buildcleaner: keep
"//net/proto2/public:proto2",
"//testing/base/public:gunit_main",
"//webrtc/pc:peerconnection", # buildcleaner: keep
],
)
@@ -0,0 +1,55 @@
// 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 "core/internal/mediums/webrtc/peer_id.h"
#include <sstream>
#include "core/internal/mediums/utils.h"
#include "absl/strings/ascii.h"
#include "absl/strings/escaping.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
namespace {
constexpr int kPeerIdLength = 64;
std::string BytesToStringUppercase(ConstPtr<ByteArray> bytes) {
std::string hex_string(
absl::BytesToHexString(std::string(bytes->getData(), bytes->size())));
absl::AsciiStrToUpper(&hex_string);
return hex_string;
}
} // namespace
ConstPtr<PeerId> PeerId::FromRandom(Ptr<HashUtils> hash_utils) {
return FromSeed(Utils::generateRandomBytes(kPeerIdLength), hash_utils);
}
ConstPtr<PeerId> PeerId::FromSeed(ConstPtr<ByteArray> seed,
Ptr<HashUtils> hash_utils) {
ScopedPtr<ConstPtr<ByteArray>> full_hash(
Utils::sha256Hash(hash_utils, seed, kPeerIdLength));
ScopedPtr<ConstPtr<ByteArray>> hashedSeed(
MakeConstPtr(new ByteArray(full_hash->getData(), kPeerIdLength / 2)));
return MakeConstPtr(new PeerId(BytesToStringUppercase(hashedSeed.get())));
}
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
@@ -0,0 +1,50 @@
// 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.
#ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_PEER_ID_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_PEER_ID_H_
#include "platform/api/hash_utils.h"
#include "platform/byte_array.h"
#include "platform/port/string.h"
#include "platform/ptr.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
// PeerId is used as an identifier to exchange SDP messages to establish WebRTC
// p2p connection.
class PeerId {
public:
explicit PeerId(const string& id) : id_(id) {}
~PeerId() = default;
static ConstPtr<PeerId> FromRandom(Ptr<HashUtils> hash_utils);
static ConstPtr<PeerId> FromSeed(ConstPtr<ByteArray> seed,
Ptr<HashUtils> hash_utils);
const string& GetId() const { return id_; }
private:
const string id_;
};
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_PEER_ID_H_
@@ -0,0 +1,90 @@
// 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 "core/internal/mediums/webrtc/peer_id.h"
#include "platform/api/hash_utils.h"
#include "platform/byte_array.h"
#include "platform/ptr.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "absl/strings/escaping.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
namespace {
class MockHashUtils : public HashUtils {
public:
MOCK_METHOD(ConstPtr<ByteArray>, md5, (const std::string& input), (override));
MOCK_METHOD(ConstPtr<ByteArray>, sha256, (const std::string& input),
(override));
};
} // namespace
TEST(PeerIdTest, GenerateRandomPeerId) {
// These are actual SHA-256 values for |seed| = "seed".
std::string hashed_output =
"19b25856e1c150ca834cffc8b59b23adbd0ec0389e58eb22b3b64768098d002b";
std::string expected_peer_id =
"19B25856E1C150CA834CFFC8B59B23ADBD0EC0389E58EB22B3B64768098D002B";
Ptr<testing::NiceMock<MockHashUtils>> mock_hash_utils(
MakePtr(new MockHashUtils()));
ON_CALL(*mock_hash_utils.get(), sha256(testing::_))
.WillByDefault(testing::Return(
MakeConstPtr(new ByteArray(absl::HexStringToBytes(hashed_output)))));
EXPECT_CALL(*mock_hash_utils.get(), sha256(testing::_));
ConstPtr<PeerId> peer_id = PeerId::FromRandom(mock_hash_utils);
ASSERT_EQ(64, peer_id->GetId().size());
ASSERT_EQ(expected_peer_id, peer_id->GetId());
}
TEST(PeerIdTest, GenerateFromSeed) {
// Values calculated by running actual SHA-256 hash on |seed|.
std::string seed = "sesdfed";
std::string hashed_output =
"19b25856e1c150ca834cffc8b59b23adbd0ec0389e58eb22b3b64768098d002b";
std::string expected_peer_id =
"19B25856E1C150CA834CFFC8B59B23ADBD0EC0389E58EB22B3B64768098D002B";
Ptr<testing::NiceMock<MockHashUtils>> mock_hash_utils(
MakePtr(new MockHashUtils()));
ON_CALL(*mock_hash_utils.get(), sha256(testing::Eq(seed)))
.WillByDefault(testing::Return(
MakeConstPtr(new ByteArray(absl::HexStringToBytes(hashed_output)))));
EXPECT_CALL(*mock_hash_utils.get(), sha256(testing::Eq(seed)));
ConstPtr<PeerId> peer_id =
PeerId::FromSeed(MakeConstPtr(new ByteArray(seed)), mock_hash_utils);
ASSERT_EQ(64, peer_id->GetId().size());
ASSERT_EQ(expected_peer_id, peer_id->GetId());
}
TEST(PeerIdTest, GetId) {
const std::string id = "this_is_a_test";
PeerId peer_id(id);
ASSERT_EQ(id, peer_id.GetId());
}
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
@@ -0,0 +1,139 @@
// 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 "core/internal/mediums/webrtc/signaling_frames.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
namespace webrtc_frames {
using WebRtcSignalingFrame = location::nearby::mediums::WebRtcSignalingFrame;
namespace {
ConstPtr<ByteArray> FrameToByteArray(
const WebRtcSignalingFrame& signaling_frame) {
std::string message;
signaling_frame.SerializeToString(&message);
return MakeConstPtr(new ByteArray(message.c_str(), message.size()));
}
void SetSenderId(ConstPtr<PeerId> sender_id, WebRtcSignalingFrame& frame) {
frame.mutable_sender_id()->set_id(sender_id->GetId());
}
ConstPtr<webrtc::IceCandidateInterface> DecodeIceCandidate(
const location::nearby::mediums::IceCandidate& ice_candidate_proto) {
webrtc::SdpParseError error;
return ConstPtr<webrtc::IceCandidateInterface>(webrtc::CreateIceCandidate(
ice_candidate_proto.sdp_mid(), ice_candidate_proto.sdp_m_line_index(),
ice_candidate_proto.sdp(), &error));
}
} // namespace
ConstPtr<ByteArray> EncodeReadyForSignalingPoke(ConstPtr<PeerId> sender_id) {
WebRtcSignalingFrame signaling_frame;
signaling_frame.set_type(WebRtcSignalingFrame::READY_FOR_SIGNALING_POKE_TYPE);
SetSenderId(sender_id, signaling_frame);
signaling_frame.mutable_ready_for_signaling_poke();
return FrameToByteArray(std::move(signaling_frame));
}
ConstPtr<ByteArray> EncodeOffer(
ConstPtr<PeerId> sender_id,
const webrtc::SessionDescriptionInterface& offer) {
WebRtcSignalingFrame signaling_frame;
signaling_frame.set_type(WebRtcSignalingFrame::OFFER_TYPE);
SetSenderId(sender_id, signaling_frame);
std::string offer_str;
offer.ToString(&offer_str);
signaling_frame.mutable_offer()
->mutable_session_description()
->set_description(offer_str);
return FrameToByteArray(std::move(signaling_frame));
}
ConstPtr<ByteArray> EncodeAnswer(
ConstPtr<PeerId> sender_id,
const webrtc::SessionDescriptionInterface& answer) {
WebRtcSignalingFrame signaling_frame;
signaling_frame.set_type(WebRtcSignalingFrame::ANSWER_TYPE);
SetSenderId(sender_id, signaling_frame);
std::string answer_str;
answer.ToString(&answer_str);
signaling_frame.mutable_answer()
->mutable_session_description()
->set_description(answer_str);
return FrameToByteArray(std::move(signaling_frame));
}
ConstPtr<ByteArray> EncodeIceCandidates(
ConstPtr<PeerId> sender_id,
const std::vector<location::nearby::mediums::IceCandidate>&
ice_candidates) {
WebRtcSignalingFrame signaling_frame;
signaling_frame.set_type(WebRtcSignalingFrame::ICE_CANDIDATES_TYPE);
SetSenderId(sender_id, signaling_frame);
for (const auto& ice_candidate : ice_candidates) {
*signaling_frame.mutable_ice_candidates()->add_ice_candidates() =
ice_candidate;
}
return FrameToByteArray(std::move(signaling_frame));
}
Ptr<webrtc::SessionDescriptionInterface> DecodeOffer(
const WebRtcSignalingFrame& frame) {
return MakePtr(webrtc::CreateSessionDescription(
webrtc::SdpType::kOffer,
frame.offer().session_description().description())
.release());
}
Ptr<webrtc::SessionDescriptionInterface> DecodeAnswer(
const WebRtcSignalingFrame& frame) {
return MakePtr(webrtc::CreateSessionDescription(
webrtc::SdpType::kAnswer,
frame.answer().session_description().description())
.release());
}
std::vector<ConstPtr<webrtc::IceCandidateInterface>> DecodeIceCandidates(
const WebRtcSignalingFrame& frame) {
std::vector<ConstPtr<webrtc::IceCandidateInterface>> ice_candidates;
for (const auto& candidate : frame.ice_candidates().ice_candidates()) {
ice_candidates.push_back(DecodeIceCandidate(candidate));
}
return ice_candidates;
}
location::nearby::mediums::IceCandidate EncodeIceCandidate(
const webrtc::IceCandidateInterface& ice_candidate) {
std::string sdp;
ice_candidate.ToString(&sdp);
location::nearby::mediums::IceCandidate ice_candidate_proto;
ice_candidate_proto.set_sdp(sdp);
ice_candidate_proto.set_sdp_mid(ice_candidate.sdp_mid());
ice_candidate_proto.set_sdp_m_line_index(ice_candidate.sdp_mline_index());
return ice_candidate_proto;
}
} // namespace webrtc_frames
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
@@ -0,0 +1,63 @@
// 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.
#ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_SIGNALING_FRAMES_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_SIGNALING_FRAMES_H_
#include <vector>
#include "core/internal/mediums/webrtc/peer_id.h"
#include "platform/byte_array.h"
#include "platform/ptr.h"
#include "location/nearby/mediums/proto/web_rtc_signaling_frames.pb.h"
#include "webrtc/api/peer_connection_interface.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
namespace webrtc_frames {
ConstPtr<ByteArray> EncodeReadyForSignalingPoke(ConstPtr<PeerId> sender_id);
ConstPtr<ByteArray> EncodeOffer(
ConstPtr<PeerId> sender_id,
const webrtc::SessionDescriptionInterface& offer);
ConstPtr<ByteArray> EncodeAnswer(
ConstPtr<PeerId> sender_id,
const webrtc::SessionDescriptionInterface& answer);
ConstPtr<ByteArray> EncodeIceCandidates(
ConstPtr<PeerId> sender_id,
const std::vector<location::nearby::mediums::IceCandidate>& ice_candidates);
location::nearby::mediums::IceCandidate EncodeIceCandidate(
const webrtc::IceCandidateInterface& ice_candidate);
Ptr<webrtc::SessionDescriptionInterface> DecodeOffer(
const location::nearby::mediums::WebRtcSignalingFrame& frame);
Ptr<webrtc::SessionDescriptionInterface> DecodeAnswer(
const location::nearby::mediums::WebRtcSignalingFrame& frame);
std::vector<ConstPtr<webrtc::IceCandidateInterface>> DecodeIceCandidates(
const location::nearby::mediums::WebRtcSignalingFrame& frame);
} // namespace webrtc_frames
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_SIGNALING_FRAMES_H_
@@ -0,0 +1,198 @@
// 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 "core/internal/mediums/webrtc/signaling_frames.h"
#include <memory>
#include "core/internal/mediums/webrtc/peer_id.h"
#include "platform/ptr.h"
#include "net/proto2/public/text_format.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
namespace webrtc_frames {
namespace {
const char kSampleSdp[] =
"v=0\r\no=- 7859371131 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 "
"0\r\na=msid-semantic: WMS\r\n";
const char kIceCandidateSdp1[] =
"a=candidate:1 1 UDP 2130706431 10.0.1.1 8998 typ host";
const char kIceCandidateSdp2[] =
"a=candidate:2 1 UDP 1694498815 192.0.2.3 45664 typ srflx raddr";
const char kIceSdpMid[] = "data";
const int kIceSdpMLineIndex = 0;
const char kOfferProto[] = R"(
sender_id { id: "abc" }
type: OFFER_TYPE
offer {
session_description {
description: "v=0\r\no=- 7859371131 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=msid-semantic: WMS\r\n"
}
}
)";
const char kAnswerProto[] = R"(
sender_id { id: "abc" }
type: ANSWER_TYPE
answer {
session_description {
description: "v=0\r\no=- 7859371131 2 IN IP4 127.0.0.1\r\ns=-\r\nt=0 0\r\na=msid-semantic: WMS\r\n"
}
}
)";
const char kIceCandidatesProto[] = R"(
sender_id { id: "abc" }
type: ICE_CANDIDATES_TYPE
ice_candidates {
ice_candidates {
sdp: "candidate:1 1 udp 2130706431 10.0.1.1 8998 typ host generation 0"
sdp_mid: "data"
sdp_m_line_index: 0
}
ice_candidates {
sdp: "candidate:2 1 udp 1694498815 192.0.2.3 45664 typ srflx generation 0"
sdp_mid: "data"
sdp_m_line_index: 0
}
}
)";
} // namespace
TEST(SignalingFramesTest, SignalingPoke) {
ConstPtr<PeerId> sender_id(new PeerId("abc"));
ConstPtr<ByteArray> encoded_poke = EncodeReadyForSignalingPoke(sender_id);
location::nearby::mediums::WebRtcSignalingFrame frame;
frame.ParseFromString(
std::string(encoded_poke->getData(), encoded_poke->size()));
EXPECT_THAT(frame, testing::EqualsProto(R"(
sender_id { id: "abc" }
type: READY_FOR_SIGNALING_POKE_TYPE
ready_for_signaling_poke {}
)"));
}
TEST(SignalingFramesTest, EncodeValidOffer) {
ConstPtr<PeerId> sender_id(new PeerId("abc"));
std::unique_ptr<webrtc::SessionDescriptionInterface> offer =
webrtc::CreateSessionDescription(webrtc::SdpType::kOffer, kSampleSdp);
ConstPtr<ByteArray> encoded_offer = EncodeOffer(sender_id, *offer);
location::nearby::mediums::WebRtcSignalingFrame frame;
frame.ParseFromString(
std::string(encoded_offer->getData(), encoded_offer->size()));
EXPECT_THAT(frame, testing::EqualsProto(kOfferProto));
}
TEST(SignalingFramesTest, DecodeValidOffer) {
location::nearby::mediums::WebRtcSignalingFrame frame;
proto2::TextFormat::ParseFromString(kOfferProto, &frame);
Ptr<webrtc::SessionDescriptionInterface> decoded_offer = DecodeOffer(frame);
EXPECT_EQ(webrtc::SdpType::kOffer, decoded_offer->GetType());
std::string description;
decoded_offer->ToString(&description);
EXPECT_EQ(kSampleSdp, description);
}
TEST(SignalingFramesTest, EncodeValidAnswer) {
ConstPtr<PeerId> sender_id(new PeerId("abc"));
std::unique_ptr<webrtc::SessionDescriptionInterface> answer =
webrtc::CreateSessionDescription(webrtc::SdpType::kAnswer, kSampleSdp);
ConstPtr<ByteArray> encoded_answer = EncodeAnswer(sender_id, *answer);
location::nearby::mediums::WebRtcSignalingFrame frame;
frame.ParseFromString(
std::string(encoded_answer->getData(), encoded_answer->size()));
EXPECT_THAT(frame, testing::EqualsProto(kAnswerProto));
}
TEST(SignalingFramesTest, DecodeValidAnswer) {
location::nearby::mediums::WebRtcSignalingFrame frame;
proto2::TextFormat::ParseFromString(kAnswerProto, &frame);
Ptr<webrtc::SessionDescriptionInterface> decoded_answer = DecodeAnswer(frame);
EXPECT_EQ(webrtc::SdpType::kAnswer, decoded_answer->GetType());
std::string description;
decoded_answer->ToString(&description);
EXPECT_EQ(kSampleSdp, description);
}
TEST(SignalingFramesTest, EncodeValidIceCandidates) {
ConstPtr<PeerId> sender_id(new PeerId("abc"));
webrtc::SdpParseError error;
std::vector<ConstPtr<webrtc::IceCandidateInterface>> ice_candidates;
ice_candidates.emplace_back(webrtc::CreateIceCandidate(
kIceSdpMid, kIceSdpMLineIndex, kIceCandidateSdp1, &error));
ice_candidates.emplace_back(webrtc::CreateIceCandidate(
kIceSdpMid, kIceSdpMLineIndex, kIceCandidateSdp2, &error));
std::vector<location::nearby::mediums::IceCandidate> encoded_candidates_vec;
for (const auto& ice_candidate : ice_candidates) {
encoded_candidates_vec.push_back(EncodeIceCandidate(*ice_candidate.get()));
}
ConstPtr<ByteArray> encoded_candidates =
EncodeIceCandidates(sender_id, encoded_candidates_vec);
location::nearby::mediums::WebRtcSignalingFrame frame;
frame.ParseFromString(
std::string(encoded_candidates->getData(), encoded_candidates->size()));
EXPECT_THAT(frame, testing::EqualsProto(kIceCandidatesProto));
}
TEST(SignalingFramesTest, DecodeValidIceCandidates) {
webrtc::SdpParseError error;
std::vector<ConstPtr<webrtc::IceCandidateInterface>> ice_candidates;
ice_candidates.emplace_back(webrtc::CreateIceCandidate(
kIceSdpMid, kIceSdpMLineIndex, kIceCandidateSdp1, &error));
ice_candidates.emplace_back(webrtc::CreateIceCandidate(
kIceSdpMid, kIceSdpMLineIndex, kIceCandidateSdp2, &error));
std::vector<location::nearby::mediums::IceCandidate> encoded_candidates_vec;
location::nearby::mediums::WebRtcSignalingFrame frame;
proto2::TextFormat::ParseFromString(kIceCandidatesProto, &frame);
std::vector<ConstPtr<webrtc::IceCandidateInterface>> decoded_candidates =
DecodeIceCandidates(frame);
ASSERT_EQ(2u, decoded_candidates.size());
for (int i = 0; i < static_cast<int>(decoded_candidates.size()); i++) {
EXPECT_TRUE(ice_candidates[i]->candidate().IsEquivalent(
decoded_candidates[i]->candidate()));
EXPECT_EQ(ice_candidates[i]->sdp_mid(), decoded_candidates[i]->sdp_mid());
EXPECT_EQ(ice_candidates[i]->sdp_mline_index(),
decoded_candidates[i]->sdp_mline_index());
}
}
} // namespace webrtc_frames
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
@@ -0,0 +1,153 @@
// 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 "core/internal/mediums/webrtc/webrtc_socket.h"
#include "platform/synchronized.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
// OutputStreamImpl
template <typename Platform>
Exception::Value WebRtcSocket<Platform>::OutputStreamImpl::write(
ConstPtr<ByteArray> data) {
ScopedPtr<ConstPtr<ByteArray>> scoped_data(data);
if (scoped_data->size() > kMaxDataSize) {
NEARBY_LOG(WARNING, "Sending data larger than 1MB");
return Exception::IO;
}
socket_->BlockUntilSufficientSpaceInBuffer(scoped_data->size());
if (socket_->IsClosed()) {
NEARBY_LOG(WARNING, "Tried sending message while socket is closed");
return Exception::IO;
}
if (!socket_->SendMessage(scoped_data.release())) {
return Exception::IO;
}
return Exception::NONE;
}
template <typename Platform>
Exception::Value WebRtcSocket<Platform>::OutputStreamImpl::flush() {
// Java implementation is empty.
return Exception::NONE;
}
template <typename Platform>
Exception::Value WebRtcSocket<Platform>::OutputStreamImpl::close() {
socket_->close();
return Exception::NONE;
}
// WebRtcSocket
template <typename Platform>
WebRtcSocket<Platform>::WebRtcSocket(
const string& name,
rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel)
: name_(name),
data_channel_(std::move(data_channel)),
pipe_(MakeRefCountedPtr(new Pipe())),
incoming_data_piped_input_stream_(Pipe::createInputStream(pipe_)),
incoming_data_piped_output_stream_(Pipe::createOutputStream(pipe_)),
output_stream_(MakePtr(new OutputStreamImpl(this))),
closed_(Platform::createAtomicBoolean(false)),
backpressure_lock_(Platform::createLock()),
buffer_variable_(
Platform::createConditionVariable(backpressure_lock_.get())) {}
template <typename Platform>
Ptr<InputStream> WebRtcSocket<Platform>::getInputStream() {
return incoming_data_piped_input_stream_.get();
}
template <typename Platform>
Ptr<OutputStream> WebRtcSocket<Platform>::getOutputStream() {
return output_stream_.get();
}
template <typename Platform>
void WebRtcSocket<Platform>::close() {
if (IsClosed()) return;
closed_->set(true);
incoming_data_piped_output_stream_->close();
incoming_data_piped_input_stream_->close();
data_channel_->Close();
WakeUpWriter();
if (!socket_closed_listener_.isNull()) {
socket_closed_listener_->OnSocketClosed();
}
}
template <typename Platform>
void WebRtcSocket<Platform>::NotifyDataChannelMsgReceived(
ConstPtr<ByteArray> message) {
Exception::Value exception =
incoming_data_piped_output_stream_->write(message);
if (exception != Exception::NONE) close();
exception = incoming_data_piped_output_stream_->flush();
if (exception != Exception::NONE) close();
}
template <typename Platform>
void WebRtcSocket<Platform>::NotifyDataChannelBufferedAmountChanged() {
WakeUpWriter();
}
template <typename Platform>
bool WebRtcSocket<Platform>::SendMessage(ConstPtr<ByteArray> data) {
ScopedPtr<ConstPtr<ByteArray>> scoped_data(data);
return data_channel_->Send(webrtc::DataBuffer(
std::string(scoped_data->getData(), scoped_data->size())));
}
template <typename Platform>
bool WebRtcSocket<Platform>::IsClosed() {
return closed_->get();
}
template <typename Platform>
void WebRtcSocket<Platform>::WakeUpWriter() {
Synchronized s(backpressure_lock_.get());
buffer_variable_->notify();
}
template <typename Platform>
void WebRtcSocket<Platform>::SetOnSocketClosedListener(
Ptr<SocketClosedListener> listener) {
socket_closed_listener_ = listener;
}
template <typename Platform>
void WebRtcSocket<Platform>::BlockUntilSufficientSpaceInBuffer(int length) {
Synchronized s(backpressure_lock_.get());
while (!IsClosed() &&
(data_channel_->buffered_amount() + length > kMaxDataSize)) {
// TODO(himanshujaju): Add wait with timeout.
buffer_variable_->wait();
}
}
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
@@ -0,0 +1,118 @@
// 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.
#ifndef CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_SOCKET_H_
#define CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_SOCKET_H_
#include "platform/api/atomic_boolean.h"
#include "platform/api/input_stream.h"
#include "platform/api/output_stream.h"
#include "platform/api/socket.h"
#include "platform/pipe.h"
#include "webrtc/api/data_channel_interface.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
// Maximum data size: 1 MB
constexpr int kMaxDataSize = 1 * 1024 * 1024;
// Defines the Socket implementation specific to WebRTC, which uses the WebRTC
// data channel to send and receive messages.
//
// Messages are buffered here to prevent the data channel from overflowing,
// which could lead to data loss.
template <typename Platform>
class WebRtcSocket : public Socket {
public:
WebRtcSocket(const string& name,
rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel);
~WebRtcSocket() override = default;
WebRtcSocket(const WebRtcSocket& other) = delete;
WebRtcSocket& operator=(const WebRtcSocket& other) = delete;
// Overrides for location::nearby::Socket:
Ptr<InputStream> getInputStream() override;
Ptr<OutputStream> getOutputStream() override;
void close() override;
// Callback from WebRTC data channel when new message has been received from
// the remote.
void NotifyDataChannelMsgReceived(ConstPtr<ByteArray> message);
// Callback from WebRTC data channel that the buffered data amount has
// changed.
void NotifyDataChannelBufferedAmountChanged();
// Listener class the gets called when the socket is closed.
class SocketClosedListener {
public:
virtual ~SocketClosedListener() = default;
virtual void OnSocketClosed() = 0;
};
void SetOnSocketClosedListener(Ptr<SocketClosedListener> listener);
private:
class OutputStreamImpl : public OutputStream {
public:
explicit OutputStreamImpl(WebRtcSocket<Platform>* const socket)
: socket_(socket) {}
~OutputStreamImpl() override = default;
OutputStreamImpl(const OutputStreamImpl& other) = delete;
OutputStreamImpl& operator=(const OutputStreamImpl& other) = delete;
// OutputStream:
Exception::Value write(ConstPtr<ByteArray> data) override;
Exception::Value flush() override;
Exception::Value close() override;
private:
// |this| OutputStreamImpl is owned by |socket_|.
WebRtcSocket<Platform>* const socket_;
};
void WakeUpWriter();
bool IsClosed();
bool SendMessage(ConstPtr<ByteArray> data);
void BlockUntilSufficientSpaceInBuffer(int length);
string name_;
rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel_;
Ptr<Pipe> pipe_;
ScopedPtr<Ptr<InputStream>> incoming_data_piped_input_stream_;
ScopedPtr<Ptr<OutputStream>> incoming_data_piped_output_stream_;
ScopedPtr<Ptr<OutputStream>> output_stream_;
ScopedPtr<Ptr<AtomicBoolean>> closed_;
Ptr<SocketClosedListener> socket_closed_listener_;
ScopedPtr<Ptr<Lock>> backpressure_lock_;
ScopedPtr<Ptr<ConditionVariable>> buffer_variable_;
};
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
#include "core/internal/mediums/webrtc/webrtc_socket.cc"
#endif // CORE_INTERNAL_MEDIUMS_WEBRTC_WEBRTC_SOCKET_H_
@@ -0,0 +1,169 @@
// 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 "core/internal/mediums/webrtc/webrtc_socket.h"
#include "platform/api/platform.h"
#include "platform/byte_array.h"
#include "platform/ptr.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "webrtc/api/data_channel_interface.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
namespace {
using TestPlatform = platform::ImplementationPlatform;
const char kSocketName[] = "TestSocket";
class MockDataChannel
: public rtc::RefCountedObject<webrtc::DataChannelInterface> {
public:
MOCK_METHOD(void, RegisterObserver, (webrtc::DataChannelObserver*));
MOCK_METHOD(void, UnregisterObserver, ());
MOCK_METHOD(std::string, label, (), (const));
MOCK_METHOD(bool, reliable, (), (const));
MOCK_METHOD(int, id, (), (const));
MOCK_METHOD(DataState, state, (), (const));
MOCK_METHOD(uint32_t, messages_sent, (), (const));
MOCK_METHOD(uint64_t, bytes_sent, (), (const));
MOCK_METHOD(uint32_t, messages_received, (), (const));
MOCK_METHOD(uint64_t, bytes_received, (), (const));
MOCK_METHOD(uint64_t, buffered_amount, (), (const));
MOCK_METHOD(void, Close, ());
MOCK_METHOD(bool, Send, (const webrtc::DataBuffer&));
};
} // namespace
class MockSocketClosedListener
: public WebRtcSocket<TestPlatform>::SocketClosedListener {
public:
MOCK_METHOD(void, OnSocketClosed, ());
};
TEST(WebRtcSocketTest, ReadFromSocket) {
ConstPtr<ByteArray> kMessage = MakeConstPtr(new ByteArray("Message"));
rtc::scoped_refptr<MockDataChannel> mock_data_channel = new MockDataChannel();
WebRtcSocket<TestPlatform> webrtc_socket(kSocketName, mock_data_channel);
webrtc_socket.NotifyDataChannelMsgReceived(kMessage);
ExceptionOr<ConstPtr<ByteArray>> result =
webrtc_socket.getInputStream()->read();
EXPECT_TRUE(result.ok());
EXPECT_EQ(result.result(), kMessage);
}
TEST(WebRtcSocketTest, ReadMultipleMessages) {
rtc::scoped_refptr<MockDataChannel> mock_data_channel = new MockDataChannel();
WebRtcSocket<TestPlatform> webrtc_socket(kSocketName, mock_data_channel);
webrtc_socket.NotifyDataChannelMsgReceived(MakeConstPtr(new ByteArray("Me")));
webrtc_socket.NotifyDataChannelMsgReceived(
MakeConstPtr(new ByteArray("ssa")));
webrtc_socket.NotifyDataChannelMsgReceived(MakeConstPtr(new ByteArray("ge")));
ExceptionOr<ConstPtr<ByteArray>> result;
// This behaviour is different from the Java code
result = webrtc_socket.getInputStream()->read();
EXPECT_TRUE(result.ok());
EXPECT_EQ(result.result()->asString(), "Me");
result = webrtc_socket.getInputStream()->read();
EXPECT_TRUE(result.ok());
EXPECT_EQ(result.result()->asString(), "ssa");
result = webrtc_socket.getInputStream()->read();
EXPECT_TRUE(result.ok());
EXPECT_EQ(result.result()->asString(), "ge");
}
TEST(WebRtcSocketTest, WriteToSocket) {
ConstPtr<ByteArray> kMessage = MakeConstPtr(new ByteArray("Message"));
rtc::scoped_refptr<MockDataChannel> mock_data_channel = new MockDataChannel();
WebRtcSocket<TestPlatform> webrtc_socket(kSocketName, mock_data_channel);
EXPECT_CALL(*mock_data_channel, Send(testing::_))
.WillRepeatedly(testing::Return(true));
EXPECT_EQ(webrtc_socket.getOutputStream()->write(kMessage), Exception::NONE);
}
TEST(WebRtcSocketTest, SendDataBiggerThanMax) {
ConstPtr<ByteArray> kMessage = MakeConstPtr(new ByteArray(kMaxDataSize + 1));
rtc::scoped_refptr<MockDataChannel> mock_data_channel = new MockDataChannel();
WebRtcSocket<TestPlatform> webrtc_socket(kSocketName, mock_data_channel);
EXPECT_CALL(*mock_data_channel, Send(testing::_)).Times(0);
EXPECT_EQ(webrtc_socket.getOutputStream()->write(kMessage), Exception::IO);
}
TEST(WebRtcSocketTest, WriteToDataChannelFails) {
ConstPtr<ByteArray> kMessage = MakeConstPtr(new ByteArray("Message"));
rtc::scoped_refptr<MockDataChannel> mock_data_channel = new MockDataChannel();
WebRtcSocket<TestPlatform> webrtc_socket(kSocketName, mock_data_channel);
ON_CALL(*mock_data_channel, Send(testing::_))
.WillByDefault(testing::Return(false));
EXPECT_EQ(webrtc_socket.getOutputStream()->write(kMessage), Exception::IO);
}
TEST(WebRtcSocketTest, Close) {
rtc::scoped_refptr<MockDataChannel> mock_data_channel = new MockDataChannel();
ScopedPtr<Ptr<MockSocketClosedListener>> mock_listener(
MakePtr(new MockSocketClosedListener()));
WebRtcSocket<TestPlatform> webrtc_socket(kSocketName, mock_data_channel);
webrtc_socket.SetOnSocketClosedListener(mock_listener.get());
EXPECT_CALL(*mock_listener, OnSocketClosed());
EXPECT_CALL(*mock_data_channel, Close());
webrtc_socket.close();
}
TEST(WebRtcSocketTest, WriteOnClosedChannel) {
ConstPtr<ByteArray> kMessage = MakeConstPtr(new ByteArray("Message"));
rtc::scoped_refptr<MockDataChannel> mock_data_channel = new MockDataChannel();
WebRtcSocket<TestPlatform> webrtc_socket(kSocketName, mock_data_channel);
webrtc_socket.close();
EXPECT_CALL(*mock_data_channel, Send(testing::_)).Times(0);
EXPECT_EQ(webrtc_socket.getOutputStream()->write(kMessage), Exception::IO);
}
TEST(WebRtcSocketTest, ReadFromClosedChannel) {
ConstPtr<ByteArray> kMessage = MakeConstPtr(new ByteArray("Message"));
rtc::scoped_refptr<MockDataChannel> mock_data_channel = new MockDataChannel();
WebRtcSocket<TestPlatform> webrtc_socket(kSocketName, mock_data_channel);
ON_CALL(*mock_data_channel, Send(testing::_))
.WillByDefault(testing::Return(true));
webrtc_socket.getOutputStream()->write(kMessage);
webrtc_socket.close();
EXPECT_EQ(webrtc_socket.getInputStream()->read().exception(), Exception::IO);
}
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location