1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #include <ostream>
6 #include <string>
7 #include <vector>
8 
9 #include "CSFLog.h"
10 
11 #include "nspr.h"
12 
13 #include "nricectx.h"
14 #include "nricemediastream.h"
15 #include "MediaPipelineFilter.h"
16 #include "MediaPipeline.h"
17 #include "PeerConnectionImpl.h"
18 #include "PeerConnectionMedia.h"
19 #include "runnable_utils.h"
20 #include "transportlayerice.h"
21 #include "transportlayerdtls.h"
22 #include "signaling/src/jsep/JsepSession.h"
23 #include "signaling/src/jsep/JsepTransport.h"
24 
25 #include "nsContentUtils.h"
26 #include "nsNetCID.h"
27 #include "nsNetUtil.h"
28 #include "nsIURI.h"
29 #include "nsIScriptSecurityManager.h"
30 #include "nsICancelable.h"
31 #include "nsILoadInfo.h"
32 #include "nsIContentPolicy.h"
33 #include "nsIProxyInfo.h"
34 #include "nsIProtocolProxyService.h"
35 
36 #include "nsProxyRelease.h"
37 
38 #include "nsIScriptGlobalObject.h"
39 #include "mozilla/Preferences.h"
40 #include "mozilla/Telemetry.h"
41 #include "MediaManager.h"
42 #include "WebrtcGmpVideoCodec.h"
43 
44 namespace mozilla {
45 using namespace dom;
46 
47 static const char* pcmLogTag = "PeerConnectionMedia";
48 #ifdef LOGTAG
49 #undef LOGTAG
50 #endif
51 #define LOGTAG pcmLogTag
52 
OnProxyAvailable(nsICancelable * request,nsIChannel * aChannel,nsIProxyInfo * proxyinfo,nsresult result)53 NS_IMETHODIMP PeerConnectionMedia::ProtocolProxyQueryHandler::OnProxyAvailable(
54     nsICancelable* request, nsIChannel* aChannel, nsIProxyInfo* proxyinfo,
55     nsresult result) {
56   if (!pcm_->mProxyRequest) {
57     // PeerConnectionMedia is no longer waiting
58     return NS_OK;
59   }
60 
61   CSFLogInfo(LOGTAG, "%s: Proxy Available: %d", __FUNCTION__, (int)result);
62 
63   if (NS_SUCCEEDED(result) && proxyinfo) {
64     SetProxyOnPcm(*proxyinfo);
65   }
66 
67   pcm_->mProxyResolveCompleted = true;
68   pcm_->mProxyRequest = nullptr;
69   pcm_->FlushIceCtxOperationQueueIfReady();
70 
71   return NS_OK;
72 }
73 
SetProxyOnPcm(nsIProxyInfo & proxyinfo)74 void PeerConnectionMedia::ProtocolProxyQueryHandler::SetProxyOnPcm(
75     nsIProxyInfo& proxyinfo) {
76   CSFLogInfo(LOGTAG, "%s: Had proxyinfo", __FUNCTION__);
77   nsresult rv;
78   nsCString httpsProxyHost;
79   int32_t httpsProxyPort;
80 
81   rv = proxyinfo.GetHost(httpsProxyHost);
82   if (NS_FAILED(rv)) {
83     CSFLogError(LOGTAG, "%s: Failed to get proxy server host", __FUNCTION__);
84     return;
85   }
86 
87   rv = proxyinfo.GetPort(&httpsProxyPort);
88   if (NS_FAILED(rv)) {
89     CSFLogError(LOGTAG, "%s: Failed to get proxy server port", __FUNCTION__);
90     return;
91   }
92 
93   if (pcm_->mIceCtxHdlr.get()) {
94     assert(httpsProxyPort >= 0 && httpsProxyPort < (1 << 16));
95     // Note that this could check if PrivacyRequested() is set on the PC and
96     // remove "webrtc" from the ALPN list.  But that would only work if the PC
97     // was constructed with a peerIdentity constraint, not when isolated
98     // streams are added.  If we ever need to signal to the proxy that the
99     // media is isolated, then we would need to restructure this code.
100     pcm_->mProxyServer.reset(new NrIceProxyServer(
101         httpsProxyHost.get(), static_cast<uint16_t>(httpsProxyPort),
102         "webrtc,c-webrtc"));
103   } else {
104     CSFLogError(LOGTAG, "%s: Failed to set proxy server (ICE ctx unavailable)",
105                 __FUNCTION__);
106   }
107 }
108 
NS_IMPL_ISUPPORTS(PeerConnectionMedia::ProtocolProxyQueryHandler,nsIProtocolProxyCallback)109 NS_IMPL_ISUPPORTS(PeerConnectionMedia::ProtocolProxyQueryHandler,
110                   nsIProtocolProxyCallback)
111 
112 void PeerConnectionMedia::StunAddrsHandler::OnStunAddrsAvailable(
113     const mozilla::net::NrIceStunAddrArray& addrs) {
114   CSFLogInfo(LOGTAG, "%s: receiving (%d) stun addrs", __FUNCTION__,
115              (int)addrs.Length());
116   if (pcm_) {
117     pcm_->mStunAddrs = addrs;
118     pcm_->mLocalAddrsCompleted = true;
119     pcm_->mStunAddrsRequest = nullptr;
120     pcm_->FlushIceCtxOperationQueueIfReady();
121     // If parent process returns 0 STUN addresses, change ICE connection
122     // state to failed.
123     if (!pcm_->mStunAddrs.Length()) {
124       pcm_->SignalIceConnectionStateChange(pcm_->mIceCtxHdlr->ctx().get(),
125                                            NrIceCtx::ICE_CTX_FAILED);
126     }
127 
128     pcm_ = nullptr;
129   }
130 }
131 
PeerConnectionMedia(PeerConnectionImpl * parent)132 PeerConnectionMedia::PeerConnectionMedia(PeerConnectionImpl* parent)
133     : mParent(parent),
134       mParentHandle(parent->GetHandle()),
135       mParentName(parent->GetName()),
136       mIceCtxHdlr(nullptr),
137       mDNSResolver(new NrIceResolver()),
138       mUuidGen(MakeUnique<PCUuidGenerator>()),
139       mMainThread(mParent->GetMainThread()),
140       mSTSThread(mParent->GetSTSThread()),
141       mProxyResolveCompleted(false),
142       mIceRestartState(ICE_RESTART_NONE),
143       mLocalAddrsCompleted(false) {}
144 
~PeerConnectionMedia()145 PeerConnectionMedia::~PeerConnectionMedia() {
146   MOZ_RELEASE_ASSERT(!mMainThread);
147 }
148 
InitLocalAddrs()149 void PeerConnectionMedia::InitLocalAddrs() {
150   if (XRE_IsContentProcess()) {
151     CSFLogDebug(LOGTAG, "%s: Get stun addresses via IPC",
152                 mParentHandle.c_str());
153 
154     nsCOMPtr<nsIEventTarget> target =
155         mParent->GetWindow()
156             ? mParent->GetWindow()->EventTargetFor(TaskCategory::Other)
157             : nullptr;
158 
159     // We're in the content process, so send a request over IPC for the
160     // stun address discovery.
161     mStunAddrsRequest =
162         new StunAddrsRequestChild(new StunAddrsHandler(this), target);
163     mStunAddrsRequest->SendGetStunAddrs();
164   } else {
165     // No content process, so don't need to hold up the ice event queue
166     // until completion of stun address discovery. We can let the
167     // discovery of stun addresses happen in the same process.
168     mLocalAddrsCompleted = true;
169   }
170 }
171 
InitProxy()172 nsresult PeerConnectionMedia::InitProxy() {
173   // Allow mochitests to disable this, since mochitest configures a fake proxy
174   // that serves up content.
175   bool disable =
176       Preferences::GetBool("media.peerconnection.disable_http_proxy", false);
177   if (disable) {
178     mProxyResolveCompleted = true;
179     return NS_OK;
180   }
181 
182   nsresult rv;
183   nsCOMPtr<nsIProtocolProxyService> pps =
184       do_GetService(NS_PROTOCOLPROXYSERVICE_CONTRACTID, &rv);
185   if (NS_FAILED(rv)) {
186     CSFLogError(LOGTAG, "%s: Failed to get proxy service: %d", __FUNCTION__,
187                 (int)rv);
188     return NS_ERROR_FAILURE;
189   }
190 
191   // We use the following URL to find the "default" proxy address for all HTTPS
192   // connections.  We will only attempt one HTTP(S) CONNECT per peer connection.
193   // "example.com" is guaranteed to be unallocated and should return the best
194   // default.
195   nsCOMPtr<nsIURI> fakeHttpsLocation;
196   rv = NS_NewURI(getter_AddRefs(fakeHttpsLocation), "https://example.com");
197   if (NS_FAILED(rv)) {
198     CSFLogError(LOGTAG, "%s: Failed to set URI: %d", __FUNCTION__, (int)rv);
199     return NS_ERROR_FAILURE;
200   }
201 
202   nsCOMPtr<nsIChannel> channel;
203   rv = NS_NewChannel(getter_AddRefs(channel), fakeHttpsLocation,
204                      nsContentUtils::GetSystemPrincipal(),
205                      nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
206                      nsIContentPolicy::TYPE_OTHER);
207 
208   if (NS_FAILED(rv)) {
209     CSFLogError(LOGTAG, "%s: Failed to get channel from URI: %d", __FUNCTION__,
210                 (int)rv);
211     return NS_ERROR_FAILURE;
212   }
213 
214   nsCOMPtr<nsIEventTarget> target =
215       mParent->GetWindow()
216           ? mParent->GetWindow()->EventTargetFor(TaskCategory::Network)
217           : nullptr;
218   RefPtr<ProtocolProxyQueryHandler> handler =
219       new ProtocolProxyQueryHandler(this);
220   rv = pps->AsyncResolve(channel,
221                          nsIProtocolProxyService::RESOLVE_PREFER_HTTPS_PROXY |
222                              nsIProtocolProxyService::RESOLVE_ALWAYS_TUNNEL,
223                          handler, target, getter_AddRefs(mProxyRequest));
224   if (NS_FAILED(rv)) {
225     CSFLogError(LOGTAG, "%s: Failed to resolve protocol proxy: %d",
226                 __FUNCTION__, (int)rv);
227     return NS_ERROR_FAILURE;
228   }
229 
230   return NS_OK;
231 }
232 
Init(const std::vector<NrIceStunServer> & stun_servers,const std::vector<NrIceTurnServer> & turn_servers,NrIceCtx::Policy policy)233 nsresult PeerConnectionMedia::Init(
234     const std::vector<NrIceStunServer>& stun_servers,
235     const std::vector<NrIceTurnServer>& turn_servers, NrIceCtx::Policy policy) {
236   nsresult rv = InitProxy();
237   NS_ENSURE_SUCCESS(rv, rv);
238 
239   bool ice_tcp = Preferences::GetBool("media.peerconnection.ice.tcp", false);
240 
241   // setup the stun local addresses IPC async call
242   InitLocalAddrs();
243 
244   // TODO(ekr@rtfm.com): need some way to set not offerer later
245   // Looks like a bug in the NrIceCtx API.
246   mIceCtxHdlr = NrIceCtxHandler::Create(
247       "PC:" + mParentName, mParent->GetAllowIceLoopback(), ice_tcp,
248       mParent->GetAllowIceLinkLocal(), policy);
249   if (!mIceCtxHdlr) {
250     CSFLogError(LOGTAG, "%s: Failed to create Ice Context", __FUNCTION__);
251     return NS_ERROR_FAILURE;
252   }
253 
254   if (NS_FAILED(rv = mIceCtxHdlr->ctx()->SetStunServers(stun_servers))) {
255     CSFLogError(LOGTAG, "%s: Failed to set stun servers", __FUNCTION__);
256     return rv;
257   }
258   // Give us a way to globally turn off TURN support
259   bool disabled =
260       Preferences::GetBool("media.peerconnection.turn.disable", false);
261   if (!disabled) {
262     if (NS_FAILED(rv = mIceCtxHdlr->ctx()->SetTurnServers(turn_servers))) {
263       CSFLogError(LOGTAG, "%s: Failed to set turn servers", __FUNCTION__);
264       return rv;
265     }
266   } else if (!turn_servers.empty()) {
267     CSFLogError(LOGTAG, "%s: Setting turn servers disabled", __FUNCTION__);
268   }
269   if (NS_FAILED(rv = mDNSResolver->Init())) {
270     CSFLogError(LOGTAG, "%s: Failed to initialize dns resolver", __FUNCTION__);
271     return rv;
272   }
273   if (NS_FAILED(rv = mIceCtxHdlr->ctx()->SetResolver(
274                     mDNSResolver->AllocateResolver()))) {
275     CSFLogError(LOGTAG, "%s: Failed to get dns resolver", __FUNCTION__);
276     return rv;
277   }
278   ConnectSignals(mIceCtxHdlr->ctx().get());
279 
280   return NS_OK;
281 }
282 
EnsureTransports(const JsepSession & aSession)283 void PeerConnectionMedia::EnsureTransports(const JsepSession& aSession) {
284   for (const auto& transceiver : aSession.GetTransceivers()) {
285     if (!transceiver->HasLevel()) {
286       continue;
287     }
288 
289     RefPtr<JsepTransport> transport = transceiver->mTransport;
290     RUN_ON_THREAD(GetSTSThread(),
291                   WrapRunnable(RefPtr<PeerConnectionMedia>(this),
292                                &PeerConnectionMedia::EnsureTransport_s,
293                                transceiver->GetLevel(), transport->mComponents),
294                   NS_DISPATCH_NORMAL);
295   }
296 
297   GatherIfReady();
298 }
299 
EnsureTransport_s(size_t aLevel,size_t aComponentCount)300 void PeerConnectionMedia::EnsureTransport_s(size_t aLevel,
301                                             size_t aComponentCount) {
302   RefPtr<NrIceMediaStream> stream(mIceCtxHdlr->ctx()->GetStream(aLevel));
303   if (!stream) {
304     CSFLogDebug(LOGTAG, "%s: Creating ICE media stream=%u components=%u",
305                 mParentHandle.c_str(), static_cast<unsigned>(aLevel),
306                 static_cast<unsigned>(aComponentCount));
307 
308     std::ostringstream os;
309     os << mParentName << " aLevel=" << aLevel;
310     RefPtr<NrIceMediaStream> stream =
311         mIceCtxHdlr->CreateStream(os.str(), aComponentCount);
312 
313     if (!stream) {
314       CSFLogError(LOGTAG, "Failed to create ICE stream.");
315       return;
316     }
317 
318     stream->SetLevel(aLevel);
319     stream->SignalReady.connect(this, &PeerConnectionMedia::IceStreamReady_s);
320     stream->SignalCandidate.connect(this,
321                                     &PeerConnectionMedia::OnCandidateFound_s);
322     mIceCtxHdlr->ctx()->SetStream(aLevel, stream);
323   }
324 }
325 
ActivateOrRemoveTransports(const JsepSession & aSession,const bool forceIceTcp)326 nsresult PeerConnectionMedia::ActivateOrRemoveTransports(
327     const JsepSession& aSession, const bool forceIceTcp) {
328   for (const auto& transceiver : aSession.GetTransceivers()) {
329     if (!transceiver->HasLevel()) {
330       continue;
331     }
332 
333     std::string ufrag;
334     std::string pwd;
335     std::vector<std::string> candidates;
336     size_t components = 0;
337 
338     RefPtr<JsepTransport> transport = transceiver->mTransport;
339     unsigned level = transceiver->GetLevel();
340 
341     if (transport->mComponents && (!transceiver->HasBundleLevel() ||
342                                    (transceiver->BundleLevel() == level))) {
343       CSFLogDebug(LOGTAG,
344                   "ACTIVATING TRANSPORT! - PC %s: level=%u components=%u",
345                   mParentHandle.c_str(), (unsigned)level,
346                   (unsigned)transport->mComponents);
347 
348       ufrag = transport->mIce->GetUfrag();
349       pwd = transport->mIce->GetPassword();
350       candidates = transport->mIce->GetCandidates();
351       components = transport->mComponents;
352       if (forceIceTcp) {
353         candidates.erase(
354             std::remove_if(candidates.begin(), candidates.end(),
355                            [](const std::string& s) {
356                              return s.find(" UDP ") != std::string::npos ||
357                                     s.find(" udp ") != std::string::npos;
358                            }),
359             candidates.end());
360       }
361     }
362 
363     RUN_ON_THREAD(
364         GetSTSThread(),
365         WrapRunnable(RefPtr<PeerConnectionMedia>(this),
366                      &PeerConnectionMedia::ActivateOrRemoveTransport_s,
367                      transceiver->GetLevel(), components, ufrag, pwd,
368                      candidates),
369         NS_DISPATCH_NORMAL);
370   }
371 
372   // We can have more streams than m-lines due to rollback.
373   RUN_ON_THREAD(GetSTSThread(),
374                 WrapRunnable(RefPtr<PeerConnectionMedia>(this),
375                              &PeerConnectionMedia::RemoveTransportsAtOrAfter_s,
376                              aSession.GetTransceivers().size()),
377                 NS_DISPATCH_NORMAL);
378 
379   return NS_OK;
380 }
381 
UpdateTransceiverTransports(const JsepSession & aSession)382 nsresult PeerConnectionMedia::UpdateTransceiverTransports(
383     const JsepSession& aSession) {
384   for (const auto& transceiver : aSession.GetTransceivers()) {
385     nsresult rv = UpdateTransportFlows(*transceiver);
386     if (NS_FAILED(rv)) {
387       return rv;
388     }
389   }
390 
391   for (const auto& transceiverImpl : mTransceivers) {
392     transceiverImpl->UpdateTransport(*this);
393   }
394 
395   return NS_OK;
396 }
397 
ActivateOrRemoveTransport_s(size_t aMLine,size_t aComponentCount,const std::string & aUfrag,const std::string & aPassword,const std::vector<std::string> & aCandidateList)398 void PeerConnectionMedia::ActivateOrRemoveTransport_s(
399     size_t aMLine, size_t aComponentCount, const std::string& aUfrag,
400     const std::string& aPassword,
401     const std::vector<std::string>& aCandidateList) {
402   if (!aComponentCount) {
403     CSFLogDebug(LOGTAG, "%s: Removing ICE media stream=%u",
404                 mParentHandle.c_str(), static_cast<unsigned>(aMLine));
405     mIceCtxHdlr->ctx()->SetStream(aMLine, nullptr);
406     return;
407   }
408 
409   RefPtr<NrIceMediaStream> stream(mIceCtxHdlr->ctx()->GetStream(aMLine));
410   if (!stream) {
411     MOZ_ASSERT(false);
412     return;
413   }
414 
415   if (!stream->HasParsedAttributes()) {
416     CSFLogDebug(LOGTAG, "%s: Activating ICE media stream=%u components=%u",
417                 mParentHandle.c_str(), static_cast<unsigned>(aMLine),
418                 static_cast<unsigned>(aComponentCount));
419 
420     std::vector<std::string> attrs;
421     attrs.reserve(aCandidateList.size() + 2 /* ufrag + pwd */);
422     for (const auto& candidate : aCandidateList) {
423       attrs.push_back("candidate:" + candidate);
424     }
425     attrs.push_back("ice-ufrag:" + aUfrag);
426     attrs.push_back("ice-pwd:" + aPassword);
427 
428     nsresult rv = stream->ParseAttributes(attrs);
429     if (NS_FAILED(rv)) {
430       CSFLogError(LOGTAG, "Couldn't parse ICE attributes, rv=%u",
431                   static_cast<unsigned>(rv));
432     }
433 
434     for (size_t c = aComponentCount; c < stream->components(); ++c) {
435       // components are 1-indexed
436       stream->DisableComponent(c + 1);
437     }
438   }
439 }
440 
RemoveTransportsAtOrAfter_s(size_t aMLine)441 void PeerConnectionMedia::RemoveTransportsAtOrAfter_s(size_t aMLine) {
442   for (size_t i = aMLine; i < mIceCtxHdlr->ctx()->GetStreamCount(); ++i) {
443     mIceCtxHdlr->ctx()->SetStream(i, nullptr);
444   }
445 }
446 
UpdateMediaPipelines()447 nsresult PeerConnectionMedia::UpdateMediaPipelines() {
448   // The GMP code is all the way on the other side of webrtc.org, and it is not
449   // feasible to plumb error information all the way back. So, we set up a
450   // handle to the PC (for the duration of this call) in a global variable.
451   // This allows the GMP code to report errors to the PC.
452   WebrtcGmpPCHandleSetter setter(mParentHandle);
453 
454   for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
455     nsresult rv = transceiver->UpdateConduit();
456     if (NS_FAILED(rv)) {
457       return rv;
458     }
459 
460     if (!transceiver->IsVideo()) {
461       rv = transceiver->SyncWithMatchingVideoConduits(mTransceivers);
462       if (NS_FAILED(rv)) {
463         return rv;
464       }
465       // TODO: If there is no audio, we should probably de-sync. However, this
466       // has never been done before, and it is unclear whether it is safe...
467     }
468   }
469 
470   return NS_OK;
471 }
472 
UpdateTransportFlows(const JsepTransceiver & aTransceiver)473 nsresult PeerConnectionMedia::UpdateTransportFlows(
474     const JsepTransceiver& aTransceiver) {
475   if (!aTransceiver.HasLevel()) {
476     // Nothing to do
477     return NS_OK;
478   }
479 
480   size_t transportLevel = aTransceiver.GetTransportLevel();
481 
482   nsresult rv =
483       UpdateTransportFlow(transportLevel, false, *aTransceiver.mTransport);
484   if (NS_FAILED(rv)) {
485     return rv;
486   }
487 
488   return UpdateTransportFlow(transportLevel, true, *aTransceiver.mTransport);
489 }
490 
491 // Accessing the PCMedia should be safe here because we shouldn't
492 // have enqueued this function unless it was still active and
493 // the ICE data is destroyed on the STS.
FinalizeTransportFlow_s(RefPtr<PeerConnectionMedia> aPCMedia,RefPtr<TransportFlow> aFlow,size_t aLevel,bool aIsRtcp,nsAutoPtr<PtrVector<TransportLayer>> aLayerList)494 static void FinalizeTransportFlow_s(
495     RefPtr<PeerConnectionMedia> aPCMedia, RefPtr<TransportFlow> aFlow,
496     size_t aLevel, bool aIsRtcp,
497     nsAutoPtr<PtrVector<TransportLayer>> aLayerList) {
498   TransportLayerIce* ice =
499       static_cast<TransportLayerIce*>(aLayerList->values.front());
500   ice->SetParameters(aPCMedia->ice_media_stream(aLevel), aIsRtcp ? 2 : 1);
501   nsAutoPtr<std::queue<TransportLayer*>> layerQueue(
502       new std::queue<TransportLayer*>);
503   for (auto& value : aLayerList->values) {
504     layerQueue->push(value);
505   }
506   aLayerList->values.clear();
507   (void)aFlow->PushLayers(layerQueue);  // TODO(bug 854518): Process errors.
508 }
509 
AddNewIceStreamForRestart_s(RefPtr<PeerConnectionMedia> aPCMedia,RefPtr<TransportFlow> aFlow,size_t aLevel,bool aIsRtcp)510 static void AddNewIceStreamForRestart_s(RefPtr<PeerConnectionMedia> aPCMedia,
511                                         RefPtr<TransportFlow> aFlow,
512                                         size_t aLevel, bool aIsRtcp) {
513   TransportLayerIce* ice =
514       static_cast<TransportLayerIce*>(aFlow->GetLayer("ice"));
515   ice->SetParameters(aPCMedia->ice_media_stream(aLevel), aIsRtcp ? 2 : 1);
516 }
517 
UpdateTransportFlow(size_t aLevel,bool aIsRtcp,const JsepTransport & aTransport)518 nsresult PeerConnectionMedia::UpdateTransportFlow(
519     size_t aLevel, bool aIsRtcp, const JsepTransport& aTransport) {
520   if (aIsRtcp && aTransport.mComponents < 2) {
521     RemoveTransportFlow(aLevel, aIsRtcp);
522     return NS_OK;
523   }
524 
525   if (!aIsRtcp && !aTransport.mComponents) {
526     RemoveTransportFlow(aLevel, aIsRtcp);
527     return NS_OK;
528   }
529 
530   nsresult rv;
531 
532   RefPtr<TransportFlow> flow = GetTransportFlow(aLevel, aIsRtcp);
533   if (flow) {
534     if (IsIceRestarting()) {
535       CSFLogInfo(LOGTAG, "Flow[%s]: detected ICE restart - level: %u rtcp: %d",
536                  flow->id().c_str(), (unsigned)aLevel, aIsRtcp);
537 
538       RefPtr<PeerConnectionMedia> pcMedia(this);
539       rv = GetSTSThread()->Dispatch(
540           WrapRunnableNM(AddNewIceStreamForRestart_s, pcMedia, flow, aLevel,
541                          aIsRtcp),
542           NS_DISPATCH_NORMAL);
543       if (NS_FAILED(rv)) {
544         CSFLogError(LOGTAG, "Failed to dispatch AddNewIceStreamForRestart_s");
545         return rv;
546       }
547     }
548 
549     return NS_OK;
550   }
551 
552   std::ostringstream osId;
553   osId << mParentHandle << ":" << aLevel << "," << (aIsRtcp ? "rtcp" : "rtp");
554   flow = new TransportFlow(osId.str());
555 
556   // The media streams are made on STS so we need to defer setup.
557   auto ice = MakeUnique<TransportLayerIce>();
558   auto dtls = MakeUnique<TransportLayerDtls>();
559   dtls->SetRole(aTransport.mDtls->GetRole() ==
560                         JsepDtlsTransport::kJsepDtlsClient
561                     ? TransportLayerDtls::CLIENT
562                     : TransportLayerDtls::SERVER);
563 
564   RefPtr<DtlsIdentity> pcid = mParent->Identity();
565   if (!pcid) {
566     CSFLogError(LOGTAG, "Failed to get DTLS identity.");
567     return NS_ERROR_FAILURE;
568   }
569   dtls->SetIdentity(pcid);
570 
571   const SdpFingerprintAttributeList& fingerprints =
572       aTransport.mDtls->GetFingerprints();
573   for (const auto& fingerprint : fingerprints.mFingerprints) {
574     std::ostringstream ss;
575     ss << fingerprint.hashFunc;
576     rv = dtls->SetVerificationDigest(ss.str(), &fingerprint.fingerprint[0],
577                                      fingerprint.fingerprint.size());
578     if (NS_FAILED(rv)) {
579       CSFLogError(LOGTAG, "Could not set fingerprint");
580       return rv;
581     }
582   }
583 
584   std::vector<uint16_t> srtpCiphers;
585   srtpCiphers.push_back(SRTP_AES128_CM_HMAC_SHA1_80);
586   srtpCiphers.push_back(SRTP_AES128_CM_HMAC_SHA1_32);
587 
588   rv = dtls->SetSrtpCiphers(srtpCiphers);
589   if (NS_FAILED(rv)) {
590     CSFLogError(LOGTAG, "Couldn't set SRTP ciphers");
591     return rv;
592   }
593 
594   // Always permits negotiation of the confidential mode.
595   // Only allow non-confidential (which is an allowed default),
596   // if we aren't confidential.
597   std::set<std::string> alpn;
598   std::string alpnDefault = "";
599   alpn.insert("c-webrtc");
600   if (!mParent->PrivacyRequested()) {
601     alpnDefault = "webrtc";
602     alpn.insert(alpnDefault);
603   }
604   rv = dtls->SetAlpn(alpn, alpnDefault);
605   if (NS_FAILED(rv)) {
606     CSFLogError(LOGTAG, "Couldn't set ALPN");
607     return rv;
608   }
609 
610   nsAutoPtr<PtrVector<TransportLayer>> layers(new PtrVector<TransportLayer>);
611   layers->values.push_back(ice.release());
612   layers->values.push_back(dtls.release());
613 
614   RefPtr<PeerConnectionMedia> pcMedia(this);
615   rv = GetSTSThread()->Dispatch(WrapRunnableNM(FinalizeTransportFlow_s, pcMedia,
616                                                flow, aLevel, aIsRtcp, layers),
617                                 NS_DISPATCH_NORMAL);
618   if (NS_FAILED(rv)) {
619     CSFLogError(LOGTAG, "Failed to dispatch FinalizeTransportFlow_s");
620     return rv;
621   }
622 
623   AddTransportFlow(aLevel, aIsRtcp, flow);
624 
625   return NS_OK;
626 }
627 
StartIceChecks(const JsepSession & aSession)628 void PeerConnectionMedia::StartIceChecks(const JsepSession& aSession) {
629   nsCOMPtr<nsIRunnable> runnable(WrapRunnable(
630       RefPtr<PeerConnectionMedia>(this), &PeerConnectionMedia::StartIceChecks_s,
631       aSession.IsIceControlling(), aSession.IsOfferer(),
632       aSession.RemoteIsIceLite(),
633       // Copy, just in case API changes to return a ref
634       std::vector<std::string>(aSession.GetIceOptions())));
635 
636   PerformOrEnqueueIceCtxOperation(runnable);
637 }
638 
StartIceChecks_s(bool aIsControlling,bool aIsOfferer,bool aIsIceLite,const std::vector<std::string> & aIceOptionsList)639 void PeerConnectionMedia::StartIceChecks_s(
640     bool aIsControlling, bool aIsOfferer, bool aIsIceLite,
641     const std::vector<std::string>& aIceOptionsList) {
642   CSFLogDebug(LOGTAG, "Starting ICE Checking");
643 
644   std::vector<std::string> attributes;
645   if (aIsIceLite) {
646     attributes.push_back("ice-lite");
647   }
648 
649   if (!aIceOptionsList.empty()) {
650     attributes.push_back("ice-options:");
651     for (const auto& option : aIceOptionsList) {
652       attributes.back() += option + ' ';
653     }
654   }
655 
656   nsresult rv = mIceCtxHdlr->ctx()->ParseGlobalAttributes(attributes);
657   if (NS_FAILED(rv)) {
658     CSFLogError(LOGTAG, "%s: couldn't parse global parameters", __FUNCTION__);
659   }
660 
661   mIceCtxHdlr->ctx()->SetControlling(aIsControlling ? NrIceCtx::ICE_CONTROLLING
662                                                     : NrIceCtx::ICE_CONTROLLED);
663 
664   mIceCtxHdlr->ctx()->StartChecks(aIsOfferer);
665 }
666 
IsIceRestarting() const667 bool PeerConnectionMedia::IsIceRestarting() const {
668   ASSERT_ON_THREAD(mMainThread);
669 
670   return (mIceRestartState != ICE_RESTART_NONE);
671 }
672 
GetIceRestartState() const673 PeerConnectionMedia::IceRestartState PeerConnectionMedia::GetIceRestartState()
674     const {
675   ASSERT_ON_THREAD(mMainThread);
676 
677   return mIceRestartState;
678 }
679 
BeginIceRestart(const std::string & ufrag,const std::string & pwd)680 void PeerConnectionMedia::BeginIceRestart(const std::string& ufrag,
681                                           const std::string& pwd) {
682   ASSERT_ON_THREAD(mMainThread);
683   if (IsIceRestarting()) {
684     return;
685   }
686 
687   RefPtr<NrIceCtx> new_ctx = mIceCtxHdlr->CreateCtx(ufrag, pwd);
688 
689   RUN_ON_THREAD(GetSTSThread(),
690                 WrapRunnable(RefPtr<PeerConnectionMedia>(this),
691                              &PeerConnectionMedia::BeginIceRestart_s, new_ctx),
692                 NS_DISPATCH_NORMAL);
693 
694   mIceRestartState = ICE_RESTART_PROVISIONAL;
695 }
696 
BeginIceRestart_s(RefPtr<NrIceCtx> new_ctx)697 void PeerConnectionMedia::BeginIceRestart_s(RefPtr<NrIceCtx> new_ctx) {
698   ASSERT_ON_THREAD(mSTSThread);
699 
700   // hold the original context so we can disconnect signals if needed
701   RefPtr<NrIceCtx> originalCtx = mIceCtxHdlr->ctx();
702 
703   if (mIceCtxHdlr->BeginIceRestart(new_ctx)) {
704     ConnectSignals(mIceCtxHdlr->ctx().get(), originalCtx.get());
705   }
706 }
707 
CommitIceRestart()708 void PeerConnectionMedia::CommitIceRestart() {
709   ASSERT_ON_THREAD(mMainThread);
710   if (mIceRestartState != ICE_RESTART_PROVISIONAL) {
711     return;
712   }
713 
714   mIceRestartState = ICE_RESTART_COMMITTED;
715 }
716 
FinalizeIceRestart()717 void PeerConnectionMedia::FinalizeIceRestart() {
718   ASSERT_ON_THREAD(mMainThread);
719   if (!IsIceRestarting()) {
720     return;
721   }
722 
723   RUN_ON_THREAD(GetSTSThread(),
724                 WrapRunnable(RefPtr<PeerConnectionMedia>(this),
725                              &PeerConnectionMedia::FinalizeIceRestart_s),
726                 NS_DISPATCH_NORMAL);
727 
728   mIceRestartState = ICE_RESTART_NONE;
729 }
730 
FinalizeIceRestart_s()731 void PeerConnectionMedia::FinalizeIceRestart_s() {
732   ASSERT_ON_THREAD(mSTSThread);
733 
734   // reset old streams since we don't need them anymore
735   for (auto& transportFlow : mTransportFlows) {
736     RefPtr<TransportFlow> aFlow = transportFlow.second;
737     if (!aFlow) continue;
738     TransportLayerIce* ice = static_cast<TransportLayerIce*>(
739         aFlow->GetLayer(TransportLayerIce::ID()));
740     ice->ResetOldStream();
741   }
742 
743   mIceCtxHdlr->FinalizeIceRestart();
744 }
745 
RollbackIceRestart()746 void PeerConnectionMedia::RollbackIceRestart() {
747   ASSERT_ON_THREAD(mMainThread);
748   if (mIceRestartState != ICE_RESTART_PROVISIONAL) {
749     return;
750   }
751 
752   RUN_ON_THREAD(GetSTSThread(),
753                 WrapRunnable(RefPtr<PeerConnectionMedia>(this),
754                              &PeerConnectionMedia::RollbackIceRestart_s),
755                 NS_DISPATCH_NORMAL);
756 
757   mIceRestartState = ICE_RESTART_NONE;
758 }
759 
RollbackIceRestart_s()760 void PeerConnectionMedia::RollbackIceRestart_s() {
761   ASSERT_ON_THREAD(mSTSThread);
762 
763   // hold the restart context so we can disconnect signals
764   RefPtr<NrIceCtx> restartCtx = mIceCtxHdlr->ctx();
765 
766   // restore old streams since we're rolling back
767   for (auto& transportFlow : mTransportFlows) {
768     RefPtr<TransportFlow> aFlow = transportFlow.second;
769     if (!aFlow) continue;
770     TransportLayerIce* ice = static_cast<TransportLayerIce*>(
771         aFlow->GetLayer(TransportLayerIce::ID()));
772     ice->RestoreOldStream();
773   }
774 
775   mIceCtxHdlr->RollbackIceRestart();
776   ConnectSignals(mIceCtxHdlr->ctx().get(), restartCtx.get());
777 
778   // Fixup the telemetry by transferring abandoned ctx stats to current ctx.
779   NrIceStats stats = restartCtx->Destroy();
780   restartCtx = nullptr;
781   mIceCtxHdlr->ctx()->AccumulateStats(stats);
782 }
783 
GetPrefDefaultAddressOnly() const784 bool PeerConnectionMedia::GetPrefDefaultAddressOnly() const {
785   ASSERT_ON_THREAD(mMainThread);  // will crash on STS thread
786 
787   uint64_t winId = mParent->GetWindow()->WindowID();
788 
789   bool default_address_only = Preferences::GetBool(
790       "media.peerconnection.ice.default_address_only", false);
791   default_address_only |=
792       !MediaManager::Get()->IsActivelyCapturingOrHasAPermission(winId);
793   return default_address_only;
794 }
795 
GetPrefProxyOnly() const796 bool PeerConnectionMedia::GetPrefProxyOnly() const {
797   ASSERT_ON_THREAD(mMainThread);  // will crash on STS thread
798 
799   return Preferences::GetBool("media.peerconnection.ice.proxy_only", false);
800 }
801 
ConnectSignals(NrIceCtx * aCtx,NrIceCtx * aOldCtx)802 void PeerConnectionMedia::ConnectSignals(NrIceCtx* aCtx, NrIceCtx* aOldCtx) {
803   aCtx->SignalGatheringStateChange.connect(
804       this, &PeerConnectionMedia::IceGatheringStateChange_s);
805   aCtx->SignalConnectionStateChange.connect(
806       this, &PeerConnectionMedia::IceConnectionStateChange_s);
807 
808   if (aOldCtx) {
809     MOZ_ASSERT(aCtx != aOldCtx);
810     aOldCtx->SignalGatheringStateChange.disconnect(this);
811     aOldCtx->SignalConnectionStateChange.disconnect(this);
812 
813     // if the old and new connection state and/or gathering state is
814     // different fire the state update.  Note: we don't fire the update
815     // if the state is *INIT since updates for the INIT state aren't
816     // sent during the normal flow. (mjf)
817     if (aOldCtx->connection_state() != aCtx->connection_state() &&
818         aCtx->connection_state() != NrIceCtx::ICE_CTX_INIT) {
819       aCtx->SignalConnectionStateChange(aCtx, aCtx->connection_state());
820     }
821 
822     if (aOldCtx->gathering_state() != aCtx->gathering_state() &&
823         aCtx->gathering_state() != NrIceCtx::ICE_CTX_GATHER_INIT) {
824       aCtx->SignalGatheringStateChange(aCtx, aCtx->gathering_state());
825     }
826   }
827 }
828 
AddIceCandidate(const std::string & candidate,const std::string & mid,uint32_t aMLine)829 void PeerConnectionMedia::AddIceCandidate(const std::string& candidate,
830                                           const std::string& mid,
831                                           uint32_t aMLine) {
832   RUN_ON_THREAD(GetSTSThread(),
833                 WrapRunnable(RefPtr<PeerConnectionMedia>(this),
834                              &PeerConnectionMedia::AddIceCandidate_s,
835                              std::string(candidate),  // Make copies.
836                              std::string(mid), aMLine),
837                 NS_DISPATCH_NORMAL);
838 }
839 
AddIceCandidate_s(const std::string & aCandidate,const std::string & aMid,uint32_t aMLine)840 void PeerConnectionMedia::AddIceCandidate_s(const std::string& aCandidate,
841                                             const std::string& aMid,
842                                             uint32_t aMLine) {
843   RefPtr<NrIceMediaStream> stream(mIceCtxHdlr->ctx()->GetStream(aMLine));
844   if (!stream) {
845     CSFLogError(LOGTAG, "No ICE stream for candidate at level %u: %s",
846                 static_cast<unsigned>(aMLine), aCandidate.c_str());
847     return;
848   }
849 
850   nsresult rv = stream->ParseTrickleCandidate(aCandidate);
851   if (NS_FAILED(rv)) {
852     CSFLogError(LOGTAG, "Couldn't process ICE candidate at level %u",
853                 static_cast<unsigned>(aMLine));
854     return;
855   }
856 }
857 
UpdateNetworkState(bool online)858 void PeerConnectionMedia::UpdateNetworkState(bool online) {
859   RUN_ON_THREAD(
860       GetSTSThread(),
861       WrapRunnable(RefPtr<PeerConnectionMedia>(this),
862                    &PeerConnectionMedia::UpdateNetworkState_s, online),
863       NS_DISPATCH_NORMAL);
864 }
865 
UpdateNetworkState_s(bool online)866 void PeerConnectionMedia::UpdateNetworkState_s(bool online) {
867   mIceCtxHdlr->ctx()->UpdateNetworkState(online);
868 }
869 
FlushIceCtxOperationQueueIfReady()870 void PeerConnectionMedia::FlushIceCtxOperationQueueIfReady() {
871   ASSERT_ON_THREAD(mMainThread);
872 
873   if (IsIceCtxReady()) {
874     for (auto& mQueuedIceCtxOperation : mQueuedIceCtxOperations) {
875       GetSTSThread()->Dispatch(mQueuedIceCtxOperation, NS_DISPATCH_NORMAL);
876     }
877     mQueuedIceCtxOperations.clear();
878   }
879 }
880 
PerformOrEnqueueIceCtxOperation(nsIRunnable * runnable)881 void PeerConnectionMedia::PerformOrEnqueueIceCtxOperation(
882     nsIRunnable* runnable) {
883   ASSERT_ON_THREAD(mMainThread);
884 
885   if (IsIceCtxReady()) {
886     GetSTSThread()->Dispatch(runnable, NS_DISPATCH_NORMAL);
887   } else {
888     mQueuedIceCtxOperations.push_back(runnable);
889   }
890 }
891 
GatherIfReady()892 void PeerConnectionMedia::GatherIfReady() {
893   ASSERT_ON_THREAD(mMainThread);
894 
895   nsCOMPtr<nsIRunnable> runnable(
896       WrapRunnable(RefPtr<PeerConnectionMedia>(this),
897                    &PeerConnectionMedia::EnsureIceGathering_s,
898                    GetPrefDefaultAddressOnly(), GetPrefProxyOnly()));
899 
900   PerformOrEnqueueIceCtxOperation(runnable);
901 }
902 
EnsureIceGathering_s(bool aDefaultRouteOnly,bool aProxyOnly)903 void PeerConnectionMedia::EnsureIceGathering_s(bool aDefaultRouteOnly,
904                                                bool aProxyOnly) {
905   if (mProxyServer) {
906     mIceCtxHdlr->ctx()->SetProxyServer(*mProxyServer);
907   } else if (aProxyOnly) {
908     IceGatheringStateChange_s(mIceCtxHdlr->ctx().get(),
909                               NrIceCtx::ICE_CTX_GATHER_COMPLETE);
910     return;
911   }
912 
913   // Make sure we don't call NrIceCtx::StartGathering if we're in e10s mode
914   // and we received no STUN addresses from the parent process.  In the
915   // absence of previously provided STUN addresses, StartGathering will
916   // attempt to gather them (as in non-e10s mode), and this will cause a
917   // sandboxing exception in e10s mode.
918   if (!mStunAddrs.Length() && XRE_IsContentProcess()) {
919     CSFLogInfo(LOGTAG, "%s: No STUN addresses returned from parent process",
920                __FUNCTION__);
921     return;
922   }
923 
924   // Belt and suspenders - in e10s mode, the call below to SetStunAddrs
925   // needs to have the proper flags set on ice ctx.  For non-e10s,
926   // setting those flags happens in StartGathering.  We could probably
927   // just set them here, and only do it here.
928   mIceCtxHdlr->ctx()->SetCtxFlags(aDefaultRouteOnly, aProxyOnly);
929 
930   if (mStunAddrs.Length()) {
931     mIceCtxHdlr->ctx()->SetStunAddrs(mStunAddrs);
932   }
933 
934   // Start gathering, but only if there are streams
935   for (size_t i = 0; i < mIceCtxHdlr->ctx()->GetStreamCount(); ++i) {
936     if (mIceCtxHdlr->ctx()->GetStream(i)) {
937       mIceCtxHdlr->ctx()->StartGathering(aDefaultRouteOnly, aProxyOnly);
938       return;
939     }
940   }
941 
942   // If there are no streams, we're probably in a situation where we've rolled
943   // back while still waiting for our proxy configuration to come back. Make
944   // sure content knows that the rollback has stuck wrt gathering.
945   IceGatheringStateChange_s(mIceCtxHdlr->ctx().get(),
946                             NrIceCtx::ICE_CTX_GATHER_COMPLETE);
947 }
948 
SelfDestruct()949 void PeerConnectionMedia::SelfDestruct() {
950   ASSERT_ON_THREAD(mMainThread);
951 
952   CSFLogDebug(LOGTAG, "%s: ", __FUNCTION__);
953 
954   if (mStunAddrsRequest) {
955     mStunAddrsRequest->Cancel();
956     mStunAddrsRequest = nullptr;
957   }
958 
959   if (mProxyRequest) {
960     mProxyRequest->Cancel(NS_ERROR_ABORT);
961     mProxyRequest = nullptr;
962   }
963 
964   for (auto& transceiver : mTransceivers) {
965     // transceivers are garbage-collected, so we need to poke them to perform
966     // cleanup right now so the appropriate events fire.
967     transceiver->Shutdown_m();
968   }
969 
970   mTransceivers.clear();
971 
972   mQueuedIceCtxOperations.clear();
973 
974   // Shutdown the transport (async)
975   RUN_ON_THREAD(
976       mSTSThread,
977       WrapRunnable(this, &PeerConnectionMedia::ShutdownMediaTransport_s),
978       NS_DISPATCH_NORMAL);
979 
980   CSFLogDebug(LOGTAG, "%s: Media shut down", __FUNCTION__);
981 }
982 
SelfDestruct_m()983 void PeerConnectionMedia::SelfDestruct_m() {
984   CSFLogDebug(LOGTAG, "%s: ", __FUNCTION__);
985 
986   ASSERT_ON_THREAD(mMainThread);
987 
988   mMainThread = nullptr;
989 
990   // Final self-destruct.
991   this->Release();
992 }
993 
ShutdownMediaTransport_s()994 void PeerConnectionMedia::ShutdownMediaTransport_s() {
995   ASSERT_ON_THREAD(mSTSThread);
996 
997   CSFLogDebug(LOGTAG, "%s: ", __FUNCTION__);
998 
999   disconnect_all();
1000   mTransportFlows.clear();
1001 
1002 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
1003   NrIceStats stats = mIceCtxHdlr->Destroy();
1004 
1005   CSFLogDebug(LOGTAG,
1006               "Ice Telemetry: stun (retransmits: %d)"
1007               "   turn (401s: %d   403s: %d   438s: %d)",
1008               stats.stun_retransmits, stats.turn_401s, stats.turn_403s,
1009               stats.turn_438s);
1010 
1011   Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_STUN_RETRANSMITS,
1012                        stats.stun_retransmits);
1013   Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_TURN_401S,
1014                        stats.turn_401s);
1015   Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_TURN_403S,
1016                        stats.turn_403s);
1017   Telemetry::ScalarAdd(Telemetry::ScalarID::WEBRTC_NICER_TURN_438S,
1018                        stats.turn_438s);
1019 #endif
1020 
1021   mIceCtxHdlr = nullptr;
1022 
1023   // we're holding a ref to 'this' that's released by SelfDestruct_m
1024   mMainThread->Dispatch(
1025       WrapRunnable(this, &PeerConnectionMedia::SelfDestruct_m),
1026       NS_DISPATCH_NORMAL);
1027 }
1028 
AddTransceiver(JsepTransceiver * aJsepTransceiver,dom::MediaStreamTrack & aReceiveTrack,dom::MediaStreamTrack * aSendTrack,RefPtr<TransceiverImpl> * aTransceiverImpl)1029 nsresult PeerConnectionMedia::AddTransceiver(
1030     JsepTransceiver* aJsepTransceiver, dom::MediaStreamTrack& aReceiveTrack,
1031     dom::MediaStreamTrack* aSendTrack,
1032     RefPtr<TransceiverImpl>* aTransceiverImpl) {
1033   if (!mCall) {
1034     mCall = WebRtcCallWrapper::Create();
1035   }
1036 
1037   RefPtr<TransceiverImpl> transceiver = new TransceiverImpl(
1038       mParent->GetHandle(), aJsepTransceiver, mMainThread.get(),
1039       mSTSThread.get(), &aReceiveTrack, aSendTrack, mCall.get());
1040 
1041   if (!transceiver->IsValid()) {
1042     return NS_ERROR_FAILURE;
1043   }
1044 
1045   if (aSendTrack) {
1046     // implement checking for peerIdentity (where failure == black/silence)
1047     nsIDocument* doc = mParent->GetWindow()->GetExtantDoc();
1048     if (doc) {
1049       transceiver->UpdateSinkIdentity(nullptr, doc->NodePrincipal(),
1050                                       mParent->GetPeerIdentity());
1051     } else {
1052       MOZ_CRASH();
1053       return NS_ERROR_FAILURE;  // Don't remove this till we know it's safe.
1054     }
1055   }
1056 
1057   mTransceivers.push_back(transceiver);
1058   *aTransceiverImpl = transceiver;
1059 
1060   return NS_OK;
1061 }
1062 
GetTransmitPipelinesMatching(MediaStreamTrack * aTrack,nsTArray<RefPtr<MediaPipeline>> * aPipelines)1063 void PeerConnectionMedia::GetTransmitPipelinesMatching(
1064     MediaStreamTrack* aTrack, nsTArray<RefPtr<MediaPipeline>>* aPipelines) {
1065   for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
1066     if (transceiver->HasSendTrack(aTrack)) {
1067       aPipelines->AppendElement(transceiver->GetSendPipeline());
1068     }
1069   }
1070 
1071   if (!aPipelines->Length()) {
1072     CSFLogWarn(LOGTAG, "%s: none found for %p", __FUNCTION__, aTrack);
1073   }
1074 }
1075 
GetReceivePipelinesMatching(MediaStreamTrack * aTrack,nsTArray<RefPtr<MediaPipeline>> * aPipelines)1076 void PeerConnectionMedia::GetReceivePipelinesMatching(
1077     MediaStreamTrack* aTrack, nsTArray<RefPtr<MediaPipeline>>* aPipelines) {
1078   for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
1079     if (transceiver->HasReceiveTrack(aTrack)) {
1080       aPipelines->AppendElement(transceiver->GetReceivePipeline());
1081     }
1082   }
1083 
1084   if (!aPipelines->Length()) {
1085     CSFLogWarn(LOGTAG, "%s: none found for %p", __FUNCTION__, aTrack);
1086   }
1087 }
1088 
AddRIDExtension(MediaStreamTrack & aRecvTrack,unsigned short aExtensionId)1089 nsresult PeerConnectionMedia::AddRIDExtension(MediaStreamTrack& aRecvTrack,
1090                                               unsigned short aExtensionId) {
1091   for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
1092     if (transceiver->HasReceiveTrack(&aRecvTrack)) {
1093       transceiver->AddRIDExtension(aExtensionId);
1094     }
1095   }
1096   return NS_OK;
1097 }
1098 
AddRIDFilter(MediaStreamTrack & aRecvTrack,const nsAString & aRid)1099 nsresult PeerConnectionMedia::AddRIDFilter(MediaStreamTrack& aRecvTrack,
1100                                            const nsAString& aRid) {
1101   for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
1102     if (transceiver->HasReceiveTrack(&aRecvTrack)) {
1103       transceiver->AddRIDFilter(aRid);
1104     }
1105   }
1106   return NS_OK;
1107 }
1108 
IceGatheringStateChange_s(NrIceCtx * ctx,NrIceCtx::GatheringState state)1109 void PeerConnectionMedia::IceGatheringStateChange_s(
1110     NrIceCtx* ctx, NrIceCtx::GatheringState state) {
1111   ASSERT_ON_THREAD(mSTSThread);
1112 
1113   if (state == NrIceCtx::ICE_CTX_GATHER_COMPLETE) {
1114     // Fire off EndOfLocalCandidates for each stream
1115     for (size_t i = 0;; ++i) {
1116       RefPtr<NrIceMediaStream> stream(ctx->GetStream(i));
1117       if (!stream) {
1118         break;
1119       }
1120 
1121       NrIceCandidate candidate;
1122       NrIceCandidate rtcpCandidate;
1123       GetDefaultCandidates(*stream, &candidate, &rtcpCandidate);
1124       EndOfLocalCandidates(candidate.cand_addr.host, candidate.cand_addr.port,
1125                            rtcpCandidate.cand_addr.host,
1126                            rtcpCandidate.cand_addr.port, i);
1127     }
1128   }
1129 
1130   // ShutdownMediaTransport_s has not run yet because it unhooks this function
1131   // from its signal, which means that SelfDestruct_m has not been dispatched
1132   // yet either, so this PCMedia will still be around when this dispatch reaches
1133   // main.
1134   GetMainThread()->Dispatch(
1135       WrapRunnable(this, &PeerConnectionMedia::IceGatheringStateChange_m, ctx,
1136                    state),
1137       NS_DISPATCH_NORMAL);
1138 }
1139 
IceConnectionStateChange_s(NrIceCtx * ctx,NrIceCtx::ConnectionState state)1140 void PeerConnectionMedia::IceConnectionStateChange_s(
1141     NrIceCtx* ctx, NrIceCtx::ConnectionState state) {
1142   ASSERT_ON_THREAD(mSTSThread);
1143   // ShutdownMediaTransport_s has not run yet because it unhooks this function
1144   // from its signal, which means that SelfDestruct_m has not been dispatched
1145   // yet either, so this PCMedia will still be around when this dispatch reaches
1146   // main.
1147   GetMainThread()->Dispatch(
1148       WrapRunnable(this, &PeerConnectionMedia::IceConnectionStateChange_m, ctx,
1149                    state),
1150       NS_DISPATCH_NORMAL);
1151 }
1152 
OnCandidateFound_s(NrIceMediaStream * aStream,const std::string & aCandidateLine)1153 void PeerConnectionMedia::OnCandidateFound_s(
1154     NrIceMediaStream* aStream, const std::string& aCandidateLine) {
1155   ASSERT_ON_THREAD(mSTSThread);
1156   MOZ_ASSERT(aStream);
1157   MOZ_RELEASE_ASSERT(mIceCtxHdlr);
1158 
1159   CSFLogDebug(LOGTAG, "%s: %s", __FUNCTION__, aStream->name().c_str());
1160 
1161   NrIceCandidate candidate;
1162   NrIceCandidate rtcpCandidate;
1163   GetDefaultCandidates(*aStream, &candidate, &rtcpCandidate);
1164 
1165   // ShutdownMediaTransport_s has not run yet because it unhooks this function
1166   // from its signal, which means that SelfDestruct_m has not been dispatched
1167   // yet either, so this PCMedia will still be around when this dispatch reaches
1168   // main.
1169   GetMainThread()->Dispatch(
1170       WrapRunnable(this, &PeerConnectionMedia::OnCandidateFound_m,
1171                    aCandidateLine, candidate.cand_addr.host,
1172                    candidate.cand_addr.port, rtcpCandidate.cand_addr.host,
1173                    rtcpCandidate.cand_addr.port, aStream->GetLevel()),
1174       NS_DISPATCH_NORMAL);
1175 }
1176 
EndOfLocalCandidates(const std::string & aDefaultAddr,uint16_t aDefaultPort,const std::string & aDefaultRtcpAddr,uint16_t aDefaultRtcpPort,uint16_t aMLine)1177 void PeerConnectionMedia::EndOfLocalCandidates(
1178     const std::string& aDefaultAddr, uint16_t aDefaultPort,
1179     const std::string& aDefaultRtcpAddr, uint16_t aDefaultRtcpPort,
1180     uint16_t aMLine) {
1181   GetMainThread()->Dispatch(
1182       WrapRunnable(this, &PeerConnectionMedia::EndOfLocalCandidates_m,
1183                    aDefaultAddr, aDefaultPort, aDefaultRtcpAddr,
1184                    aDefaultRtcpPort, aMLine),
1185       NS_DISPATCH_NORMAL);
1186 }
1187 
GetDefaultCandidates(const NrIceMediaStream & aStream,NrIceCandidate * aCandidate,NrIceCandidate * aRtcpCandidate)1188 void PeerConnectionMedia::GetDefaultCandidates(const NrIceMediaStream& aStream,
1189                                                NrIceCandidate* aCandidate,
1190                                                NrIceCandidate* aRtcpCandidate) {
1191   nsresult res = aStream.GetDefaultCandidate(1, aCandidate);
1192   // Optional; component won't exist if doing rtcp-mux
1193   if (NS_FAILED(aStream.GetDefaultCandidate(2, aRtcpCandidate))) {
1194     aRtcpCandidate->cand_addr.host.clear();
1195     aRtcpCandidate->cand_addr.port = 0;
1196   }
1197   if (NS_FAILED(res)) {
1198     aCandidate->cand_addr.host.clear();
1199     aCandidate->cand_addr.port = 0;
1200     CSFLogError(LOGTAG,
1201                 "%s: GetDefaultCandidates failed for level %u, "
1202                 "res=%u",
1203                 __FUNCTION__, static_cast<unsigned>(aStream.GetLevel()),
1204                 static_cast<unsigned>(res));
1205   }
1206 }
1207 
IceGatheringStateChange_m(NrIceCtx * ctx,NrIceCtx::GatheringState state)1208 void PeerConnectionMedia::IceGatheringStateChange_m(
1209     NrIceCtx* ctx, NrIceCtx::GatheringState state) {
1210   ASSERT_ON_THREAD(mMainThread);
1211   SignalIceGatheringStateChange(ctx, state);
1212 }
1213 
IceConnectionStateChange_m(NrIceCtx * ctx,NrIceCtx::ConnectionState state)1214 void PeerConnectionMedia::IceConnectionStateChange_m(
1215     NrIceCtx* ctx, NrIceCtx::ConnectionState state) {
1216   ASSERT_ON_THREAD(mMainThread);
1217   SignalIceConnectionStateChange(ctx, state);
1218 }
1219 
IceStreamReady_s(NrIceMediaStream * aStream)1220 void PeerConnectionMedia::IceStreamReady_s(NrIceMediaStream* aStream) {
1221   MOZ_ASSERT(aStream);
1222 
1223   CSFLogDebug(LOGTAG, "%s: %s", __FUNCTION__, aStream->name().c_str());
1224 }
1225 
OnCandidateFound_m(const std::string & aCandidateLine,const std::string & aDefaultAddr,uint16_t aDefaultPort,const std::string & aDefaultRtcpAddr,uint16_t aDefaultRtcpPort,uint16_t aMLine)1226 void PeerConnectionMedia::OnCandidateFound_m(
1227     const std::string& aCandidateLine, const std::string& aDefaultAddr,
1228     uint16_t aDefaultPort, const std::string& aDefaultRtcpAddr,
1229     uint16_t aDefaultRtcpPort, uint16_t aMLine) {
1230   ASSERT_ON_THREAD(mMainThread);
1231   if (!aDefaultAddr.empty()) {
1232     SignalUpdateDefaultCandidate(aDefaultAddr, aDefaultPort, aDefaultRtcpAddr,
1233                                  aDefaultRtcpPort, aMLine);
1234   }
1235   SignalCandidate(aCandidateLine, aMLine);
1236 }
1237 
EndOfLocalCandidates_m(const std::string & aDefaultAddr,uint16_t aDefaultPort,const std::string & aDefaultRtcpAddr,uint16_t aDefaultRtcpPort,uint16_t aMLine)1238 void PeerConnectionMedia::EndOfLocalCandidates_m(
1239     const std::string& aDefaultAddr, uint16_t aDefaultPort,
1240     const std::string& aDefaultRtcpAddr, uint16_t aDefaultRtcpPort,
1241     uint16_t aMLine) {
1242   ASSERT_ON_THREAD(mMainThread);
1243   if (!aDefaultAddr.empty()) {
1244     SignalUpdateDefaultCandidate(aDefaultAddr, aDefaultPort, aDefaultRtcpAddr,
1245                                  aDefaultRtcpPort, aMLine);
1246   }
1247   SignalEndOfLocalCandidates(aMLine);
1248 }
1249 
DtlsConnected_s(TransportLayer * layer,TransportLayer::State state)1250 void PeerConnectionMedia::DtlsConnected_s(TransportLayer* layer,
1251                                           TransportLayer::State state) {
1252   MOZ_ASSERT(layer->id() == "dtls");
1253   TransportLayerDtls* dtlsLayer = static_cast<TransportLayerDtls*>(layer);
1254   dtlsLayer->SignalStateChange.disconnect(this);
1255 
1256   bool privacyRequested = (dtlsLayer->GetNegotiatedAlpn() == "c-webrtc");
1257   GetMainThread()->Dispatch(
1258       WrapRunnableNM(&PeerConnectionMedia::DtlsConnected_m, mParentHandle,
1259                      privacyRequested),
1260       NS_DISPATCH_NORMAL);
1261 }
1262 
DtlsConnected_m(const std::string & aParentHandle,bool aPrivacyRequested)1263 void PeerConnectionMedia::DtlsConnected_m(const std::string& aParentHandle,
1264                                           bool aPrivacyRequested) {
1265   PeerConnectionWrapper pcWrapper(aParentHandle);
1266   PeerConnectionImpl* pc = pcWrapper.impl();
1267   if (pc) {
1268     pc->SetDtlsConnected(aPrivacyRequested);
1269   }
1270 }
1271 
AddTransportFlow(int aIndex,bool aRtcp,const RefPtr<TransportFlow> & aFlow)1272 void PeerConnectionMedia::AddTransportFlow(int aIndex, bool aRtcp,
1273                                            const RefPtr<TransportFlow>& aFlow) {
1274   int index_inner = GetTransportFlowIndex(aIndex, aRtcp);
1275 
1276   MOZ_ASSERT(!mTransportFlows[index_inner]);
1277   mTransportFlows[index_inner] = aFlow;
1278 
1279   GetSTSThread()->Dispatch(
1280       WrapRunnable(this, &PeerConnectionMedia::ConnectDtlsListener_s, aFlow),
1281       NS_DISPATCH_NORMAL);
1282 }
1283 
RemoveTransportFlow(int aIndex,bool aRtcp)1284 void PeerConnectionMedia::RemoveTransportFlow(int aIndex, bool aRtcp) {
1285   int index_inner = GetTransportFlowIndex(aIndex, aRtcp);
1286   NS_ProxyRelease("PeerConnectionMedia::mTransportFlows", GetSTSThread(),
1287                   mTransportFlows[index_inner].forget());
1288 }
1289 
ConnectDtlsListener_s(const RefPtr<TransportFlow> & aFlow)1290 void PeerConnectionMedia::ConnectDtlsListener_s(
1291     const RefPtr<TransportFlow>& aFlow) {
1292   TransportLayer* dtls = aFlow->GetLayer(TransportLayerDtls::ID());
1293   if (dtls) {
1294     dtls->SignalStateChange.connect(this,
1295                                     &PeerConnectionMedia::DtlsConnected_s);
1296   }
1297 }
1298 
1299 /**
1300  * Tells you if any local track is isolated to a specific peer identity.
1301  * Obviously, we want all the tracks to be isolated equally so that they can
1302  * all be sent or not.  We check once when we are setting a local description
1303  * and that determines if we flip the "privacy requested" bit on.  Once the bit
1304  * is on, all media originating from this peer connection is isolated.
1305  *
1306  * @returns true if any track has a peerIdentity set on it
1307  */
AnyLocalTrackHasPeerIdentity() const1308 bool PeerConnectionMedia::AnyLocalTrackHasPeerIdentity() const {
1309   ASSERT_ON_THREAD(mMainThread);
1310 
1311   for (const RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
1312     if (transceiver->GetSendTrack() &&
1313         transceiver->GetSendTrack()->GetPeerIdentity()) {
1314       return true;
1315     }
1316   }
1317   return false;
1318 }
1319 
UpdateRemoteStreamPrincipals_m(nsIPrincipal * aPrincipal)1320 void PeerConnectionMedia::UpdateRemoteStreamPrincipals_m(
1321     nsIPrincipal* aPrincipal) {
1322   ASSERT_ON_THREAD(mMainThread);
1323 
1324   for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
1325     transceiver->UpdatePrincipal(aPrincipal);
1326   }
1327 }
1328 
UpdateSinkIdentity_m(const MediaStreamTrack * aTrack,nsIPrincipal * aPrincipal,const PeerIdentity * aSinkIdentity)1329 void PeerConnectionMedia::UpdateSinkIdentity_m(
1330     const MediaStreamTrack* aTrack, nsIPrincipal* aPrincipal,
1331     const PeerIdentity* aSinkIdentity) {
1332   ASSERT_ON_THREAD(mMainThread);
1333 
1334   for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
1335     transceiver->UpdateSinkIdentity(aTrack, aPrincipal, aSinkIdentity);
1336   }
1337 }
1338 
AnyCodecHasPluginID(uint64_t aPluginID)1339 bool PeerConnectionMedia::AnyCodecHasPluginID(uint64_t aPluginID) {
1340   for (RefPtr<TransceiverImpl>& transceiver : mTransceivers) {
1341     if (transceiver->ConduitHasPluginID(aPluginID)) {
1342       return true;
1343     }
1344   }
1345   return false;
1346 }
1347 
1348 }  // namespace mozilla
1349