1 /*
2 * Copyright 2018 The WebRTC Project Authors. All rights reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11 #include "pc/jsep_transport_controller.h"
12
13 #include <map>
14 #include <memory>
15
16 #include "p2p/base/dtls_transport_factory.h"
17 #include "p2p/base/fake_dtls_transport.h"
18 #include "p2p/base/fake_ice_transport.h"
19 #include "p2p/base/transport_info.h"
20 #include "rtc_base/gunit.h"
21 #include "rtc_base/thread.h"
22 #include "test/gtest.h"
23
24 using cricket::Candidate;
25 using cricket::Candidates;
26 using cricket::FakeDtlsTransport;
27 using webrtc::SdpType;
28
29 static const int kTimeout = 100;
30 static const char kIceUfrag1[] = "u0001";
31 static const char kIcePwd1[] = "TESTICEPWD00000000000001";
32 static const char kIceUfrag2[] = "u0002";
33 static const char kIcePwd2[] = "TESTICEPWD00000000000002";
34 static const char kIceUfrag3[] = "u0003";
35 static const char kIcePwd3[] = "TESTICEPWD00000000000003";
36 static const char kAudioMid1[] = "audio1";
37 static const char kAudioMid2[] = "audio2";
38 static const char kVideoMid1[] = "video1";
39 static const char kVideoMid2[] = "video2";
40 static const char kDataMid1[] = "data1";
41
42 namespace webrtc {
43
44 class FakeIceTransportFactory : public webrtc::IceTransportFactory {
45 public:
46 ~FakeIceTransportFactory() override = default;
CreateIceTransport(const std::string & transport_name,int component,IceTransportInit init)47 rtc::scoped_refptr<IceTransportInterface> CreateIceTransport(
48 const std::string& transport_name,
49 int component,
50 IceTransportInit init) override {
51 return new rtc::RefCountedObject<cricket::FakeIceTransportWrapper>(
52 std::make_unique<cricket::FakeIceTransport>(transport_name, component));
53 }
54 };
55
56 class FakeDtlsTransportFactory : public cricket::DtlsTransportFactory {
57 public:
CreateDtlsTransport(cricket::IceTransportInternal * ice,const webrtc::CryptoOptions & crypto_options,rtc::SSLProtocolVersion max_version)58 std::unique_ptr<cricket::DtlsTransportInternal> CreateDtlsTransport(
59 cricket::IceTransportInternal* ice,
60 const webrtc::CryptoOptions& crypto_options,
61 rtc::SSLProtocolVersion max_version) override {
62 return std::make_unique<FakeDtlsTransport>(
63 static_cast<cricket::FakeIceTransport*>(ice));
64 }
65 };
66
67 class JsepTransportControllerTest : public JsepTransportController::Observer,
68 public ::testing::Test,
69 public sigslot::has_slots<> {
70 public:
JsepTransportControllerTest()71 JsepTransportControllerTest() : signaling_thread_(rtc::Thread::Current()) {
72 fake_ice_transport_factory_ = std::make_unique<FakeIceTransportFactory>();
73 fake_dtls_transport_factory_ = std::make_unique<FakeDtlsTransportFactory>();
74 }
75
CreateJsepTransportController(JsepTransportController::Config config,rtc::Thread * network_thread=rtc::Thread::Current (),cricket::PortAllocator * port_allocator=nullptr)76 void CreateJsepTransportController(
77 JsepTransportController::Config config,
78 rtc::Thread* network_thread = rtc::Thread::Current(),
79 cricket::PortAllocator* port_allocator = nullptr) {
80 config.transport_observer = this;
81 config.rtcp_handler = [](const rtc::CopyOnWriteBuffer& packet,
82 int64_t packet_time_us) { RTC_NOTREACHED(); };
83 config.ice_transport_factory = fake_ice_transport_factory_.get();
84 config.dtls_transport_factory = fake_dtls_transport_factory_.get();
85 config.on_dtls_handshake_error_ = [](rtc::SSLHandshakeError s) {};
86 transport_controller_ = std::make_unique<JsepTransportController>(
87 network_thread, port_allocator, nullptr /* async_resolver_factory */,
88 config);
89 network_thread->Invoke<void>(RTC_FROM_HERE,
90 [&] { ConnectTransportControllerSignals(); });
91 }
92
ConnectTransportControllerSignals()93 void ConnectTransportControllerSignals() {
94 transport_controller_->SubscribeIceConnectionState(
95 [this](cricket::IceConnectionState s) {
96 JsepTransportControllerTest::OnConnectionState(s);
97 });
98 transport_controller_->SubscribeConnectionState(
99 [this](PeerConnectionInterface::PeerConnectionState s) {
100 JsepTransportControllerTest::OnCombinedConnectionState(s);
101 });
102 transport_controller_->SubscribeStandardizedIceConnectionState(
103 [this](PeerConnectionInterface::IceConnectionState s) {
104 JsepTransportControllerTest::OnStandardizedIceConnectionState(s);
105 });
106 transport_controller_->SubscribeIceGatheringState(
107 [this](cricket::IceGatheringState s) {
108 JsepTransportControllerTest::OnGatheringState(s);
109 });
110 transport_controller_->SubscribeIceCandidateGathered(
111 [this](const std::string& transport,
112 const std::vector<cricket::Candidate>& candidates) {
113 JsepTransportControllerTest::OnCandidatesGathered(transport,
114 candidates);
115 });
116 }
117
118 std::unique_ptr<cricket::SessionDescription>
CreateSessionDescriptionWithoutBundle()119 CreateSessionDescriptionWithoutBundle() {
120 auto description = std::make_unique<cricket::SessionDescription>();
121 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
122 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
123 nullptr);
124 AddVideoSection(description.get(), kVideoMid1, kIceUfrag1, kIcePwd1,
125 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
126 nullptr);
127 return description;
128 }
129
130 std::unique_ptr<cricket::SessionDescription>
CreateSessionDescriptionWithBundleGroup()131 CreateSessionDescriptionWithBundleGroup() {
132 auto description = CreateSessionDescriptionWithoutBundle();
133 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
134 bundle_group.AddContentName(kAudioMid1);
135 bundle_group.AddContentName(kVideoMid1);
136 description->AddGroup(bundle_group);
137
138 return description;
139 }
140
141 std::unique_ptr<cricket::SessionDescription>
CreateSessionDescriptionWithBundledData()142 CreateSessionDescriptionWithBundledData() {
143 auto description = CreateSessionDescriptionWithoutBundle();
144 AddDataSection(description.get(), kDataMid1,
145 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
146 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
147 nullptr);
148 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
149 bundle_group.AddContentName(kAudioMid1);
150 bundle_group.AddContentName(kVideoMid1);
151 bundle_group.AddContentName(kDataMid1);
152 description->AddGroup(bundle_group);
153 return description;
154 }
155
AddAudioSection(cricket::SessionDescription * description,const std::string & mid,const std::string & ufrag,const std::string & pwd,cricket::IceMode ice_mode,cricket::ConnectionRole conn_role,rtc::scoped_refptr<rtc::RTCCertificate> cert)156 void AddAudioSection(cricket::SessionDescription* description,
157 const std::string& mid,
158 const std::string& ufrag,
159 const std::string& pwd,
160 cricket::IceMode ice_mode,
161 cricket::ConnectionRole conn_role,
162 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
163 std::unique_ptr<cricket::AudioContentDescription> audio(
164 new cricket::AudioContentDescription());
165 // Set RTCP-mux to be true because the default policy is "mux required".
166 audio->set_rtcp_mux(true);
167 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
168 /*rejected=*/false, std::move(audio));
169 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
170 }
171
AddVideoSection(cricket::SessionDescription * description,const std::string & mid,const std::string & ufrag,const std::string & pwd,cricket::IceMode ice_mode,cricket::ConnectionRole conn_role,rtc::scoped_refptr<rtc::RTCCertificate> cert)172 void AddVideoSection(cricket::SessionDescription* description,
173 const std::string& mid,
174 const std::string& ufrag,
175 const std::string& pwd,
176 cricket::IceMode ice_mode,
177 cricket::ConnectionRole conn_role,
178 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
179 std::unique_ptr<cricket::VideoContentDescription> video(
180 new cricket::VideoContentDescription());
181 // Set RTCP-mux to be true because the default policy is "mux required".
182 video->set_rtcp_mux(true);
183 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
184 /*rejected=*/false, std::move(video));
185 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
186 }
187
AddDataSection(cricket::SessionDescription * description,const std::string & mid,cricket::MediaProtocolType protocol_type,const std::string & ufrag,const std::string & pwd,cricket::IceMode ice_mode,cricket::ConnectionRole conn_role,rtc::scoped_refptr<rtc::RTCCertificate> cert)188 void AddDataSection(cricket::SessionDescription* description,
189 const std::string& mid,
190 cricket::MediaProtocolType protocol_type,
191 const std::string& ufrag,
192 const std::string& pwd,
193 cricket::IceMode ice_mode,
194 cricket::ConnectionRole conn_role,
195 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
196 RTC_CHECK(protocol_type == cricket::MediaProtocolType::kSctp);
197 std::unique_ptr<cricket::SctpDataContentDescription> data(
198 new cricket::SctpDataContentDescription());
199 data->set_rtcp_mux(true);
200 description->AddContent(mid, protocol_type,
201 /*rejected=*/false, std::move(data));
202 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
203 }
204
AddTransportInfo(cricket::SessionDescription * description,const std::string & mid,const std::string & ufrag,const std::string & pwd,cricket::IceMode ice_mode,cricket::ConnectionRole conn_role,rtc::scoped_refptr<rtc::RTCCertificate> cert)205 void AddTransportInfo(cricket::SessionDescription* description,
206 const std::string& mid,
207 const std::string& ufrag,
208 const std::string& pwd,
209 cricket::IceMode ice_mode,
210 cricket::ConnectionRole conn_role,
211 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
212 std::unique_ptr<rtc::SSLFingerprint> fingerprint;
213 if (cert) {
214 fingerprint = rtc::SSLFingerprint::CreateFromCertificate(*cert);
215 }
216
217 cricket::TransportDescription transport_desc(std::vector<std::string>(),
218 ufrag, pwd, ice_mode,
219 conn_role, fingerprint.get());
220 description->AddTransportInfo(cricket::TransportInfo(mid, transport_desc));
221 }
222
CreateIceConfig(int receiving_timeout,cricket::ContinualGatheringPolicy continual_gathering_policy)223 cricket::IceConfig CreateIceConfig(
224 int receiving_timeout,
225 cricket::ContinualGatheringPolicy continual_gathering_policy) {
226 cricket::IceConfig config;
227 config.receiving_timeout = receiving_timeout;
228 config.continual_gathering_policy = continual_gathering_policy;
229 return config;
230 }
231
CreateCandidate(const std::string & transport_name,int component)232 Candidate CreateCandidate(const std::string& transport_name, int component) {
233 Candidate c;
234 c.set_transport_name(transport_name);
235 c.set_address(rtc::SocketAddress("192.168.1.1", 8000));
236 c.set_component(component);
237 c.set_protocol(cricket::UDP_PROTOCOL_NAME);
238 c.set_priority(1);
239 return c;
240 }
241
CreateLocalDescriptionAndCompleteConnectionOnNetworkThread()242 void CreateLocalDescriptionAndCompleteConnectionOnNetworkThread() {
243 if (!network_thread_->IsCurrent()) {
244 network_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
245 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
246 });
247 return;
248 }
249
250 auto description = CreateSessionDescriptionWithBundleGroup();
251 EXPECT_TRUE(transport_controller_
252 ->SetLocalDescription(SdpType::kOffer, description.get())
253 .ok());
254
255 transport_controller_->MaybeStartGathering();
256 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
257 transport_controller_->GetDtlsTransport(kAudioMid1));
258 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
259 transport_controller_->GetDtlsTransport(kVideoMid1));
260 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
261 fake_audio_dtls->fake_ice_transport(),
262 CreateCandidate(kAudioMid1, /*component=*/1));
263 fake_video_dtls->fake_ice_transport()->SignalCandidateGathered(
264 fake_video_dtls->fake_ice_transport(),
265 CreateCandidate(kVideoMid1, /*component=*/1));
266 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
267 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
268 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
269 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
270 fake_audio_dtls->SetReceiving(true);
271 fake_video_dtls->SetReceiving(true);
272 fake_audio_dtls->SetWritable(true);
273 fake_video_dtls->SetWritable(true);
274 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
275 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
276 }
277
278 protected:
OnConnectionState(cricket::IceConnectionState state)279 void OnConnectionState(cricket::IceConnectionState state) {
280 ice_signaled_on_thread_ = rtc::Thread::Current();
281 connection_state_ = state;
282 ++connection_state_signal_count_;
283 }
284
OnStandardizedIceConnectionState(PeerConnectionInterface::IceConnectionState state)285 void OnStandardizedIceConnectionState(
286 PeerConnectionInterface::IceConnectionState state) {
287 ice_signaled_on_thread_ = rtc::Thread::Current();
288 ice_connection_state_ = state;
289 ++ice_connection_state_signal_count_;
290 }
291
OnCombinedConnectionState(PeerConnectionInterface::PeerConnectionState state)292 void OnCombinedConnectionState(
293 PeerConnectionInterface::PeerConnectionState state) {
294 RTC_LOG(LS_INFO) << "OnCombinedConnectionState: "
295 << static_cast<int>(state);
296 ice_signaled_on_thread_ = rtc::Thread::Current();
297 combined_connection_state_ = state;
298 ++combined_connection_state_signal_count_;
299 }
300
OnGatheringState(cricket::IceGatheringState state)301 void OnGatheringState(cricket::IceGatheringState state) {
302 ice_signaled_on_thread_ = rtc::Thread::Current();
303 gathering_state_ = state;
304 ++gathering_state_signal_count_;
305 }
306
OnCandidatesGathered(const std::string & transport_name,const Candidates & candidates)307 void OnCandidatesGathered(const std::string& transport_name,
308 const Candidates& candidates) {
309 ice_signaled_on_thread_ = rtc::Thread::Current();
310 candidates_[transport_name].insert(candidates_[transport_name].end(),
311 candidates.begin(), candidates.end());
312 ++candidates_signal_count_;
313 }
314
315 // JsepTransportController::Observer overrides.
OnTransportChanged(const std::string & mid,RtpTransportInternal * rtp_transport,rtc::scoped_refptr<DtlsTransport> dtls_transport,DataChannelTransportInterface * data_channel_transport)316 bool OnTransportChanged(
317 const std::string& mid,
318 RtpTransportInternal* rtp_transport,
319 rtc::scoped_refptr<DtlsTransport> dtls_transport,
320 DataChannelTransportInterface* data_channel_transport) override {
321 changed_rtp_transport_by_mid_[mid] = rtp_transport;
322 if (dtls_transport) {
323 changed_dtls_transport_by_mid_[mid] = dtls_transport->internal();
324 } else {
325 changed_dtls_transport_by_mid_[mid] = nullptr;
326 }
327 return true;
328 }
329
330 // Information received from signals from transport controller.
331 cricket::IceConnectionState connection_state_ =
332 cricket::kIceConnectionConnecting;
333 PeerConnectionInterface::IceConnectionState ice_connection_state_ =
334 PeerConnectionInterface::kIceConnectionNew;
335 PeerConnectionInterface::PeerConnectionState combined_connection_state_ =
336 PeerConnectionInterface::PeerConnectionState::kNew;
337 bool receiving_ = false;
338 cricket::IceGatheringState gathering_state_ = cricket::kIceGatheringNew;
339 // transport_name => candidates
340 std::map<std::string, Candidates> candidates_;
341 // Counts of each signal emitted.
342 int connection_state_signal_count_ = 0;
343 int ice_connection_state_signal_count_ = 0;
344 int combined_connection_state_signal_count_ = 0;
345 int receiving_signal_count_ = 0;
346 int gathering_state_signal_count_ = 0;
347 int candidates_signal_count_ = 0;
348
349 // |network_thread_| should be destroyed after |transport_controller_|
350 std::unique_ptr<rtc::Thread> network_thread_;
351 std::unique_ptr<FakeIceTransportFactory> fake_ice_transport_factory_;
352 std::unique_ptr<FakeDtlsTransportFactory> fake_dtls_transport_factory_;
353 rtc::Thread* const signaling_thread_ = nullptr;
354 rtc::Thread* ice_signaled_on_thread_ = nullptr;
355 // Used to verify the SignalRtpTransportChanged/SignalDtlsTransportChanged are
356 // signaled correctly.
357 std::map<std::string, RtpTransportInternal*> changed_rtp_transport_by_mid_;
358 std::map<std::string, cricket::DtlsTransportInternal*>
359 changed_dtls_transport_by_mid_;
360
361 // Transport controller needs to be destroyed first, because it may issue
362 // callbacks that modify the changed_*_by_mid in the destructor.
363 std::unique_ptr<JsepTransportController> transport_controller_;
364 };
365
TEST_F(JsepTransportControllerTest,GetRtpTransport)366 TEST_F(JsepTransportControllerTest, GetRtpTransport) {
367 CreateJsepTransportController(JsepTransportController::Config());
368 auto description = CreateSessionDescriptionWithoutBundle();
369 EXPECT_TRUE(transport_controller_
370 ->SetLocalDescription(SdpType::kOffer, description.get())
371 .ok());
372 auto audio_rtp_transport = transport_controller_->GetRtpTransport(kAudioMid1);
373 auto video_rtp_transport = transport_controller_->GetRtpTransport(kVideoMid1);
374 EXPECT_NE(nullptr, audio_rtp_transport);
375 EXPECT_NE(nullptr, video_rtp_transport);
376 EXPECT_NE(audio_rtp_transport, video_rtp_transport);
377 // Return nullptr for non-existing ones.
378 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid2));
379 }
380
TEST_F(JsepTransportControllerTest,GetDtlsTransport)381 TEST_F(JsepTransportControllerTest, GetDtlsTransport) {
382 JsepTransportController::Config config;
383 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
384 CreateJsepTransportController(config);
385 auto description = CreateSessionDescriptionWithoutBundle();
386 EXPECT_TRUE(transport_controller_
387 ->SetLocalDescription(SdpType::kOffer, description.get())
388 .ok());
389 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
390 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
391 EXPECT_NE(nullptr,
392 transport_controller_->LookupDtlsTransportByMid(kAudioMid1));
393 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
394 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
395 EXPECT_NE(nullptr,
396 transport_controller_->LookupDtlsTransportByMid(kVideoMid1));
397 // Lookup for all MIDs should return different transports (no bundle)
398 EXPECT_NE(transport_controller_->LookupDtlsTransportByMid(kAudioMid1),
399 transport_controller_->LookupDtlsTransportByMid(kVideoMid1));
400 // Return nullptr for non-existing ones.
401 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kVideoMid2));
402 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid2));
403 EXPECT_EQ(nullptr,
404 transport_controller_->LookupDtlsTransportByMid(kVideoMid2));
405 // Take a pointer to a transport, shut down the transport controller,
406 // and verify that the resulting container is empty.
407 auto dtls_transport =
408 transport_controller_->LookupDtlsTransportByMid(kVideoMid1);
409 webrtc::DtlsTransport* my_transport =
410 static_cast<DtlsTransport*>(dtls_transport.get());
411 EXPECT_NE(nullptr, my_transport->internal());
412 transport_controller_.reset();
413 EXPECT_EQ(nullptr, my_transport->internal());
414 }
415
TEST_F(JsepTransportControllerTest,GetDtlsTransportWithRtcpMux)416 TEST_F(JsepTransportControllerTest, GetDtlsTransportWithRtcpMux) {
417 JsepTransportController::Config config;
418 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
419 CreateJsepTransportController(config);
420 auto description = CreateSessionDescriptionWithoutBundle();
421 EXPECT_TRUE(transport_controller_
422 ->SetLocalDescription(SdpType::kOffer, description.get())
423 .ok());
424 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
425 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
426 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
427 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
428 }
429
TEST_F(JsepTransportControllerTest,SetIceConfig)430 TEST_F(JsepTransportControllerTest, SetIceConfig) {
431 CreateJsepTransportController(JsepTransportController::Config());
432 auto description = CreateSessionDescriptionWithoutBundle();
433 EXPECT_TRUE(transport_controller_
434 ->SetLocalDescription(SdpType::kOffer, description.get())
435 .ok());
436
437 transport_controller_->SetIceConfig(
438 CreateIceConfig(kTimeout, cricket::GATHER_CONTINUALLY));
439 FakeDtlsTransport* fake_audio_dtls = static_cast<FakeDtlsTransport*>(
440 transport_controller_->GetDtlsTransport(kAudioMid1));
441 ASSERT_NE(nullptr, fake_audio_dtls);
442 EXPECT_EQ(kTimeout,
443 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
444 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
445
446 // Test that value stored in controller is applied to new transports.
447 AddAudioSection(description.get(), kAudioMid2, kIceUfrag1, kIcePwd1,
448 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
449 nullptr);
450
451 EXPECT_TRUE(transport_controller_
452 ->SetLocalDescription(SdpType::kOffer, description.get())
453 .ok());
454 fake_audio_dtls = static_cast<FakeDtlsTransport*>(
455 transport_controller_->GetDtlsTransport(kAudioMid2));
456 ASSERT_NE(nullptr, fake_audio_dtls);
457 EXPECT_EQ(kTimeout,
458 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
459 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
460 }
461
462 // Tests the getter and setter of the ICE restart flag.
TEST_F(JsepTransportControllerTest,NeedIceRestart)463 TEST_F(JsepTransportControllerTest, NeedIceRestart) {
464 CreateJsepTransportController(JsepTransportController::Config());
465 auto description = CreateSessionDescriptionWithoutBundle();
466 EXPECT_TRUE(transport_controller_
467 ->SetLocalDescription(SdpType::kOffer, description.get())
468 .ok());
469 EXPECT_TRUE(transport_controller_
470 ->SetRemoteDescription(SdpType::kAnswer, description.get())
471 .ok());
472
473 // Initially NeedsIceRestart should return false.
474 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
475 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid1));
476 // Set the needs-ice-restart flag and verify NeedsIceRestart starts returning
477 // true.
478 transport_controller_->SetNeedsIceRestartFlag();
479 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kAudioMid1));
480 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
481 // For a nonexistent transport, false should be returned.
482 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid2));
483
484 // Reset the ice_ufrag/ice_pwd for audio.
485 auto audio_transport_info = description->GetTransportInfoByName(kAudioMid1);
486 audio_transport_info->description.ice_ufrag = kIceUfrag2;
487 audio_transport_info->description.ice_pwd = kIcePwd2;
488 EXPECT_TRUE(transport_controller_
489 ->SetLocalDescription(SdpType::kOffer, description.get())
490 .ok());
491 // Because the ICE is only restarted for audio, NeedsIceRestart is expected to
492 // return false for audio and true for video.
493 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
494 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
495 }
496
TEST_F(JsepTransportControllerTest,MaybeStartGathering)497 TEST_F(JsepTransportControllerTest, MaybeStartGathering) {
498 CreateJsepTransportController(JsepTransportController::Config());
499 auto description = CreateSessionDescriptionWithBundleGroup();
500 EXPECT_TRUE(transport_controller_
501 ->SetLocalDescription(SdpType::kOffer, description.get())
502 .ok());
503 // After setting the local description, we should be able to start gathering
504 // candidates.
505 transport_controller_->MaybeStartGathering();
506 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
507 EXPECT_EQ(1, gathering_state_signal_count_);
508 }
509
TEST_F(JsepTransportControllerTest,AddRemoveRemoteCandidates)510 TEST_F(JsepTransportControllerTest, AddRemoveRemoteCandidates) {
511 CreateJsepTransportController(JsepTransportController::Config());
512 auto description = CreateSessionDescriptionWithoutBundle();
513 transport_controller_->SetLocalDescription(SdpType::kOffer,
514 description.get());
515 transport_controller_->SetRemoteDescription(SdpType::kAnswer,
516 description.get());
517 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
518 transport_controller_->GetDtlsTransport(kAudioMid1));
519 ASSERT_NE(nullptr, fake_audio_dtls);
520 Candidates candidates;
521 candidates.push_back(
522 CreateCandidate(kAudioMid1, cricket::ICE_CANDIDATE_COMPONENT_RTP));
523 EXPECT_TRUE(
524 transport_controller_->AddRemoteCandidates(kAudioMid1, candidates).ok());
525 EXPECT_EQ(1U,
526 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
527
528 EXPECT_TRUE(transport_controller_->RemoveRemoteCandidates(candidates).ok());
529 EXPECT_EQ(0U,
530 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
531 }
532
TEST_F(JsepTransportControllerTest,SetAndGetLocalCertificate)533 TEST_F(JsepTransportControllerTest, SetAndGetLocalCertificate) {
534 CreateJsepTransportController(JsepTransportController::Config());
535
536 rtc::scoped_refptr<rtc::RTCCertificate> certificate1 =
537 rtc::RTCCertificate::Create(
538 rtc::SSLIdentity::Create("session1", rtc::KT_DEFAULT));
539 rtc::scoped_refptr<rtc::RTCCertificate> returned_certificate;
540
541 auto description = std::make_unique<cricket::SessionDescription>();
542 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
543 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
544 certificate1);
545
546 // Apply the local certificate.
547 EXPECT_TRUE(transport_controller_->SetLocalCertificate(certificate1));
548 // Apply the local description.
549 EXPECT_TRUE(transport_controller_
550 ->SetLocalDescription(SdpType::kOffer, description.get())
551 .ok());
552 returned_certificate = transport_controller_->GetLocalCertificate(kAudioMid1);
553 EXPECT_TRUE(returned_certificate);
554 EXPECT_EQ(certificate1->identity()->certificate().ToPEMString(),
555 returned_certificate->identity()->certificate().ToPEMString());
556
557 // Should fail if called for a nonexistant transport.
558 EXPECT_EQ(nullptr, transport_controller_->GetLocalCertificate(kVideoMid1));
559
560 // Shouldn't be able to change the identity once set.
561 rtc::scoped_refptr<rtc::RTCCertificate> certificate2 =
562 rtc::RTCCertificate::Create(
563 rtc::SSLIdentity::Create("session2", rtc::KT_DEFAULT));
564 EXPECT_FALSE(transport_controller_->SetLocalCertificate(certificate2));
565 }
566
TEST_F(JsepTransportControllerTest,GetRemoteSSLCertChain)567 TEST_F(JsepTransportControllerTest, GetRemoteSSLCertChain) {
568 CreateJsepTransportController(JsepTransportController::Config());
569 auto description = CreateSessionDescriptionWithBundleGroup();
570 EXPECT_TRUE(transport_controller_
571 ->SetLocalDescription(SdpType::kOffer, description.get())
572 .ok());
573 rtc::FakeSSLCertificate fake_certificate("fake_data");
574
575 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
576 transport_controller_->GetDtlsTransport(kAudioMid1));
577 fake_audio_dtls->SetRemoteSSLCertificate(&fake_certificate);
578 std::unique_ptr<rtc::SSLCertChain> returned_cert_chain =
579 transport_controller_->GetRemoteSSLCertChain(kAudioMid1);
580 ASSERT_TRUE(returned_cert_chain);
581 ASSERT_EQ(1u, returned_cert_chain->GetSize());
582 EXPECT_EQ(fake_certificate.ToPEMString(),
583 returned_cert_chain->Get(0).ToPEMString());
584
585 // Should fail if called for a nonexistant transport.
586 EXPECT_FALSE(transport_controller_->GetRemoteSSLCertChain(kAudioMid2));
587 }
588
TEST_F(JsepTransportControllerTest,GetDtlsRole)589 TEST_F(JsepTransportControllerTest, GetDtlsRole) {
590 CreateJsepTransportController(JsepTransportController::Config());
591 auto offer_certificate = rtc::RTCCertificate::Create(
592 rtc::SSLIdentity::Create("offer", rtc::KT_DEFAULT));
593 auto answer_certificate = rtc::RTCCertificate::Create(
594 rtc::SSLIdentity::Create("answer", rtc::KT_DEFAULT));
595 transport_controller_->SetLocalCertificate(offer_certificate);
596
597 auto offer_desc = std::make_unique<cricket::SessionDescription>();
598 AddAudioSection(offer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
599 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
600 offer_certificate);
601 auto answer_desc = std::make_unique<cricket::SessionDescription>();
602 AddAudioSection(answer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
603 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
604 answer_certificate);
605
606 EXPECT_TRUE(transport_controller_
607 ->SetLocalDescription(SdpType::kOffer, offer_desc.get())
608 .ok());
609
610 absl::optional<rtc::SSLRole> role =
611 transport_controller_->GetDtlsRole(kAudioMid1);
612 // The DTLS role is not decided yet.
613 EXPECT_FALSE(role);
614 EXPECT_TRUE(transport_controller_
615 ->SetRemoteDescription(SdpType::kAnswer, answer_desc.get())
616 .ok());
617 role = transport_controller_->GetDtlsRole(kAudioMid1);
618
619 ASSERT_TRUE(role);
620 EXPECT_EQ(rtc::SSL_CLIENT, *role);
621 }
622
TEST_F(JsepTransportControllerTest,GetStats)623 TEST_F(JsepTransportControllerTest, GetStats) {
624 CreateJsepTransportController(JsepTransportController::Config());
625 auto description = CreateSessionDescriptionWithBundleGroup();
626 EXPECT_TRUE(transport_controller_
627 ->SetLocalDescription(SdpType::kOffer, description.get())
628 .ok());
629
630 cricket::TransportStats stats;
631 EXPECT_TRUE(transport_controller_->GetStats(kAudioMid1, &stats));
632 EXPECT_EQ(kAudioMid1, stats.transport_name);
633 EXPECT_EQ(1u, stats.channel_stats.size());
634 // Return false for non-existing transport.
635 EXPECT_FALSE(transport_controller_->GetStats(kAudioMid2, &stats));
636 }
637
TEST_F(JsepTransportControllerTest,SignalConnectionStateFailed)638 TEST_F(JsepTransportControllerTest, SignalConnectionStateFailed) {
639 CreateJsepTransportController(JsepTransportController::Config());
640 auto description = CreateSessionDescriptionWithoutBundle();
641 EXPECT_TRUE(transport_controller_
642 ->SetLocalDescription(SdpType::kOffer, description.get())
643 .ok());
644
645 auto fake_ice = static_cast<cricket::FakeIceTransport*>(
646 transport_controller_->GetDtlsTransport(kAudioMid1)->ice_transport());
647 fake_ice->SetCandidatesGatheringComplete();
648 fake_ice->SetConnectionCount(1);
649 // The connection stats will be failed if there is no active connection.
650 fake_ice->SetConnectionCount(0);
651 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
652 EXPECT_EQ(1, connection_state_signal_count_);
653 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionFailed,
654 ice_connection_state_, kTimeout);
655 EXPECT_EQ(1, ice_connection_state_signal_count_);
656 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kFailed,
657 combined_connection_state_, kTimeout);
658 EXPECT_EQ(1, combined_connection_state_signal_count_);
659 }
660
TEST_F(JsepTransportControllerTest,SignalConnectionStateConnectedNoMediaTransport)661 TEST_F(JsepTransportControllerTest,
662 SignalConnectionStateConnectedNoMediaTransport) {
663 CreateJsepTransportController(JsepTransportController::Config());
664 auto description = CreateSessionDescriptionWithoutBundle();
665 EXPECT_TRUE(transport_controller_
666 ->SetLocalDescription(SdpType::kOffer, description.get())
667 .ok());
668
669 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
670 transport_controller_->GetDtlsTransport(kAudioMid1));
671 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
672 transport_controller_->GetDtlsTransport(kVideoMid1));
673
674 // First, have one transport connect, and another fail, to ensure that
675 // the first transport connecting didn't trigger a "connected" state signal.
676 // We should only get a signal when all are connected.
677 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
678 fake_audio_dtls->SetWritable(true);
679 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
680 // Decrease the number of the connection to trigger the signal.
681 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
682 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
683 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
684
685 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
686 EXPECT_EQ(1, connection_state_signal_count_);
687 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionFailed,
688 ice_connection_state_, kTimeout);
689 EXPECT_EQ(2, ice_connection_state_signal_count_);
690 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kFailed,
691 combined_connection_state_, kTimeout);
692 EXPECT_EQ(2, combined_connection_state_signal_count_);
693
694 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
695 fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
696 // Set the connection count to be 2 and the cricket::FakeIceTransport will set
697 // the transport state to be STATE_CONNECTING.
698 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
699 fake_video_dtls->SetWritable(true);
700 EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
701 EXPECT_EQ(2, connection_state_signal_count_);
702 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionConnected,
703 ice_connection_state_, kTimeout);
704 EXPECT_EQ(3, ice_connection_state_signal_count_);
705 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kConnected,
706 combined_connection_state_, kTimeout);
707 EXPECT_EQ(3, combined_connection_state_signal_count_);
708 }
709
TEST_F(JsepTransportControllerTest,SignalConnectionStateComplete)710 TEST_F(JsepTransportControllerTest, SignalConnectionStateComplete) {
711 CreateJsepTransportController(JsepTransportController::Config());
712 auto description = CreateSessionDescriptionWithoutBundle();
713 EXPECT_TRUE(transport_controller_
714 ->SetLocalDescription(SdpType::kOffer, description.get())
715 .ok());
716
717 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
718 transport_controller_->GetDtlsTransport(kAudioMid1));
719 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
720 transport_controller_->GetDtlsTransport(kVideoMid1));
721
722 // First, have one transport connect, and another fail, to ensure that
723 // the first transport connecting didn't trigger a "connected" state signal.
724 // We should only get a signal when all are connected.
725 fake_audio_dtls->fake_ice_transport()->SetTransportState(
726 IceTransportState::kCompleted,
727 cricket::IceTransportState::STATE_COMPLETED);
728 fake_audio_dtls->SetWritable(true);
729 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
730
731 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionChecking,
732 ice_connection_state_, kTimeout);
733 EXPECT_EQ(1, ice_connection_state_signal_count_);
734 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kConnecting,
735 combined_connection_state_, kTimeout);
736 EXPECT_EQ(1, combined_connection_state_signal_count_);
737
738 fake_video_dtls->fake_ice_transport()->SetTransportState(
739 IceTransportState::kFailed, cricket::IceTransportState::STATE_FAILED);
740 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
741
742 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
743 EXPECT_EQ(1, connection_state_signal_count_);
744 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionFailed,
745 ice_connection_state_, kTimeout);
746 EXPECT_EQ(2, ice_connection_state_signal_count_);
747 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kFailed,
748 combined_connection_state_, kTimeout);
749 EXPECT_EQ(2, combined_connection_state_signal_count_);
750
751 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
752 fake_video_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
753 // Set the connection count to be 1 and the cricket::FakeIceTransport will set
754 // the transport state to be STATE_COMPLETED.
755 fake_video_dtls->fake_ice_transport()->SetTransportState(
756 IceTransportState::kCompleted,
757 cricket::IceTransportState::STATE_COMPLETED);
758 fake_video_dtls->SetWritable(true);
759 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
760 EXPECT_EQ(3, connection_state_signal_count_);
761 EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
762 ice_connection_state_, kTimeout);
763 EXPECT_EQ(3, ice_connection_state_signal_count_);
764 EXPECT_EQ_WAIT(PeerConnectionInterface::PeerConnectionState::kConnected,
765 combined_connection_state_, kTimeout);
766 EXPECT_EQ(3, combined_connection_state_signal_count_);
767 }
768
TEST_F(JsepTransportControllerTest,SignalIceGatheringStateGathering)769 TEST_F(JsepTransportControllerTest, SignalIceGatheringStateGathering) {
770 CreateJsepTransportController(JsepTransportController::Config());
771 auto description = CreateSessionDescriptionWithoutBundle();
772 EXPECT_TRUE(transport_controller_
773 ->SetLocalDescription(SdpType::kOffer, description.get())
774 .ok());
775
776 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
777 transport_controller_->GetDtlsTransport(kAudioMid1));
778 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
779 // Should be in the gathering state as soon as any transport starts gathering.
780 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
781 EXPECT_EQ(1, gathering_state_signal_count_);
782 }
783
TEST_F(JsepTransportControllerTest,SignalIceGatheringStateComplete)784 TEST_F(JsepTransportControllerTest, SignalIceGatheringStateComplete) {
785 CreateJsepTransportController(JsepTransportController::Config());
786 auto description = CreateSessionDescriptionWithoutBundle();
787 EXPECT_TRUE(transport_controller_
788 ->SetLocalDescription(SdpType::kOffer, description.get())
789 .ok());
790
791 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
792 transport_controller_->GetDtlsTransport(kAudioMid1));
793 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
794 transport_controller_->GetDtlsTransport(kVideoMid1));
795
796 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
797 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
798 EXPECT_EQ(1, gathering_state_signal_count_);
799
800 // Have one transport finish gathering, to make sure gathering
801 // completion wasn't signalled if only one transport finished gathering.
802 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
803 EXPECT_EQ(1, gathering_state_signal_count_);
804
805 fake_video_dtls->fake_ice_transport()->MaybeStartGathering();
806 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
807 EXPECT_EQ(1, gathering_state_signal_count_);
808
809 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
810 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
811 EXPECT_EQ(2, gathering_state_signal_count_);
812 }
813
814 // Test that when the last transport that hasn't finished connecting and/or
815 // gathering is destroyed, the aggregate state jumps to "completed". This can
816 // happen if, for example, we have an audio and video transport, the audio
817 // transport completes, then we start bundling video on the audio transport.
TEST_F(JsepTransportControllerTest,SignalingWhenLastIncompleteTransportDestroyed)818 TEST_F(JsepTransportControllerTest,
819 SignalingWhenLastIncompleteTransportDestroyed) {
820 CreateJsepTransportController(JsepTransportController::Config());
821 auto description = CreateSessionDescriptionWithBundleGroup();
822 EXPECT_TRUE(transport_controller_
823 ->SetLocalDescription(SdpType::kOffer, description.get())
824 .ok());
825
826 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
827 transport_controller_->GetDtlsTransport(kAudioMid1));
828 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
829 transport_controller_->GetDtlsTransport(kVideoMid1));
830 EXPECT_NE(fake_audio_dtls, fake_video_dtls);
831
832 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
833 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
834 EXPECT_EQ(1, gathering_state_signal_count_);
835
836 // Let the audio transport complete.
837 fake_audio_dtls->SetWritable(true);
838 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
839 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
840 fake_audio_dtls->SetDtlsState(cricket::DTLS_TRANSPORT_CONNECTED);
841 EXPECT_EQ(1, gathering_state_signal_count_);
842
843 // Set the remote description and enable the bundle.
844 EXPECT_TRUE(transport_controller_
845 ->SetRemoteDescription(SdpType::kAnswer, description.get())
846 .ok());
847 // The BUNDLE should be enabled, the incomplete video transport should be
848 // deleted and the states shoud be updated.
849 fake_video_dtls = static_cast<FakeDtlsTransport*>(
850 transport_controller_->GetDtlsTransport(kVideoMid1));
851 EXPECT_EQ(fake_audio_dtls, fake_video_dtls);
852 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
853 EXPECT_EQ(PeerConnectionInterface::kIceConnectionCompleted,
854 ice_connection_state_);
855 EXPECT_EQ(PeerConnectionInterface::PeerConnectionState::kConnected,
856 combined_connection_state_);
857 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
858 EXPECT_EQ(2, gathering_state_signal_count_);
859 }
860
TEST_F(JsepTransportControllerTest,SignalCandidatesGathered)861 TEST_F(JsepTransportControllerTest, SignalCandidatesGathered) {
862 CreateJsepTransportController(JsepTransportController::Config());
863 auto description = CreateSessionDescriptionWithBundleGroup();
864 EXPECT_TRUE(transport_controller_
865 ->SetLocalDescription(SdpType::kOffer, description.get())
866 .ok());
867 transport_controller_->MaybeStartGathering();
868
869 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
870 transport_controller_->GetDtlsTransport(kAudioMid1));
871 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
872 fake_audio_dtls->fake_ice_transport(), CreateCandidate(kAudioMid1, 1));
873 EXPECT_EQ_WAIT(1, candidates_signal_count_, kTimeout);
874 EXPECT_EQ(1u, candidates_[kAudioMid1].size());
875 }
876
TEST_F(JsepTransportControllerTest,IceSignalingOccursOnNetworkThread)877 TEST_F(JsepTransportControllerTest, IceSignalingOccursOnNetworkThread) {
878 network_thread_ = rtc::Thread::CreateWithSocketServer();
879 network_thread_->Start();
880 EXPECT_EQ(ice_signaled_on_thread_, nullptr);
881 CreateJsepTransportController(JsepTransportController::Config(),
882 network_thread_.get(),
883 /*port_allocator=*/nullptr);
884 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
885
886 // connecting --> connected --> completed
887 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
888 EXPECT_EQ(2, connection_state_signal_count_);
889
890 // new --> gathering --> complete
891 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
892 EXPECT_EQ(2, gathering_state_signal_count_);
893
894 EXPECT_EQ_WAIT(1u, candidates_[kAudioMid1].size(), kTimeout);
895 EXPECT_EQ_WAIT(1u, candidates_[kVideoMid1].size(), kTimeout);
896 EXPECT_EQ(2, candidates_signal_count_);
897
898 EXPECT_EQ(ice_signaled_on_thread_, network_thread_.get());
899
900 network_thread_->Invoke<void>(RTC_FROM_HERE,
901 [&] { transport_controller_.reset(); });
902 }
903
904 // Test that if the TransportController was created with the
905 // |redetermine_role_on_ice_restart| parameter set to false, the role is *not*
906 // redetermined on an ICE restart.
TEST_F(JsepTransportControllerTest,IceRoleNotRedetermined)907 TEST_F(JsepTransportControllerTest, IceRoleNotRedetermined) {
908 JsepTransportController::Config config;
909 config.redetermine_role_on_ice_restart = false;
910
911 CreateJsepTransportController(config);
912 // Let the |transport_controller_| be the controlled side initially.
913 auto remote_offer = std::make_unique<cricket::SessionDescription>();
914 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
915 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
916 nullptr);
917 auto local_answer = std::make_unique<cricket::SessionDescription>();
918 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
919 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
920 nullptr);
921
922 EXPECT_TRUE(transport_controller_
923 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
924 .ok());
925 EXPECT_TRUE(transport_controller_
926 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
927 .ok());
928
929 auto fake_dtls = static_cast<FakeDtlsTransport*>(
930 transport_controller_->GetDtlsTransport(kAudioMid1));
931 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
932 fake_dtls->fake_ice_transport()->GetIceRole());
933
934 // New offer will trigger the ICE restart.
935 auto restart_local_offer = std::make_unique<cricket::SessionDescription>();
936 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
937 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
938 nullptr);
939 EXPECT_TRUE(
940 transport_controller_
941 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
942 .ok());
943 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
944 fake_dtls->fake_ice_transport()->GetIceRole());
945 }
946
947 // Tests ICE-Lite mode in remote answer.
TEST_F(JsepTransportControllerTest,SetIceRoleWhenIceLiteInRemoteAnswer)948 TEST_F(JsepTransportControllerTest, SetIceRoleWhenIceLiteInRemoteAnswer) {
949 CreateJsepTransportController(JsepTransportController::Config());
950 auto local_offer = std::make_unique<cricket::SessionDescription>();
951 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
952 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
953 nullptr);
954 EXPECT_TRUE(transport_controller_
955 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
956 .ok());
957 auto fake_dtls = static_cast<FakeDtlsTransport*>(
958 transport_controller_->GetDtlsTransport(kAudioMid1));
959 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
960 fake_dtls->fake_ice_transport()->GetIceRole());
961 EXPECT_EQ(cricket::ICEMODE_FULL,
962 fake_dtls->fake_ice_transport()->remote_ice_mode());
963
964 auto remote_answer = std::make_unique<cricket::SessionDescription>();
965 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
966 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_PASSIVE,
967 nullptr);
968 EXPECT_TRUE(transport_controller_
969 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
970 .ok());
971 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
972 fake_dtls->fake_ice_transport()->GetIceRole());
973 EXPECT_EQ(cricket::ICEMODE_LITE,
974 fake_dtls->fake_ice_transport()->remote_ice_mode());
975 }
976
977 // Tests that the ICE role remains "controlling" if a subsequent offer that
978 // does an ICE restart is received from an ICE lite endpoint. Regression test
979 // for: https://crbug.com/710760
TEST_F(JsepTransportControllerTest,IceRoleIsControllingAfterIceRestartFromIceLiteEndpoint)980 TEST_F(JsepTransportControllerTest,
981 IceRoleIsControllingAfterIceRestartFromIceLiteEndpoint) {
982 CreateJsepTransportController(JsepTransportController::Config());
983 auto remote_offer = std::make_unique<cricket::SessionDescription>();
984 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
985 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
986 nullptr);
987 auto local_answer = std::make_unique<cricket::SessionDescription>();
988 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
989 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
990 nullptr);
991 // Initial Offer/Answer exchange. If the remote offerer is ICE-Lite, then the
992 // local side is the controlling.
993 EXPECT_TRUE(transport_controller_
994 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
995 .ok());
996 EXPECT_TRUE(transport_controller_
997 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
998 .ok());
999 auto fake_dtls = static_cast<FakeDtlsTransport*>(
1000 transport_controller_->GetDtlsTransport(kAudioMid1));
1001 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1002 fake_dtls->fake_ice_transport()->GetIceRole());
1003
1004 // In the subsequence remote offer triggers an ICE restart.
1005 auto remote_offer2 = std::make_unique<cricket::SessionDescription>();
1006 AddAudioSection(remote_offer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1007 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
1008 nullptr);
1009 auto local_answer2 = std::make_unique<cricket::SessionDescription>();
1010 AddAudioSection(local_answer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1011 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1012 nullptr);
1013 EXPECT_TRUE(transport_controller_
1014 ->SetRemoteDescription(SdpType::kOffer, remote_offer2.get())
1015 .ok());
1016 EXPECT_TRUE(transport_controller_
1017 ->SetLocalDescription(SdpType::kAnswer, local_answer2.get())
1018 .ok());
1019 fake_dtls = static_cast<FakeDtlsTransport*>(
1020 transport_controller_->GetDtlsTransport(kAudioMid1));
1021 // The local side is still the controlling role since the remote side is using
1022 // ICE-Lite.
1023 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
1024 fake_dtls->fake_ice_transport()->GetIceRole());
1025 }
1026
1027 // Tests that the SDP has more than one audio/video m= sections.
TEST_F(JsepTransportControllerTest,MultipleMediaSectionsOfSameTypeWithBundle)1028 TEST_F(JsepTransportControllerTest, MultipleMediaSectionsOfSameTypeWithBundle) {
1029 CreateJsepTransportController(JsepTransportController::Config());
1030 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1031 bundle_group.AddContentName(kAudioMid1);
1032 bundle_group.AddContentName(kAudioMid2);
1033 bundle_group.AddContentName(kVideoMid1);
1034 bundle_group.AddContentName(kDataMid1);
1035
1036 auto local_offer = std::make_unique<cricket::SessionDescription>();
1037 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1038 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1039 nullptr);
1040 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1041 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1042 nullptr);
1043 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1044 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1045 nullptr);
1046 AddDataSection(local_offer.get(), kDataMid1,
1047 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1048 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1049 nullptr);
1050
1051 auto remote_answer = std::make_unique<cricket::SessionDescription>();
1052 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1053 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1054 nullptr);
1055 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1056 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1057 nullptr);
1058 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1059 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1060 nullptr);
1061 AddDataSection(remote_answer.get(), kDataMid1,
1062 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1063 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1064 nullptr);
1065
1066 local_offer->AddGroup(bundle_group);
1067 remote_answer->AddGroup(bundle_group);
1068
1069 EXPECT_TRUE(transport_controller_
1070 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1071 .ok());
1072 EXPECT_TRUE(transport_controller_
1073 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1074 .ok());
1075 // Verify that all the sections are bundled on kAudio1.
1076 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
1077 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
1078 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
1079 auto transport4 = transport_controller_->GetRtpTransport(kDataMid1);
1080 EXPECT_EQ(transport1, transport2);
1081 EXPECT_EQ(transport1, transport3);
1082 EXPECT_EQ(transport1, transport4);
1083
1084 EXPECT_EQ(transport_controller_->LookupDtlsTransportByMid(kAudioMid1),
1085 transport_controller_->LookupDtlsTransportByMid(kVideoMid1));
1086
1087 // Verify the OnRtpTransport/DtlsTransportChanged signals are fired correctly.
1088 auto it = changed_rtp_transport_by_mid_.find(kAudioMid2);
1089 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1090 EXPECT_EQ(transport1, it->second);
1091 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
1092 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1093 EXPECT_EQ(transport1, it->second);
1094 it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1095 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1096 EXPECT_EQ(transport1, it->second);
1097 // Verify the DtlsTransport for the SCTP data channel is reset correctly.
1098 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1099 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1100 }
1101
1102 // Tests that only a subset of all the m= sections are bundled.
TEST_F(JsepTransportControllerTest,BundleSubsetOfMediaSections)1103 TEST_F(JsepTransportControllerTest, BundleSubsetOfMediaSections) {
1104 CreateJsepTransportController(JsepTransportController::Config());
1105 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1106 bundle_group.AddContentName(kAudioMid1);
1107 bundle_group.AddContentName(kVideoMid1);
1108
1109 auto local_offer = std::make_unique<cricket::SessionDescription>();
1110 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1111 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1112 nullptr);
1113 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1114 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1115 nullptr);
1116 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1117 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1118 nullptr);
1119
1120 auto remote_answer = std::make_unique<cricket::SessionDescription>();
1121 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1122 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1123 nullptr);
1124 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1125 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1126 nullptr);
1127 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1128 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1129 nullptr);
1130
1131 local_offer->AddGroup(bundle_group);
1132 remote_answer->AddGroup(bundle_group);
1133 EXPECT_TRUE(transport_controller_
1134 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1135 .ok());
1136 EXPECT_TRUE(transport_controller_
1137 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1138 .ok());
1139
1140 // Verifiy that only |kAudio1| and |kVideo1| are bundled.
1141 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
1142 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
1143 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
1144 EXPECT_NE(transport1, transport2);
1145 EXPECT_EQ(transport1, transport3);
1146
1147 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1148 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1149 EXPECT_EQ(transport1, it->second);
1150 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
1151 EXPECT_TRUE(transport2 == it->second);
1152 }
1153
1154 // Tests that the initial offer/answer only have data section and audio/video
1155 // sections are added in the subsequent offer.
TEST_F(JsepTransportControllerTest,BundleOnDataSectionInSubsequentOffer)1156 TEST_F(JsepTransportControllerTest, BundleOnDataSectionInSubsequentOffer) {
1157 CreateJsepTransportController(JsepTransportController::Config());
1158 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1159 bundle_group.AddContentName(kDataMid1);
1160
1161 auto local_offer = std::make_unique<cricket::SessionDescription>();
1162 AddDataSection(local_offer.get(), kDataMid1,
1163 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1164 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1165 nullptr);
1166 auto remote_answer = std::make_unique<cricket::SessionDescription>();
1167 AddDataSection(remote_answer.get(), kDataMid1,
1168 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1169 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1170 nullptr);
1171 local_offer->AddGroup(bundle_group);
1172 remote_answer->AddGroup(bundle_group);
1173
1174 EXPECT_TRUE(transport_controller_
1175 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1176 .ok());
1177 EXPECT_TRUE(transport_controller_
1178 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1179 .ok());
1180 auto data_transport = transport_controller_->GetRtpTransport(kDataMid1);
1181
1182 // Add audio/video sections in subsequent offer.
1183 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1184 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1185 nullptr);
1186 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1187 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1188 nullptr);
1189 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1190 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1191 nullptr);
1192 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1193 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1194 nullptr);
1195
1196 // Reset the bundle group and do another offer/answer exchange.
1197 bundle_group.AddContentName(kAudioMid1);
1198 bundle_group.AddContentName(kVideoMid1);
1199 local_offer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1200 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1201 local_offer->AddGroup(bundle_group);
1202 remote_answer->AddGroup(bundle_group);
1203
1204 EXPECT_TRUE(transport_controller_
1205 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1206 .ok());
1207 EXPECT_TRUE(transport_controller_
1208 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1209 .ok());
1210
1211 auto audio_transport = transport_controller_->GetRtpTransport(kAudioMid1);
1212 auto video_transport = transport_controller_->GetRtpTransport(kVideoMid1);
1213 EXPECT_EQ(data_transport, audio_transport);
1214 EXPECT_EQ(data_transport, video_transport);
1215 }
1216
TEST_F(JsepTransportControllerTest,VideoDataRejectedInAnswer)1217 TEST_F(JsepTransportControllerTest, VideoDataRejectedInAnswer) {
1218 CreateJsepTransportController(JsepTransportController::Config());
1219 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1220 bundle_group.AddContentName(kAudioMid1);
1221 bundle_group.AddContentName(kVideoMid1);
1222 bundle_group.AddContentName(kDataMid1);
1223
1224 auto local_offer = std::make_unique<cricket::SessionDescription>();
1225 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1226 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1227 nullptr);
1228 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1229 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1230 nullptr);
1231 AddDataSection(local_offer.get(), kDataMid1,
1232 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1233 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1234 nullptr);
1235
1236 auto remote_answer = std::make_unique<cricket::SessionDescription>();
1237 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1238 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1239 nullptr);
1240 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1241 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1242 nullptr);
1243 AddDataSection(remote_answer.get(), kDataMid1,
1244 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1245 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1246 nullptr);
1247 // Reject video and data section.
1248 remote_answer->contents()[1].rejected = true;
1249 remote_answer->contents()[2].rejected = true;
1250
1251 local_offer->AddGroup(bundle_group);
1252 remote_answer->AddGroup(bundle_group);
1253
1254 EXPECT_TRUE(transport_controller_
1255 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1256 .ok());
1257 EXPECT_TRUE(transport_controller_
1258 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1259 .ok());
1260
1261 // Verify the RtpTransport/DtlsTransport is destroyed correctly.
1262 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1263 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1264 // Verify the signals are fired correctly.
1265 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1266 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1267 EXPECT_EQ(nullptr, it->second);
1268 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1269 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1270 EXPECT_EQ(nullptr, it2->second);
1271 }
1272
1273 // Tests that changing the bundled MID in subsequent offer/answer exchange is
1274 // not supported.
1275 // TODO(bugs.webrtc.org/6704): Change this test to expect success once issue is
1276 // fixed
TEST_F(JsepTransportControllerTest,ChangeBundledMidNotSupported)1277 TEST_F(JsepTransportControllerTest, ChangeBundledMidNotSupported) {
1278 CreateJsepTransportController(JsepTransportController::Config());
1279 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1280 bundle_group.AddContentName(kAudioMid1);
1281 bundle_group.AddContentName(kVideoMid1);
1282
1283 auto local_offer = std::make_unique<cricket::SessionDescription>();
1284 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1285 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1286 nullptr);
1287 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1288 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1289 nullptr);
1290
1291 auto remote_answer = std::make_unique<cricket::SessionDescription>();
1292 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1293 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1294 nullptr);
1295 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1296 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1297 nullptr);
1298
1299 local_offer->AddGroup(bundle_group);
1300 remote_answer->AddGroup(bundle_group);
1301 EXPECT_TRUE(transport_controller_
1302 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1303 .ok());
1304 EXPECT_TRUE(transport_controller_
1305 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1306 .ok());
1307 EXPECT_EQ(transport_controller_->GetRtpTransport(kAudioMid1),
1308 transport_controller_->GetRtpTransport(kVideoMid1));
1309
1310 // Reorder the bundle group.
1311 EXPECT_TRUE(bundle_group.RemoveContentName(kAudioMid1));
1312 bundle_group.AddContentName(kAudioMid1);
1313 // The answerer uses the new bundle group and now the bundle mid is changed to
1314 // |kVideo1|.
1315 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1316 remote_answer->AddGroup(bundle_group);
1317 EXPECT_TRUE(transport_controller_
1318 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1319 .ok());
1320 EXPECT_FALSE(transport_controller_
1321 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1322 .ok());
1323 }
1324 // Test that rejecting only the first m= section of a BUNDLE group is treated as
1325 // an error, but rejecting all of them works as expected.
TEST_F(JsepTransportControllerTest,RejectFirstContentInBundleGroup)1326 TEST_F(JsepTransportControllerTest, RejectFirstContentInBundleGroup) {
1327 CreateJsepTransportController(JsepTransportController::Config());
1328 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1329 bundle_group.AddContentName(kAudioMid1);
1330 bundle_group.AddContentName(kVideoMid1);
1331 bundle_group.AddContentName(kDataMid1);
1332
1333 auto local_offer = std::make_unique<cricket::SessionDescription>();
1334 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1335 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1336 nullptr);
1337 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1338 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1339 nullptr);
1340 AddDataSection(local_offer.get(), kDataMid1,
1341 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1342 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1343 nullptr);
1344
1345 auto remote_answer = std::make_unique<cricket::SessionDescription>();
1346 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1347 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1348 nullptr);
1349 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1350 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1351 nullptr);
1352 AddDataSection(remote_answer.get(), kDataMid1,
1353 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1354 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1355 nullptr);
1356 // Reject audio content in answer.
1357 remote_answer->contents()[0].rejected = true;
1358
1359 local_offer->AddGroup(bundle_group);
1360 remote_answer->AddGroup(bundle_group);
1361
1362 EXPECT_TRUE(transport_controller_
1363 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1364 .ok());
1365 EXPECT_FALSE(transport_controller_
1366 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1367 .ok());
1368
1369 // Reject all the contents.
1370 remote_answer->contents()[1].rejected = true;
1371 remote_answer->contents()[2].rejected = true;
1372 EXPECT_TRUE(transport_controller_
1373 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1374 .ok());
1375 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid1));
1376 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1377 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1378 }
1379
1380 // Tests that applying non-RTCP-mux offer would fail when kRtcpMuxPolicyRequire
1381 // is used.
TEST_F(JsepTransportControllerTest,ApplyNonRtcpMuxOfferWhenMuxingRequired)1382 TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxOfferWhenMuxingRequired) {
1383 JsepTransportController::Config config;
1384 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
1385 CreateJsepTransportController(config);
1386 auto local_offer = std::make_unique<cricket::SessionDescription>();
1387 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1388 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1389 nullptr);
1390
1391 local_offer->contents()[0].media_description()->set_rtcp_mux(false);
1392 // Applying a non-RTCP-mux offer is expected to fail.
1393 EXPECT_FALSE(transport_controller_
1394 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1395 .ok());
1396 }
1397
1398 // Tests that applying non-RTCP-mux answer would fail when kRtcpMuxPolicyRequire
1399 // is used.
TEST_F(JsepTransportControllerTest,ApplyNonRtcpMuxAnswerWhenMuxingRequired)1400 TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxAnswerWhenMuxingRequired) {
1401 JsepTransportController::Config config;
1402 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
1403 CreateJsepTransportController(config);
1404 auto local_offer = std::make_unique<cricket::SessionDescription>();
1405 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1406 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1407 nullptr);
1408 EXPECT_TRUE(transport_controller_
1409 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1410 .ok());
1411
1412 auto remote_answer = std::make_unique<cricket::SessionDescription>();
1413 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1414 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1415 nullptr);
1416 // Applying a non-RTCP-mux answer is expected to fail.
1417 remote_answer->contents()[0].media_description()->set_rtcp_mux(false);
1418 EXPECT_FALSE(transport_controller_
1419 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1420 .ok());
1421 }
1422
1423 // This tests that the BUNDLE group in answer should be a subset of the offered
1424 // group.
TEST_F(JsepTransportControllerTest,AddContentToBundleGroupInAnswerNotSupported)1425 TEST_F(JsepTransportControllerTest,
1426 AddContentToBundleGroupInAnswerNotSupported) {
1427 CreateJsepTransportController(JsepTransportController::Config());
1428 auto local_offer = CreateSessionDescriptionWithoutBundle();
1429 auto remote_answer = CreateSessionDescriptionWithoutBundle();
1430
1431 cricket::ContentGroup offer_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1432 offer_bundle_group.AddContentName(kAudioMid1);
1433 local_offer->AddGroup(offer_bundle_group);
1434
1435 cricket::ContentGroup answer_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1436 answer_bundle_group.AddContentName(kAudioMid1);
1437 answer_bundle_group.AddContentName(kVideoMid1);
1438 remote_answer->AddGroup(answer_bundle_group);
1439 EXPECT_TRUE(transport_controller_
1440 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1441 .ok());
1442 EXPECT_FALSE(transport_controller_
1443 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1444 .ok());
1445 }
1446
1447 // This tests that the BUNDLE group with non-existing MID should be rejectd.
TEST_F(JsepTransportControllerTest,RejectBundleGroupWithNonExistingMid)1448 TEST_F(JsepTransportControllerTest, RejectBundleGroupWithNonExistingMid) {
1449 CreateJsepTransportController(JsepTransportController::Config());
1450 auto local_offer = CreateSessionDescriptionWithoutBundle();
1451 auto remote_answer = CreateSessionDescriptionWithoutBundle();
1452
1453 cricket::ContentGroup invalid_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1454 // The BUNDLE group is invalid because there is no data section in the
1455 // description.
1456 invalid_bundle_group.AddContentName(kDataMid1);
1457 local_offer->AddGroup(invalid_bundle_group);
1458 remote_answer->AddGroup(invalid_bundle_group);
1459
1460 EXPECT_FALSE(transport_controller_
1461 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1462 .ok());
1463 EXPECT_FALSE(transport_controller_
1464 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1465 .ok());
1466 }
1467
1468 // This tests that an answer shouldn't be able to remove an m= section from an
1469 // established group without rejecting it.
TEST_F(JsepTransportControllerTest,RemoveContentFromBundleGroup)1470 TEST_F(JsepTransportControllerTest, RemoveContentFromBundleGroup) {
1471 CreateJsepTransportController(JsepTransportController::Config());
1472
1473 auto local_offer = CreateSessionDescriptionWithBundleGroup();
1474 auto remote_answer = CreateSessionDescriptionWithBundleGroup();
1475 EXPECT_TRUE(transport_controller_
1476 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1477 .ok());
1478 EXPECT_TRUE(transport_controller_
1479 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1480 .ok());
1481
1482 // Do an re-offer/answer.
1483 EXPECT_TRUE(transport_controller_
1484 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1485 .ok());
1486 auto new_answer = CreateSessionDescriptionWithoutBundle();
1487 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1488 // The answer removes video from the BUNDLE group without rejecting it is
1489 // invalid.
1490 new_bundle_group.AddContentName(kAudioMid1);
1491 new_answer->AddGroup(new_bundle_group);
1492
1493 // Applying invalid answer is expected to fail.
1494 EXPECT_FALSE(transport_controller_
1495 ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
1496 .ok());
1497
1498 // Rejected the video content.
1499 auto video_content = new_answer->GetContentByName(kVideoMid1);
1500 ASSERT_TRUE(video_content);
1501 video_content->rejected = true;
1502 EXPECT_TRUE(transport_controller_
1503 ->SetRemoteDescription(SdpType::kAnswer, new_answer.get())
1504 .ok());
1505 }
1506
1507 // Test that the JsepTransportController can process a new local and remote
1508 // description that changes the tagged BUNDLE group with the max-bundle policy
1509 // specified.
1510 // This is a regression test for bugs.webrtc.org/9954
TEST_F(JsepTransportControllerTest,ChangeTaggedMediaSectionMaxBundle)1511 TEST_F(JsepTransportControllerTest, ChangeTaggedMediaSectionMaxBundle) {
1512 CreateJsepTransportController(JsepTransportController::Config());
1513
1514 auto local_offer = std::make_unique<cricket::SessionDescription>();
1515 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1516 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1517 nullptr);
1518 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1519 bundle_group.AddContentName(kAudioMid1);
1520 local_offer->AddGroup(bundle_group);
1521 EXPECT_TRUE(transport_controller_
1522 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1523 .ok());
1524
1525 std::unique_ptr<cricket::SessionDescription> remote_answer(
1526 local_offer->Clone());
1527 EXPECT_TRUE(transport_controller_
1528 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1529 .ok());
1530
1531 std::unique_ptr<cricket::SessionDescription> local_reoffer(
1532 local_offer->Clone());
1533 local_reoffer->contents()[0].rejected = true;
1534 AddVideoSection(local_reoffer.get(), kVideoMid1, kIceUfrag1, kIcePwd1,
1535 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1536 nullptr);
1537 local_reoffer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1538 cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
1539 new_bundle_group.AddContentName(kVideoMid1);
1540 local_reoffer->AddGroup(new_bundle_group);
1541
1542 EXPECT_TRUE(transport_controller_
1543 ->SetLocalDescription(SdpType::kOffer, local_reoffer.get())
1544 .ok());
1545
1546 std::unique_ptr<cricket::SessionDescription> remote_reanswer(
1547 local_reoffer->Clone());
1548 EXPECT_TRUE(
1549 transport_controller_
1550 ->SetRemoteDescription(SdpType::kAnswer, remote_reanswer.get())
1551 .ok());
1552 }
1553
1554 } // namespace webrtc
1555