blob: 73af35d18cc6c3b6c997a95a9a66340b67add34f [file] [log] [blame]
Zhi Huange818b6e2018-02-22 15:26:27 -08001/*
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 <map>
12#include <memory>
13
14#include "p2p/base/fakedtlstransport.h"
15#include "p2p/base/fakeicetransport.h"
16#include "p2p/base/transportfactoryinterface.h"
17#include "p2p/base/transportinfo.h"
18#include "pc/jseptransportcontroller.h"
19#include "rtc_base/gunit.h"
20#include "rtc_base/ptr_util.h"
21#include "rtc_base/thread.h"
22#include "test/gtest.h"
23
24using cricket::FakeDtlsTransport;
25using cricket::Candidate;
26using cricket::Candidates;
27using webrtc::SdpType;
28
29static const int kTimeout = 100;
30static const char kIceUfrag1[] = "u0001";
31static const char kIcePwd1[] = "TESTICEPWD00000000000001";
32static const char kIceUfrag2[] = "u0002";
33static const char kIcePwd2[] = "TESTICEPWD00000000000002";
34static const char kIceUfrag3[] = "u0003";
35static const char kIcePwd3[] = "TESTICEPWD00000000000003";
36static const char kAudioMid1[] = "audio1";
37static const char kAudioMid2[] = "audio2";
38static const char kVideoMid1[] = "video1";
39static const char kVideoMid2[] = "video2";
40static const char kDataMid1[] = "data1";
41
42namespace webrtc {
43
44class FakeTransportFactory : public cricket::TransportFactoryInterface {
45 public:
46 std::unique_ptr<cricket::IceTransportInternal> CreateIceTransport(
47 const std::string& transport_name,
48 int component) override {
49 return rtc::MakeUnique<cricket::FakeIceTransport>(transport_name,
50 component);
51 }
52
53 std::unique_ptr<cricket::DtlsTransportInternal> CreateDtlsTransport(
54 std::unique_ptr<cricket::IceTransportInternal> ice,
55 const rtc::CryptoOptions& crypto_options) override {
56 std::unique_ptr<cricket::FakeIceTransport> fake_ice(
57 static_cast<cricket::FakeIceTransport*>(ice.release()));
58 return rtc::MakeUnique<FakeDtlsTransport>(std::move(fake_ice));
59 }
60};
61
62class JsepTransportControllerTest : public testing::Test,
63 public sigslot::has_slots<> {
64 public:
65 JsepTransportControllerTest() : signaling_thread_(rtc::Thread::Current()) {
66 fake_transport_factory_ = rtc::MakeUnique<FakeTransportFactory>();
67 }
68
69 void CreateJsepTransportController(
70 JsepTransportController::Config config,
71 rtc::Thread* signaling_thread = rtc::Thread::Current(),
72 rtc::Thread* network_thread = rtc::Thread::Current(),
73 cricket::PortAllocator* port_allocator = nullptr) {
74 // The tests only works with |fake_transport_factory|;
75 config.external_transport_factory = fake_transport_factory_.get();
76 transport_controller_ = rtc::MakeUnique<JsepTransportController>(
77 signaling_thread, network_thread, port_allocator, config);
78 ConnectTransportControllerSignals();
79 }
80
81 void ConnectTransportControllerSignals() {
82 transport_controller_->SignalIceConnectionState.connect(
83 this, &JsepTransportControllerTest::OnConnectionState);
84 transport_controller_->SignalIceGatheringState.connect(
85 this, &JsepTransportControllerTest::OnGatheringState);
86 transport_controller_->SignalIceCandidatesGathered.connect(
87 this, &JsepTransportControllerTest::OnCandidatesGathered);
88 transport_controller_->SignalRtpTransportChanged.connect(
89 this, &JsepTransportControllerTest::OnRtpTransportChanged);
90 transport_controller_->SignalDtlsTransportChanged.connect(
91 this, &JsepTransportControllerTest::OnDtlsTransportChanged);
92 }
93
94 std::unique_ptr<cricket::SessionDescription>
95 CreateSessionDescriptionWithoutBundle() {
96 auto description = rtc::MakeUnique<cricket::SessionDescription>();
97 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
98 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
99 nullptr);
100 AddVideoSection(description.get(), kVideoMid1, kIceUfrag1, kIcePwd1,
101 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
102 nullptr);
103 return description;
104 }
105
106 std::unique_ptr<cricket::SessionDescription>
107 CreateSessionDescriptionWithBundleGroup() {
108 auto description = CreateSessionDescriptionWithoutBundle();
109 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
110 bundle_group.AddContentName(kAudioMid1);
111 bundle_group.AddContentName(kVideoMid1);
112 description->AddGroup(bundle_group);
113
114 return description;
115 }
116
117 void AddAudioSection(cricket::SessionDescription* description,
118 const std::string& mid,
119 const std::string& ufrag,
120 const std::string& pwd,
121 cricket::IceMode ice_mode,
122 cricket::ConnectionRole conn_role,
123 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
124 std::unique_ptr<cricket::AudioContentDescription> audio(
125 new cricket::AudioContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700126 // Set RTCP-mux to be true because the default policy is "mux required".
127 audio->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800128 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
129 /*rejected=*/false, audio.release());
130 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
131 }
132
133 void AddVideoSection(cricket::SessionDescription* description,
134 const std::string& mid,
135 const std::string& ufrag,
136 const std::string& pwd,
137 cricket::IceMode ice_mode,
138 cricket::ConnectionRole conn_role,
139 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
140 std::unique_ptr<cricket::VideoContentDescription> video(
141 new cricket::VideoContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700142 // Set RTCP-mux to be true because the default policy is "mux required".
143 video->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800144 description->AddContent(mid, cricket::MediaProtocolType::kRtp,
145 /*rejected=*/false, video.release());
146 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
147 }
148
149 void AddDataSection(cricket::SessionDescription* description,
150 const std::string& mid,
151 cricket::MediaProtocolType protocol_type,
152 const std::string& ufrag,
153 const std::string& pwd,
154 cricket::IceMode ice_mode,
155 cricket::ConnectionRole conn_role,
156 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
157 std::unique_ptr<cricket::DataContentDescription> data(
158 new cricket::DataContentDescription());
Zhi Huange830e682018-03-30 10:48:35 -0700159 data->set_rtcp_mux(true);
Zhi Huange818b6e2018-02-22 15:26:27 -0800160 description->AddContent(mid, protocol_type,
161 /*rejected=*/false, data.release());
162 AddTransportInfo(description, mid, ufrag, pwd, ice_mode, conn_role, cert);
163 }
164
165 void AddTransportInfo(cricket::SessionDescription* description,
166 const std::string& mid,
167 const std::string& ufrag,
168 const std::string& pwd,
169 cricket::IceMode ice_mode,
170 cricket::ConnectionRole conn_role,
171 rtc::scoped_refptr<rtc::RTCCertificate> cert) {
172 std::unique_ptr<rtc::SSLFingerprint> fingerprint;
173 if (cert) {
174 fingerprint.reset(rtc::SSLFingerprint::CreateFromCertificate(cert));
175 }
176
177 cricket::TransportDescription transport_desc(std::vector<std::string>(),
178 ufrag, pwd, ice_mode,
179 conn_role, fingerprint.get());
180 description->AddTransportInfo(cricket::TransportInfo(mid, transport_desc));
181 }
182
183 cricket::IceConfig CreateIceConfig(
184 int receiving_timeout,
185 cricket::ContinualGatheringPolicy continual_gathering_policy) {
186 cricket::IceConfig config;
187 config.receiving_timeout = receiving_timeout;
188 config.continual_gathering_policy = continual_gathering_policy;
189 return config;
190 }
191
192 Candidate CreateCandidate(const std::string& transport_name, int component) {
193 Candidate c;
194 c.set_transport_name(transport_name);
195 c.set_address(rtc::SocketAddress("192.168.1.1", 8000));
196 c.set_component(component);
197 c.set_protocol(cricket::UDP_PROTOCOL_NAME);
198 c.set_priority(1);
199 return c;
200 }
201
202 void CreateLocalDescriptionAndCompleteConnectionOnNetworkThread() {
203 if (!network_thread_->IsCurrent()) {
204 network_thread_->Invoke<void>(RTC_FROM_HERE, [&] {
205 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
206 });
207 return;
208 }
209
210 auto description = CreateSessionDescriptionWithBundleGroup();
211 EXPECT_TRUE(transport_controller_
212 ->SetLocalDescription(SdpType::kOffer, description.get())
213 .ok());
214
215 transport_controller_->MaybeStartGathering();
216 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
217 transport_controller_->GetDtlsTransport(kAudioMid1));
218 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
219 transport_controller_->GetDtlsTransport(kVideoMid1));
220 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
221 fake_audio_dtls->fake_ice_transport(),
222 CreateCandidate(kAudioMid1, /*component=*/1));
223 fake_video_dtls->fake_ice_transport()->SignalCandidateGathered(
224 fake_video_dtls->fake_ice_transport(),
225 CreateCandidate(kVideoMid1, /*component=*/1));
226 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
227 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
228 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(2);
229 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
230 fake_audio_dtls->SetReceiving(true);
231 fake_video_dtls->SetReceiving(true);
232 fake_audio_dtls->SetWritable(true);
233 fake_video_dtls->SetWritable(true);
234 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
235 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
236 }
237
238 protected:
239 void OnConnectionState(cricket::IceConnectionState state) {
240 if (!signaling_thread_->IsCurrent()) {
241 signaled_on_non_signaling_thread_ = true;
242 }
243 connection_state_ = state;
244 ++connection_state_signal_count_;
245 }
246
247 void OnGatheringState(cricket::IceGatheringState state) {
248 if (!signaling_thread_->IsCurrent()) {
249 signaled_on_non_signaling_thread_ = true;
250 }
251 gathering_state_ = state;
252 ++gathering_state_signal_count_;
253 }
254
255 void OnCandidatesGathered(const std::string& transport_name,
256 const Candidates& candidates) {
257 if (!signaling_thread_->IsCurrent()) {
258 signaled_on_non_signaling_thread_ = true;
259 }
260 candidates_[transport_name].insert(candidates_[transport_name].end(),
261 candidates.begin(), candidates.end());
262 ++candidates_signal_count_;
263 }
264
265 void OnRtpTransportChanged(const std::string& mid,
266 RtpTransportInternal* rtp_transport) {
267 changed_rtp_transport_by_mid_[mid] = rtp_transport;
268 }
269
270 void OnDtlsTransportChanged(const std::string& mid,
271 cricket::DtlsTransportInternal* dtls_transport) {
272 changed_dtls_transport_by_mid_[mid] = dtls_transport;
273 }
274
275 // Information received from signals from transport controller.
276 cricket::IceConnectionState connection_state_ =
277 cricket::kIceConnectionConnecting;
278 bool receiving_ = false;
279 cricket::IceGatheringState gathering_state_ = cricket::kIceGatheringNew;
280 // transport_name => candidates
281 std::map<std::string, Candidates> candidates_;
282 // Counts of each signal emitted.
283 int connection_state_signal_count_ = 0;
284 int receiving_signal_count_ = 0;
285 int gathering_state_signal_count_ = 0;
286 int candidates_signal_count_ = 0;
287
288 // |network_thread_| should be destroyed after |transport_controller_|
289 std::unique_ptr<rtc::Thread> network_thread_;
290 std::unique_ptr<JsepTransportController> transport_controller_;
291 std::unique_ptr<FakeTransportFactory> fake_transport_factory_;
292 rtc::Thread* const signaling_thread_ = nullptr;
293 bool signaled_on_non_signaling_thread_ = false;
294 // Used to verify the SignalRtpTransportChanged/SignalDtlsTransportChanged are
295 // signaled correctly.
296 std::map<std::string, RtpTransportInternal*> changed_rtp_transport_by_mid_;
297 std::map<std::string, cricket::DtlsTransportInternal*>
298 changed_dtls_transport_by_mid_;
299};
300
301TEST_F(JsepTransportControllerTest, GetRtpTransport) {
302 CreateJsepTransportController(JsepTransportController::Config());
303 auto description = CreateSessionDescriptionWithoutBundle();
304 EXPECT_TRUE(transport_controller_
305 ->SetLocalDescription(SdpType::kOffer, description.get())
306 .ok());
307 auto audio_rtp_transport = transport_controller_->GetRtpTransport(kAudioMid1);
308 auto video_rtp_transport = transport_controller_->GetRtpTransport(kVideoMid1);
309 EXPECT_NE(nullptr, audio_rtp_transport);
310 EXPECT_NE(nullptr, video_rtp_transport);
311 EXPECT_NE(audio_rtp_transport, video_rtp_transport);
312 // Return nullptr for non-existing ones.
313 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid2));
314}
315
316TEST_F(JsepTransportControllerTest, GetDtlsTransport) {
317 JsepTransportController::Config config;
318 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyNegotiate;
319 CreateJsepTransportController(config);
320 auto description = CreateSessionDescriptionWithoutBundle();
321 EXPECT_TRUE(transport_controller_
322 ->SetLocalDescription(SdpType::kOffer, description.get())
323 .ok());
324 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
325 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
326 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
327 EXPECT_NE(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
328 // Return nullptr for non-existing ones.
329 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kVideoMid2));
330 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid2));
331}
332
Zhi Huange830e682018-03-30 10:48:35 -0700333TEST_F(JsepTransportControllerTest, GetDtlsTransportWithRtcpMux) {
334 JsepTransportController::Config config;
335 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
336 CreateJsepTransportController(config);
337 auto description = CreateSessionDescriptionWithoutBundle();
338 EXPECT_TRUE(transport_controller_
339 ->SetLocalDescription(SdpType::kOffer, description.get())
340 .ok());
341 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kAudioMid1));
342 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kAudioMid1));
343 EXPECT_NE(nullptr, transport_controller_->GetDtlsTransport(kVideoMid1));
344 EXPECT_EQ(nullptr, transport_controller_->GetRtcpDtlsTransport(kVideoMid1));
345}
346
Zhi Huange818b6e2018-02-22 15:26:27 -0800347TEST_F(JsepTransportControllerTest, SetIceConfig) {
348 CreateJsepTransportController(JsepTransportController::Config());
349 auto description = CreateSessionDescriptionWithoutBundle();
350 EXPECT_TRUE(transport_controller_
351 ->SetLocalDescription(SdpType::kOffer, description.get())
352 .ok());
353
354 transport_controller_->SetIceConfig(
355 CreateIceConfig(kTimeout, cricket::GATHER_CONTINUALLY));
356 FakeDtlsTransport* fake_audio_dtls = static_cast<FakeDtlsTransport*>(
357 transport_controller_->GetDtlsTransport(kAudioMid1));
358 ASSERT_NE(nullptr, fake_audio_dtls);
359 EXPECT_EQ(kTimeout,
360 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
361 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
362
363 // Test that value stored in controller is applied to new transports.
364 AddAudioSection(description.get(), kAudioMid2, kIceUfrag1, kIcePwd1,
365 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
366 nullptr);
367
368 EXPECT_TRUE(transport_controller_
369 ->SetLocalDescription(SdpType::kOffer, description.get())
370 .ok());
371 fake_audio_dtls = static_cast<FakeDtlsTransport*>(
372 transport_controller_->GetDtlsTransport(kAudioMid2));
373 ASSERT_NE(nullptr, fake_audio_dtls);
374 EXPECT_EQ(kTimeout,
375 fake_audio_dtls->fake_ice_transport()->receiving_timeout());
376 EXPECT_TRUE(fake_audio_dtls->fake_ice_transport()->gather_continually());
377}
378
379// Tests the getter and setter of the ICE restart flag.
380TEST_F(JsepTransportControllerTest, NeedIceRestart) {
381 CreateJsepTransportController(JsepTransportController::Config());
382 auto description = CreateSessionDescriptionWithoutBundle();
383 EXPECT_TRUE(transport_controller_
384 ->SetLocalDescription(SdpType::kOffer, description.get())
385 .ok());
386 EXPECT_TRUE(transport_controller_
387 ->SetRemoteDescription(SdpType::kAnswer, description.get())
388 .ok());
389
390 // Initially NeedsIceRestart should return false.
391 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
392 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid1));
393 // Set the needs-ice-restart flag and verify NeedsIceRestart starts returning
394 // true.
395 transport_controller_->SetNeedsIceRestartFlag();
396 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kAudioMid1));
397 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
398 // For a nonexistent transport, false should be returned.
399 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kVideoMid2));
400
401 // Reset the ice_ufrag/ice_pwd for audio.
402 auto audio_transport_info = description->GetTransportInfoByName(kAudioMid1);
403 audio_transport_info->description.ice_ufrag = kIceUfrag2;
404 audio_transport_info->description.ice_pwd = kIcePwd2;
405 EXPECT_TRUE(transport_controller_
406 ->SetLocalDescription(SdpType::kOffer, description.get())
407 .ok());
408 // Because the ICE is only restarted for audio, NeedsIceRestart is expected to
409 // return false for audio and true for video.
410 EXPECT_FALSE(transport_controller_->NeedsIceRestart(kAudioMid1));
411 EXPECT_TRUE(transport_controller_->NeedsIceRestart(kVideoMid1));
412}
413
414TEST_F(JsepTransportControllerTest, MaybeStartGathering) {
415 CreateJsepTransportController(JsepTransportController::Config());
416 auto description = CreateSessionDescriptionWithBundleGroup();
417 EXPECT_TRUE(transport_controller_
418 ->SetLocalDescription(SdpType::kOffer, description.get())
419 .ok());
420 // After setting the local description, we should be able to start gathering
421 // candidates.
422 transport_controller_->MaybeStartGathering();
423 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
424 EXPECT_EQ(1, gathering_state_signal_count_);
425}
426
427TEST_F(JsepTransportControllerTest, AddRemoveRemoteCandidates) {
428 CreateJsepTransportController(JsepTransportController::Config());
429 auto description = CreateSessionDescriptionWithoutBundle();
430 transport_controller_->SetLocalDescription(SdpType::kOffer,
431 description.get());
432 transport_controller_->SetRemoteDescription(SdpType::kAnswer,
433 description.get());
434 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
435 transport_controller_->GetDtlsTransport(kAudioMid1));
436 ASSERT_NE(nullptr, fake_audio_dtls);
437 Candidates candidates;
438 candidates.push_back(
439 CreateCandidate(kAudioMid1, cricket::ICE_CANDIDATE_COMPONENT_RTP));
440 EXPECT_TRUE(
441 transport_controller_->AddRemoteCandidates(kAudioMid1, candidates).ok());
442 EXPECT_EQ(1U,
443 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
444
445 EXPECT_TRUE(transport_controller_->RemoveRemoteCandidates(candidates).ok());
446 EXPECT_EQ(0U,
447 fake_audio_dtls->fake_ice_transport()->remote_candidates().size());
448}
449
450TEST_F(JsepTransportControllerTest, SetAndGetLocalCertificate) {
451 CreateJsepTransportController(JsepTransportController::Config());
452
453 rtc::scoped_refptr<rtc::RTCCertificate> certificate1 =
454 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
455 rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT)));
456 rtc::scoped_refptr<rtc::RTCCertificate> returned_certificate;
457
458 auto description = rtc::MakeUnique<cricket::SessionDescription>();
459 AddAudioSection(description.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
460 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
461 certificate1);
462
463 // Apply the local certificate.
464 EXPECT_TRUE(transport_controller_->SetLocalCertificate(certificate1));
465 // Apply the local description.
466 EXPECT_TRUE(transport_controller_
467 ->SetLocalDescription(SdpType::kOffer, description.get())
468 .ok());
469 returned_certificate = transport_controller_->GetLocalCertificate(kAudioMid1);
470 EXPECT_TRUE(returned_certificate);
471 EXPECT_EQ(certificate1->identity()->certificate().ToPEMString(),
472 returned_certificate->identity()->certificate().ToPEMString());
473
474 // Should fail if called for a nonexistant transport.
475 EXPECT_EQ(nullptr, transport_controller_->GetLocalCertificate(kVideoMid1));
476
477 // Shouldn't be able to change the identity once set.
478 rtc::scoped_refptr<rtc::RTCCertificate> certificate2 =
479 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
480 rtc::SSLIdentity::Generate("session2", rtc::KT_DEFAULT)));
481 EXPECT_FALSE(transport_controller_->SetLocalCertificate(certificate2));
482}
483
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800484TEST_F(JsepTransportControllerTest, GetRemoteSSLCertChain) {
Zhi Huange818b6e2018-02-22 15:26:27 -0800485 CreateJsepTransportController(JsepTransportController::Config());
486 auto description = CreateSessionDescriptionWithBundleGroup();
487 EXPECT_TRUE(transport_controller_
488 ->SetLocalDescription(SdpType::kOffer, description.get())
489 .ok());
490 rtc::FakeSSLCertificate fake_certificate("fake_data");
491
492 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
493 transport_controller_->GetDtlsTransport(kAudioMid1));
494 fake_audio_dtls->SetRemoteSSLCertificate(&fake_certificate);
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800495 std::unique_ptr<rtc::SSLCertChain> returned_cert_chain =
496 transport_controller_->GetRemoteSSLCertChain(kAudioMid1);
497 ASSERT_TRUE(returned_cert_chain);
498 ASSERT_EQ(1u, returned_cert_chain->GetSize());
Zhi Huange818b6e2018-02-22 15:26:27 -0800499 EXPECT_EQ(fake_certificate.ToPEMString(),
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800500 returned_cert_chain->Get(0).ToPEMString());
Zhi Huange818b6e2018-02-22 15:26:27 -0800501
502 // Should fail if called for a nonexistant transport.
Taylor Brandstetterc3928662018-02-23 13:04:51 -0800503 EXPECT_FALSE(transport_controller_->GetRemoteSSLCertChain(kAudioMid2));
Zhi Huange818b6e2018-02-22 15:26:27 -0800504}
505
506TEST_F(JsepTransportControllerTest, GetDtlsRole) {
507 CreateJsepTransportController(JsepTransportController::Config());
508 auto offer_certificate =
509 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
510 rtc::SSLIdentity::Generate("offer", rtc::KT_DEFAULT)));
511 auto answer_certificate =
512 rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
513 rtc::SSLIdentity::Generate("answer", rtc::KT_DEFAULT)));
514 transport_controller_->SetLocalCertificate(offer_certificate);
515
516 auto offer_desc = rtc::MakeUnique<cricket::SessionDescription>();
517 AddAudioSection(offer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
518 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
519 offer_certificate);
520 auto answer_desc = rtc::MakeUnique<cricket::SessionDescription>();
521 AddAudioSection(answer_desc.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
522 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
523 answer_certificate);
524
525 EXPECT_TRUE(transport_controller_
526 ->SetLocalDescription(SdpType::kOffer, offer_desc.get())
527 .ok());
528
529 rtc::Optional<rtc::SSLRole> role =
530 transport_controller_->GetDtlsRole(kAudioMid1);
531 // The DTLS role is not decided yet.
532 EXPECT_FALSE(role);
533 EXPECT_TRUE(transport_controller_
534 ->SetRemoteDescription(SdpType::kAnswer, answer_desc.get())
535 .ok());
536 role = transport_controller_->GetDtlsRole(kAudioMid1);
537
538 ASSERT_TRUE(role);
539 EXPECT_EQ(rtc::SSL_CLIENT, *role);
540}
541
542TEST_F(JsepTransportControllerTest, GetStats) {
543 CreateJsepTransportController(JsepTransportController::Config());
544 auto description = CreateSessionDescriptionWithBundleGroup();
545 EXPECT_TRUE(transport_controller_
546 ->SetLocalDescription(SdpType::kOffer, description.get())
547 .ok());
548
549 cricket::TransportStats stats;
550 EXPECT_TRUE(transport_controller_->GetStats(kAudioMid1, &stats));
551 EXPECT_EQ(kAudioMid1, stats.transport_name);
552 EXPECT_EQ(1u, stats.channel_stats.size());
553 // Return false for non-existing transport.
554 EXPECT_FALSE(transport_controller_->GetStats(kAudioMid2, &stats));
555}
556
557TEST_F(JsepTransportControllerTest, SignalConnectionStateFailed) {
558 CreateJsepTransportController(JsepTransportController::Config());
559 auto description = CreateSessionDescriptionWithoutBundle();
560 EXPECT_TRUE(transport_controller_
561 ->SetLocalDescription(SdpType::kOffer, description.get())
562 .ok());
563
564 auto fake_ice = static_cast<cricket::FakeIceTransport*>(
565 transport_controller_->GetDtlsTransport(kAudioMid1)->ice_transport());
566 fake_ice->SetCandidatesGatheringComplete();
567 fake_ice->SetConnectionCount(1);
568 // The connection stats will be failed if there is no active connection.
569 fake_ice->SetConnectionCount(0);
570 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
571 EXPECT_EQ(1, connection_state_signal_count_);
572}
573
574TEST_F(JsepTransportControllerTest, SignalConnectionStateConnected) {
575 CreateJsepTransportController(JsepTransportController::Config());
576 auto description = CreateSessionDescriptionWithoutBundle();
577 EXPECT_TRUE(transport_controller_
578 ->SetLocalDescription(SdpType::kOffer, description.get())
579 .ok());
580
581 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
582 transport_controller_->GetDtlsTransport(kAudioMid1));
583 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
584 transport_controller_->GetDtlsTransport(kVideoMid1));
585
586 // First, have one transport connect, and another fail, to ensure that
587 // the first transport connecting didn't trigger a "connected" state signal.
588 // We should only get a signal when all are connected.
589 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
590 fake_audio_dtls->SetWritable(true);
591 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
592 // Decrease the number of the connection to trigger the signal.
593 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
594 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
595 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
596
597 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
598 EXPECT_EQ(1, connection_state_signal_count_);
599
600 // Set the connection count to be 2 and the cricket::FakeIceTransport will set
601 // the transport state to be STATE_CONNECTING.
602 fake_video_dtls->fake_ice_transport()->SetConnectionCount(2);
603 fake_video_dtls->SetWritable(true);
604 EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
605 EXPECT_EQ(2, connection_state_signal_count_);
606}
607
608TEST_F(JsepTransportControllerTest, SignalConnectionStateComplete) {
609 CreateJsepTransportController(JsepTransportController::Config());
610 auto description = CreateSessionDescriptionWithoutBundle();
611 EXPECT_TRUE(transport_controller_
612 ->SetLocalDescription(SdpType::kOffer, description.get())
613 .ok());
614
615 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
616 transport_controller_->GetDtlsTransport(kAudioMid1));
617 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
618 transport_controller_->GetDtlsTransport(kVideoMid1));
619
620 // First, have one transport connect, and another fail, to ensure that
621 // the first transport connecting didn't trigger a "connected" state signal.
622 // We should only get a signal when all are connected.
623 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
624 fake_audio_dtls->SetWritable(true);
625 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
626 // Decrease the number of the connection to trigger the signal.
627 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
628 fake_video_dtls->fake_ice_transport()->SetConnectionCount(0);
629 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
630
631 EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
632 EXPECT_EQ(1, connection_state_signal_count_);
633
634 // Set the connection count to be 1 and the cricket::FakeIceTransport will set
635 // the transport state to be STATE_COMPLETED.
636 fake_video_dtls->fake_ice_transport()->SetConnectionCount(1);
637 fake_video_dtls->SetWritable(true);
638 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
639 EXPECT_EQ(2, connection_state_signal_count_);
640}
641
642TEST_F(JsepTransportControllerTest, SignalIceGatheringStateGathering) {
643 CreateJsepTransportController(JsepTransportController::Config());
644 auto description = CreateSessionDescriptionWithoutBundle();
645 EXPECT_TRUE(transport_controller_
646 ->SetLocalDescription(SdpType::kOffer, description.get())
647 .ok());
648
649 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
650 transport_controller_->GetDtlsTransport(kAudioMid1));
651 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
652 // Should be in the gathering state as soon as any transport starts gathering.
653 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
654 EXPECT_EQ(1, gathering_state_signal_count_);
655}
656
657TEST_F(JsepTransportControllerTest, SignalIceGatheringStateComplete) {
658 CreateJsepTransportController(JsepTransportController::Config());
659 auto description = CreateSessionDescriptionWithoutBundle();
660 EXPECT_TRUE(transport_controller_
661 ->SetLocalDescription(SdpType::kOffer, description.get())
662 .ok());
663
664 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
665 transport_controller_->GetDtlsTransport(kAudioMid1));
666 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
667 transport_controller_->GetDtlsTransport(kVideoMid1));
668
669 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
670 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
671 EXPECT_EQ(1, gathering_state_signal_count_);
672
673 // Have one transport finish gathering, to make sure gathering
674 // completion wasn't signalled if only one transport finished gathering.
675 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
676 EXPECT_EQ(1, gathering_state_signal_count_);
677
678 fake_video_dtls->fake_ice_transport()->MaybeStartGathering();
679 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
680 EXPECT_EQ(1, gathering_state_signal_count_);
681
682 fake_video_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
683 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
684 EXPECT_EQ(2, gathering_state_signal_count_);
685}
686
687// Test that when the last transport that hasn't finished connecting and/or
688// gathering is destroyed, the aggregate state jumps to "completed". This can
689// happen if, for example, we have an audio and video transport, the audio
690// transport completes, then we start bundling video on the audio transport.
691TEST_F(JsepTransportControllerTest,
692 SignalingWhenLastIncompleteTransportDestroyed) {
693 CreateJsepTransportController(JsepTransportController::Config());
694 auto description = CreateSessionDescriptionWithBundleGroup();
695 EXPECT_TRUE(transport_controller_
696 ->SetLocalDescription(SdpType::kOffer, description.get())
697 .ok());
698
699 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
700 transport_controller_->GetDtlsTransport(kAudioMid1));
701 auto fake_video_dtls = static_cast<FakeDtlsTransport*>(
702 transport_controller_->GetDtlsTransport(kVideoMid1));
703 EXPECT_NE(fake_audio_dtls, fake_video_dtls);
704
705 fake_audio_dtls->fake_ice_transport()->MaybeStartGathering();
706 EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
707 EXPECT_EQ(1, gathering_state_signal_count_);
708
709 // Let the audio transport complete.
710 fake_audio_dtls->SetWritable(true);
711 fake_audio_dtls->fake_ice_transport()->SetCandidatesGatheringComplete();
712 fake_audio_dtls->fake_ice_transport()->SetConnectionCount(1);
713 EXPECT_EQ(1, gathering_state_signal_count_);
714
715 // Set the remote description and enable the bundle.
716 EXPECT_TRUE(transport_controller_
717 ->SetRemoteDescription(SdpType::kAnswer, description.get())
718 .ok());
719 // The BUNDLE should be enabled, the incomplete video transport should be
720 // deleted and the states shoud be updated.
721 fake_video_dtls = static_cast<FakeDtlsTransport*>(
722 transport_controller_->GetDtlsTransport(kVideoMid1));
723 EXPECT_EQ(fake_audio_dtls, fake_video_dtls);
724 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
725 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
726 EXPECT_EQ(2, gathering_state_signal_count_);
727}
728
729TEST_F(JsepTransportControllerTest, SignalCandidatesGathered) {
730 CreateJsepTransportController(JsepTransportController::Config());
731 auto description = CreateSessionDescriptionWithBundleGroup();
732 EXPECT_TRUE(transport_controller_
733 ->SetLocalDescription(SdpType::kOffer, description.get())
734 .ok());
735 transport_controller_->MaybeStartGathering();
736
737 auto fake_audio_dtls = static_cast<FakeDtlsTransport*>(
738 transport_controller_->GetDtlsTransport(kAudioMid1));
739 fake_audio_dtls->fake_ice_transport()->SignalCandidateGathered(
740 fake_audio_dtls->fake_ice_transport(), CreateCandidate(kAudioMid1, 1));
741 EXPECT_EQ_WAIT(1, candidates_signal_count_, kTimeout);
742 EXPECT_EQ(1u, candidates_[kAudioMid1].size());
743}
744
745TEST_F(JsepTransportControllerTest, IceSignalingOccursOnSignalingThread) {
746 network_thread_ = rtc::Thread::CreateWithSocketServer();
747 network_thread_->Start();
748 CreateJsepTransportController(JsepTransportController::Config(),
749 signaling_thread_, network_thread_.get(),
750 /*PortAllocator=*/nullptr);
751 CreateLocalDescriptionAndCompleteConnectionOnNetworkThread();
752
753 // connecting --> connected --> completed
754 EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
755 EXPECT_EQ(2, connection_state_signal_count_);
756
757 // new --> gathering --> complete
758 EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
759 EXPECT_EQ(2, gathering_state_signal_count_);
760
761 EXPECT_EQ_WAIT(1u, candidates_[kAudioMid1].size(), kTimeout);
762 EXPECT_EQ_WAIT(1u, candidates_[kVideoMid1].size(), kTimeout);
763 EXPECT_EQ(2, candidates_signal_count_);
764
765 EXPECT_TRUE(!signaled_on_non_signaling_thread_);
766}
767
768// Older versions of Chrome expect the ICE role to be re-determined when an
769// ICE restart occurs, and also don't perform conflict resolution correctly,
770// so for now we can't safely stop doing this.
771// See: https://bugs.chromium.org/p/chromium/issues/detail?id=628676
772// TODO(deadbeef): Remove this when these old versions of Chrome reach a low
773// enough population.
774TEST_F(JsepTransportControllerTest, IceRoleRedeterminedOnIceRestartByDefault) {
775 CreateJsepTransportController(JsepTransportController::Config());
776 // Let the |transport_controller_| be the controlled side initially.
777 auto remote_offer = rtc::MakeUnique<cricket::SessionDescription>();
778 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
779 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
780 nullptr);
781 auto local_answer = rtc::MakeUnique<cricket::SessionDescription>();
782 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
783 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
784 nullptr);
785
786 EXPECT_TRUE(transport_controller_
787 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
788 .ok());
789 EXPECT_TRUE(transport_controller_
790 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
791 .ok());
792
793 auto fake_dtls = static_cast<FakeDtlsTransport*>(
794 transport_controller_->GetDtlsTransport(kAudioMid1));
795 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
796 fake_dtls->fake_ice_transport()->GetIceRole());
797
798 // New offer will trigger the ICE restart.
799 auto restart_local_offer = rtc::MakeUnique<cricket::SessionDescription>();
800 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
801 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
802 nullptr);
803 EXPECT_TRUE(
804 transport_controller_
805 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
806 .ok());
807 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
808 fake_dtls->fake_ice_transport()->GetIceRole());
809}
810
811// Test that if the TransportController was created with the
812// |redetermine_role_on_ice_restart| parameter set to false, the role is *not*
813// redetermined on an ICE restart.
814TEST_F(JsepTransportControllerTest, IceRoleNotRedetermined) {
815 JsepTransportController::Config config;
816 config.redetermine_role_on_ice_restart = false;
817
818 CreateJsepTransportController(config);
819 // Let the |transport_controller_| be the controlled side initially.
820 auto remote_offer = rtc::MakeUnique<cricket::SessionDescription>();
821 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
822 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
823 nullptr);
824 auto local_answer = rtc::MakeUnique<cricket::SessionDescription>();
825 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
826 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
827 nullptr);
828
829 EXPECT_TRUE(transport_controller_
830 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
831 .ok());
832 EXPECT_TRUE(transport_controller_
833 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
834 .ok());
835
836 auto fake_dtls = static_cast<FakeDtlsTransport*>(
837 transport_controller_->GetDtlsTransport(kAudioMid1));
838 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
839 fake_dtls->fake_ice_transport()->GetIceRole());
840
841 // New offer will trigger the ICE restart.
842 auto restart_local_offer = rtc::MakeUnique<cricket::SessionDescription>();
843 AddAudioSection(restart_local_offer.get(), kAudioMid1, kIceUfrag3, kIcePwd3,
844 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
845 nullptr);
846 EXPECT_TRUE(
847 transport_controller_
848 ->SetLocalDescription(SdpType::kOffer, restart_local_offer.get())
849 .ok());
850 EXPECT_EQ(cricket::ICEROLE_CONTROLLED,
851 fake_dtls->fake_ice_transport()->GetIceRole());
852}
853
854// Tests ICE-Lite mode in remote answer.
855TEST_F(JsepTransportControllerTest, SetIceRoleWhenIceLiteInRemoteAnswer) {
856 CreateJsepTransportController(JsepTransportController::Config());
857 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
858 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
859 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
860 nullptr);
861 EXPECT_TRUE(transport_controller_
862 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
863 .ok());
864 auto fake_dtls = static_cast<FakeDtlsTransport*>(
865 transport_controller_->GetDtlsTransport(kAudioMid1));
866 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
867 fake_dtls->fake_ice_transport()->GetIceRole());
868 EXPECT_EQ(cricket::ICEMODE_FULL,
869 fake_dtls->fake_ice_transport()->remote_ice_mode());
870
871 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
872 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
873 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_PASSIVE,
874 nullptr);
875 EXPECT_TRUE(transport_controller_
876 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
877 .ok());
878 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
879 fake_dtls->fake_ice_transport()->GetIceRole());
880 EXPECT_EQ(cricket::ICEMODE_LITE,
881 fake_dtls->fake_ice_transport()->remote_ice_mode());
882}
883
884// Tests that the ICE role remains "controlling" if a subsequent offer that
885// does an ICE restart is received from an ICE lite endpoint. Regression test
886// for: https://crbug.com/710760
887TEST_F(JsepTransportControllerTest,
888 IceRoleIsControllingAfterIceRestartFromIceLiteEndpoint) {
889 CreateJsepTransportController(JsepTransportController::Config());
890 auto remote_offer = rtc::MakeUnique<cricket::SessionDescription>();
891 AddAudioSection(remote_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
892 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
893 nullptr);
894 auto local_answer = rtc::MakeUnique<cricket::SessionDescription>();
895 AddAudioSection(local_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
896 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
897 nullptr);
898 // Initial Offer/Answer exchange. If the remote offerer is ICE-Lite, then the
899 // local side is the controlling.
900 EXPECT_TRUE(transport_controller_
901 ->SetRemoteDescription(SdpType::kOffer, remote_offer.get())
902 .ok());
903 EXPECT_TRUE(transport_controller_
904 ->SetLocalDescription(SdpType::kAnswer, local_answer.get())
905 .ok());
906 auto fake_dtls = static_cast<FakeDtlsTransport*>(
907 transport_controller_->GetDtlsTransport(kAudioMid1));
908 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
909 fake_dtls->fake_ice_transport()->GetIceRole());
910
911 // In the subsequence remote offer triggers an ICE restart.
912 auto remote_offer2 = rtc::MakeUnique<cricket::SessionDescription>();
913 AddAudioSection(remote_offer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
914 cricket::ICEMODE_LITE, cricket::CONNECTIONROLE_ACTPASS,
915 nullptr);
916 auto local_answer2 = rtc::MakeUnique<cricket::SessionDescription>();
917 AddAudioSection(local_answer2.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
918 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
919 nullptr);
920 EXPECT_TRUE(transport_controller_
921 ->SetRemoteDescription(SdpType::kOffer, remote_offer2.get())
922 .ok());
923 EXPECT_TRUE(transport_controller_
924 ->SetLocalDescription(SdpType::kAnswer, local_answer2.get())
925 .ok());
926 fake_dtls = static_cast<FakeDtlsTransport*>(
927 transport_controller_->GetDtlsTransport(kAudioMid1));
928 // The local side is still the controlling role since the remote side is using
929 // ICE-Lite.
930 EXPECT_EQ(cricket::ICEROLE_CONTROLLING,
931 fake_dtls->fake_ice_transport()->GetIceRole());
932}
933
934// Tests that the SDP has more than one audio/video m= sections.
935TEST_F(JsepTransportControllerTest, MultipleMediaSectionsOfSameTypeWithBundle) {
936 CreateJsepTransportController(JsepTransportController::Config());
937 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
938 bundle_group.AddContentName(kAudioMid1);
939 bundle_group.AddContentName(kAudioMid2);
940 bundle_group.AddContentName(kVideoMid1);
941 bundle_group.AddContentName(kDataMid1);
942
943 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
944 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
945 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
946 nullptr);
947 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
948 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
949 nullptr);
950 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
951 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
952 nullptr);
953 AddDataSection(local_offer.get(), kDataMid1,
954 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
955 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
956 nullptr);
957
958 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
959 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
960 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
961 nullptr);
962 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
963 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
964 nullptr);
965 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
966 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
967 nullptr);
968 AddDataSection(remote_answer.get(), kDataMid1,
969 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
970 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
971 nullptr);
972
973 local_offer->AddGroup(bundle_group);
974 remote_answer->AddGroup(bundle_group);
975
976 EXPECT_TRUE(transport_controller_
977 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
978 .ok());
979 EXPECT_TRUE(transport_controller_
980 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
981 .ok());
982 // Verify that all the sections are bundled on kAudio1.
983 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
984 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
985 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
986 auto transport4 = transport_controller_->GetRtpTransport(kDataMid1);
987 EXPECT_EQ(transport1, transport2);
988 EXPECT_EQ(transport1, transport3);
989 EXPECT_EQ(transport1, transport4);
990
991 // Verify the OnRtpTransport/DtlsTransportChanged signals are fired correctly.
992 auto it = changed_rtp_transport_by_mid_.find(kAudioMid2);
993 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
994 EXPECT_EQ(transport1, it->second);
995 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
996 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
997 EXPECT_EQ(transport1, it->second);
998 it = changed_rtp_transport_by_mid_.find(kVideoMid1);
999 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1000 EXPECT_EQ(transport1, it->second);
1001 // Verify the DtlsTransport for the SCTP data channel is reset correctly.
1002 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1003 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1004 EXPECT_EQ(transport1->rtp_packet_transport(), it2->second);
1005}
1006
1007// Tests that only a subset of all the m= sections are bundled.
1008TEST_F(JsepTransportControllerTest, BundleSubsetOfMediaSections) {
1009 CreateJsepTransportController(JsepTransportController::Config());
1010 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1011 bundle_group.AddContentName(kAudioMid1);
1012 bundle_group.AddContentName(kVideoMid1);
1013
1014 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1015 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1016 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1017 nullptr);
1018 AddAudioSection(local_offer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1019 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1020 nullptr);
1021 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1022 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1023 nullptr);
1024
1025 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1026 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1027 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1028 nullptr);
1029 AddAudioSection(remote_answer.get(), kAudioMid2, kIceUfrag2, kIcePwd2,
1030 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1031 nullptr);
1032 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1033 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1034 nullptr);
1035
1036 local_offer->AddGroup(bundle_group);
1037 remote_answer->AddGroup(bundle_group);
1038 EXPECT_TRUE(transport_controller_
1039 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1040 .ok());
1041 EXPECT_TRUE(transport_controller_
1042 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1043 .ok());
1044
1045 // Verifiy that only |kAudio1| and |kVideo1| are bundled.
1046 auto transport1 = transport_controller_->GetRtpTransport(kAudioMid1);
1047 auto transport2 = transport_controller_->GetRtpTransport(kAudioMid2);
1048 auto transport3 = transport_controller_->GetRtpTransport(kVideoMid1);
1049 EXPECT_NE(transport1, transport2);
1050 EXPECT_EQ(transport1, transport3);
1051
1052 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1053 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1054 EXPECT_EQ(transport1, it->second);
1055 it = changed_rtp_transport_by_mid_.find(kAudioMid2);
1056 EXPECT_TRUE(it == changed_rtp_transport_by_mid_.end());
1057}
1058
1059// Tests that the initial offer/answer only have data section and audio/video
1060// sections are added in the subsequent offer.
1061TEST_F(JsepTransportControllerTest, BundleOnDataSectionInSubsequentOffer) {
1062 CreateJsepTransportController(JsepTransportController::Config());
1063 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1064 bundle_group.AddContentName(kDataMid1);
1065
1066 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1067 AddDataSection(local_offer.get(), kDataMid1,
1068 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1069 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1070 nullptr);
1071 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1072 AddDataSection(remote_answer.get(), kDataMid1,
1073 cricket::MediaProtocolType::kSctp, kIceUfrag1, kIcePwd1,
1074 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1075 nullptr);
1076 local_offer->AddGroup(bundle_group);
1077 remote_answer->AddGroup(bundle_group);
1078
1079 EXPECT_TRUE(transport_controller_
1080 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1081 .ok());
1082 EXPECT_TRUE(transport_controller_
1083 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1084 .ok());
1085 auto data_transport = transport_controller_->GetRtpTransport(kDataMid1);
1086
1087 // Add audio/video sections in subsequent offer.
1088 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1089 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1090 nullptr);
1091 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1092 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1093 nullptr);
1094 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag2, kIcePwd2,
1095 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1096 nullptr);
1097 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag3, kIcePwd3,
1098 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1099 nullptr);
1100
1101 // Reset the bundle group and do another offer/answer exchange.
1102 bundle_group.AddContentName(kAudioMid1);
1103 bundle_group.AddContentName(kVideoMid1);
1104 local_offer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1105 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1106 local_offer->AddGroup(bundle_group);
1107 remote_answer->AddGroup(bundle_group);
1108
1109 EXPECT_TRUE(transport_controller_
1110 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1111 .ok());
1112 EXPECT_TRUE(transport_controller_
1113 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1114 .ok());
1115
1116 auto audio_transport = transport_controller_->GetRtpTransport(kAudioMid1);
1117 auto video_transport = transport_controller_->GetRtpTransport(kVideoMid1);
1118 EXPECT_EQ(data_transport, audio_transport);
1119 EXPECT_EQ(data_transport, video_transport);
1120}
1121
1122TEST_F(JsepTransportControllerTest, VideoDataRejectedInAnswer) {
1123 CreateJsepTransportController(JsepTransportController::Config());
1124 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1125 bundle_group.AddContentName(kAudioMid1);
1126 bundle_group.AddContentName(kVideoMid1);
1127 bundle_group.AddContentName(kDataMid1);
1128
1129 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1130 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1131 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1132 nullptr);
1133 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1134 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1135 nullptr);
1136 AddDataSection(local_offer.get(), kDataMid1,
1137 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1138 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1139 nullptr);
1140
1141 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1142 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1143 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1144 nullptr);
1145 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1146 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1147 nullptr);
1148 AddDataSection(remote_answer.get(), kDataMid1,
1149 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1150 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1151 nullptr);
1152 // Reject video and data section.
1153 remote_answer->contents()[1].rejected = true;
1154 remote_answer->contents()[2].rejected = true;
1155
1156 local_offer->AddGroup(bundle_group);
1157 remote_answer->AddGroup(bundle_group);
1158
1159 EXPECT_TRUE(transport_controller_
1160 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1161 .ok());
1162 EXPECT_TRUE(transport_controller_
1163 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1164 .ok());
1165
1166 // Verify the RtpTransport/DtlsTransport is destroyed correctly.
1167 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1168 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1169 // Verify the signals are fired correctly.
1170 auto it = changed_rtp_transport_by_mid_.find(kVideoMid1);
1171 ASSERT_TRUE(it != changed_rtp_transport_by_mid_.end());
1172 EXPECT_EQ(nullptr, it->second);
1173 auto it2 = changed_dtls_transport_by_mid_.find(kDataMid1);
1174 ASSERT_TRUE(it2 != changed_dtls_transport_by_mid_.end());
1175 EXPECT_EQ(nullptr, it2->second);
1176}
1177
1178// Tests that changing the bundled MID in subsequent offer/answer exchange is
1179// not supported.
1180// TODO(bugs.webrtc.org/6704): Change this test to expect success once issue is
1181// fixed
1182TEST_F(JsepTransportControllerTest, ChangeBundledMidNotSupported) {
1183 CreateJsepTransportController(JsepTransportController::Config());
1184 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1185 bundle_group.AddContentName(kAudioMid1);
1186 bundle_group.AddContentName(kVideoMid1);
1187
1188 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1189 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1190 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1191 nullptr);
1192 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1193 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1194 nullptr);
1195
1196 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1197 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1198 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1199 nullptr);
1200 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1201 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1202 nullptr);
1203
1204 local_offer->AddGroup(bundle_group);
1205 remote_answer->AddGroup(bundle_group);
1206 EXPECT_TRUE(transport_controller_
1207 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1208 .ok());
1209 EXPECT_TRUE(transport_controller_
1210 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1211 .ok());
1212 EXPECT_EQ(transport_controller_->GetRtpTransport(kAudioMid1),
1213 transport_controller_->GetRtpTransport(kVideoMid1));
1214
1215 // Reorder the bundle group.
1216 EXPECT_TRUE(bundle_group.RemoveContentName(kAudioMid1));
1217 bundle_group.AddContentName(kAudioMid1);
1218 // The answerer uses the new bundle group and now the bundle mid is changed to
1219 // |kVideo1|.
1220 remote_answer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
1221 remote_answer->AddGroup(bundle_group);
1222 EXPECT_TRUE(transport_controller_
1223 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1224 .ok());
1225 EXPECT_FALSE(transport_controller_
1226 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1227 .ok());
1228}
Zhi Huange830e682018-03-30 10:48:35 -07001229// Test that rejecting only the first m= section of a BUNDLE group is treated as
1230// an error, but rejecting all of them works as expected.
1231TEST_F(JsepTransportControllerTest, RejectFirstContentInBundleGroup) {
1232 CreateJsepTransportController(JsepTransportController::Config());
1233 cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
1234 bundle_group.AddContentName(kAudioMid1);
1235 bundle_group.AddContentName(kVideoMid1);
1236 bundle_group.AddContentName(kDataMid1);
1237
1238 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1239 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1240 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1241 nullptr);
1242 AddVideoSection(local_offer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1243 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1244 nullptr);
1245 AddDataSection(local_offer.get(), kDataMid1,
1246 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1247 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1248 nullptr);
1249
1250 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1251 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1252 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1253 nullptr);
1254 AddVideoSection(remote_answer.get(), kVideoMid1, kIceUfrag2, kIcePwd2,
1255 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1256 nullptr);
1257 AddDataSection(remote_answer.get(), kDataMid1,
1258 cricket::MediaProtocolType::kSctp, kIceUfrag3, kIcePwd3,
1259 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1260 nullptr);
1261 // Reject audio content in answer.
1262 remote_answer->contents()[0].rejected = true;
1263
1264 local_offer->AddGroup(bundle_group);
1265 remote_answer->AddGroup(bundle_group);
1266
1267 EXPECT_TRUE(transport_controller_
1268 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1269 .ok());
1270 EXPECT_FALSE(transport_controller_
1271 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1272 .ok());
1273
1274 // Reject all the contents.
1275 remote_answer->contents()[1].rejected = true;
1276 remote_answer->contents()[2].rejected = true;
1277 EXPECT_TRUE(transport_controller_
1278 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1279 .ok());
1280 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kAudioMid1));
1281 EXPECT_EQ(nullptr, transport_controller_->GetRtpTransport(kVideoMid1));
1282 EXPECT_EQ(nullptr, transport_controller_->GetDtlsTransport(kDataMid1));
1283}
1284
1285// Tests that applying non-RTCP-mux offer would fail when kRtcpMuxPolicyRequire
1286// is used.
1287TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxOfferWhenMuxingRequired) {
1288 JsepTransportController::Config config;
1289 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
1290 CreateJsepTransportController(config);
1291 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1292 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1293 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1294 nullptr);
1295
1296 local_offer->contents()[0].media_description()->set_rtcp_mux(false);
1297 // Applying a non-RTCP-mux offer is expected to fail.
1298 EXPECT_FALSE(transport_controller_
1299 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1300 .ok());
1301}
1302
1303// Tests that applying non-RTCP-mux answer would fail when kRtcpMuxPolicyRequire
1304// is used.
1305TEST_F(JsepTransportControllerTest, ApplyNonRtcpMuxAnswerWhenMuxingRequired) {
1306 JsepTransportController::Config config;
1307 config.rtcp_mux_policy = PeerConnectionInterface::kRtcpMuxPolicyRequire;
1308 CreateJsepTransportController(config);
1309 auto local_offer = rtc::MakeUnique<cricket::SessionDescription>();
1310 AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1311 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
1312 nullptr);
1313 EXPECT_TRUE(transport_controller_
1314 ->SetLocalDescription(SdpType::kOffer, local_offer.get())
1315 .ok());
1316
1317 auto remote_answer = rtc::MakeUnique<cricket::SessionDescription>();
1318 AddAudioSection(remote_answer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
1319 cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_PASSIVE,
1320 nullptr);
1321 // Applying a non-RTCP-mux answer is expected to fail.
1322 remote_answer->contents()[0].media_description()->set_rtcp_mux(false);
1323 EXPECT_FALSE(transport_controller_
1324 ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
1325 .ok());
1326}
Zhi Huange818b6e2018-02-22 15:26:27 -08001327
1328} // namespace webrtc