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 "WebrtcGlobalInformation.h"
6 #include "mozilla/media/webrtc/WebrtcGlobal.h"
7 #include "WebrtcGlobalChild.h"
8 #include "WebrtcGlobalParent.h"
9 
10 #include <algorithm>
11 #include <vector>
12 #include <type_traits>
13 
14 #include "mozilla/dom/WebrtcGlobalInformationBinding.h"
15 #include "mozilla/dom/RTCStatsReportBinding.h"  // for RTCStatsReportInternal
16 #include "mozilla/dom/ContentChild.h"
17 
18 #include "nsNetCID.h"               // NS_SOCKETTRANSPORTSERVICE_CONTRACTID
19 #include "nsServiceManagerUtils.h"  // do_GetService
20 #include "mozilla/ErrorResult.h"
21 #include "nsProxyRelease.h"  // nsMainThreadPtrHolder
22 #include "mozilla/Telemetry.h"
23 #include "mozilla/Unused.h"
24 #include "mozilla/RefPtr.h"
25 #include "mozilla/ClearOnShutdown.h"
26 
27 #include "common/browser_logging/WebRtcLog.h"
28 #include "transport/runnable_utils.h"
29 #include "MediaTransportHandler.h"
30 #include "PeerConnectionCtx.h"
31 #include "PeerConnectionImpl.h"
32 
33 namespace mozilla::dom {
34 
35 typedef nsMainThreadPtrHandle<WebrtcGlobalStatisticsCallback>
36     StatsRequestCallback;
37 
38 typedef nsMainThreadPtrHandle<WebrtcGlobalLoggingCallback> LogRequestCallback;
39 
40 class WebrtcContentParents {
41  public:
42   static WebrtcGlobalParent* Alloc();
43   static void Dealloc(WebrtcGlobalParent* aParent);
Empty()44   static bool Empty() { return sContentParents.empty(); }
GetAll()45   static const std::vector<RefPtr<WebrtcGlobalParent>>& GetAll() {
46     return sContentParents;
47   }
48 
49  private:
50   static std::vector<RefPtr<WebrtcGlobalParent>> sContentParents;
51   WebrtcContentParents() = delete;
52   WebrtcContentParents(const WebrtcContentParents&) = delete;
53   WebrtcContentParents& operator=(const WebrtcContentParents&) = delete;
54 };
55 
56 std::vector<RefPtr<WebrtcGlobalParent>> WebrtcContentParents::sContentParents;
57 
Alloc()58 WebrtcGlobalParent* WebrtcContentParents::Alloc() {
59   RefPtr<WebrtcGlobalParent> cp = new WebrtcGlobalParent;
60   sContentParents.push_back(cp);
61   return cp.get();
62 }
63 
Dealloc(WebrtcGlobalParent * aParent)64 void WebrtcContentParents::Dealloc(WebrtcGlobalParent* aParent) {
65   if (aParent) {
66     aParent->mShutdown = true;
67     auto cp =
68         std::find(sContentParents.begin(), sContentParents.end(), aParent);
69     if (cp != sContentParents.end()) {
70       sContentParents.erase(cp);
71     }
72   }
73 }
74 
GetPeerConnectionCtx()75 static PeerConnectionCtx* GetPeerConnectionCtx() {
76   if (PeerConnectionCtx::isActive()) {
77     MOZ_ASSERT(PeerConnectionCtx::GetInstance());
78     return PeerConnectionCtx::GetInstance();
79   }
80   return nullptr;
81 }
82 
83 static RefPtr<PWebrtcGlobalParent::GetStatsPromise>
GetStatsPromiseForThisProcess(const nsAString & aPcIdFilter)84 GetStatsPromiseForThisProcess(const nsAString& aPcIdFilter) {
85   nsTArray<RefPtr<dom::RTCStatsReportPromise>> promises;
86 
87   PeerConnectionCtx* ctx = GetPeerConnectionCtx();
88   if (ctx) {
89     // Grab stats for non-closed PCs
90     for (const auto& [id, pc] : ctx->GetPeerConnections()) {
91       if (aPcIdFilter.IsEmpty() || aPcIdFilter.EqualsASCII(id.c_str())) {
92         if (pc->HasMedia()) {
93           promises.AppendElement(pc->GetStats(nullptr, true));
94         }
95       }
96     }
97 
98     // Grab stats for closed PCs
99     for (const auto& report : ctx->mStatsForClosedPeerConnections) {
100       if (aPcIdFilter.IsEmpty() || aPcIdFilter == report.mPcid) {
101         promises.AppendElement(dom::RTCStatsReportPromise::CreateAndResolve(
102             MakeUnique<dom::RTCStatsReportInternal>(report), __func__));
103       }
104     }
105   }
106 
107   auto UnwrapUniquePtrs = [](dom::RTCStatsReportPromise::AllSettledPromiseType::
108                                  ResolveOrRejectValue&& aResult) {
109     nsTArray<dom::RTCStatsReportInternal> reports;
110     MOZ_RELEASE_ASSERT(aResult.IsResolve(), "AllSettled should never reject!");
111     for (auto& reportResult : aResult.ResolveValue()) {
112       if (reportResult.IsResolve()) {
113         reports.AppendElement(*reportResult.ResolveValue());
114       }
115     }
116     return PWebrtcGlobalParent::GetStatsPromise::CreateAndResolve(
117         std::move(reports), __func__);
118   };
119 
120   return dom::RTCStatsReportPromise::AllSettled(
121              GetMainThreadSerialEventTarget(), promises)
122       ->Then(GetMainThreadSerialEventTarget(), __func__,
123              std::move(UnwrapUniquePtrs));
124 }
125 
GetWebrtcGlobalStatsStash()126 static nsTArray<dom::RTCStatsReportInternal>& GetWebrtcGlobalStatsStash() {
127   static StaticAutoPtr<nsTArray<dom::RTCStatsReportInternal>> sStash;
128   if (!sStash) {
129     sStash = new nsTArray<dom::RTCStatsReportInternal>();
130     ClearOnShutdown(&sStash);
131   }
132   return *sStash;
133 }
134 
GetWebrtcGlobalLogStash()135 static std::map<int32_t, dom::Sequence<nsString>>& GetWebrtcGlobalLogStash() {
136   static StaticAutoPtr<std::map<int32_t, dom::Sequence<nsString>>> sStash;
137   if (!sStash) {
138     sStash = new std::map<int32_t, dom::Sequence<nsString>>();
139     ClearOnShutdown(&sStash);
140   }
141   return *sStash;
142 }
143 
ClearClosedStats()144 static void ClearClosedStats() {
145   GetWebrtcGlobalStatsStash().Clear();
146   PeerConnectionCtx* ctx = GetPeerConnectionCtx();
147 
148   if (ctx) {
149     ctx->mStatsForClosedPeerConnections.Clear();
150   }
151 }
152 
ClearAllStats(const GlobalObject & aGlobal)153 void WebrtcGlobalInformation::ClearAllStats(const GlobalObject& aGlobal) {
154   if (!NS_IsMainThread()) {
155     return;
156   }
157 
158   // Chrome-only API
159   MOZ_ASSERT(XRE_IsParentProcess());
160 
161   if (!WebrtcContentParents::Empty()) {
162     // Pass on the request to any content process based PeerConnections.
163     for (const auto& cp : WebrtcContentParents::GetAll()) {
164       Unused << cp->SendClearStats();
165     }
166   }
167 
168   // Flush the history for the chrome process
169   ClearClosedStats();
170 }
171 
GetAllStats(const GlobalObject & aGlobal,WebrtcGlobalStatisticsCallback & aStatsCallback,const Optional<nsAString> & pcIdFilter,ErrorResult & aRv)172 void WebrtcGlobalInformation::GetAllStats(
173     const GlobalObject& aGlobal, WebrtcGlobalStatisticsCallback& aStatsCallback,
174     const Optional<nsAString>& pcIdFilter, ErrorResult& aRv) {
175   if (!NS_IsMainThread()) {
176     aRv.Throw(NS_ERROR_NOT_SAME_THREAD);
177     return;
178   }
179 
180   MOZ_ASSERT(XRE_IsParentProcess());
181 
182   nsTArray<RefPtr<PWebrtcGlobalParent::GetStatsPromise>> statsPromises;
183 
184   nsString filter;
185   if (pcIdFilter.WasPassed()) {
186     filter = pcIdFilter.Value();
187   }
188 
189   for (const auto& cp : WebrtcContentParents::GetAll()) {
190     statsPromises.AppendElement(cp->SendGetStats(filter));
191   }
192 
193   // Stats from this (the parent) process. How long do we keep supporting this?
194   statsPromises.AppendElement(GetStatsPromiseForThisProcess(filter));
195 
196   // CallbackObject does not support threadsafe refcounting, and must be
197   // used and destroyed on main.
198   StatsRequestCallback callbackHandle(
199       new nsMainThreadPtrHolder<WebrtcGlobalStatisticsCallback>(
200           "WebrtcGlobalStatisticsCallback", &aStatsCallback));
201 
202   auto FlattenThenStashThenCallback =
203       [callbackHandle,
204        filter](PWebrtcGlobalParent::GetStatsPromise::AllSettledPromiseType::
205                    ResolveOrRejectValue&& aResult) MOZ_CAN_RUN_SCRIPT_BOUNDARY {
206         std::set<nsString> pcids;
207         WebrtcGlobalStatisticsReport flattened;
208         MOZ_RELEASE_ASSERT(aResult.IsResolve(),
209                            "AllSettled should never reject!");
210         for (auto& contentProcessResult : aResult.ResolveValue()) {
211           // TODO: Report rejection on individual content processes someday?
212           if (contentProcessResult.IsResolve()) {
213             for (auto& pcStats : contentProcessResult.ResolveValue()) {
214               pcids.insert(pcStats.mPcid);
215               if (!flattened.mReports.AppendElement(std::move(pcStats),
216                                                     fallible)) {
217                 mozalloc_handle_oom(0);
218               }
219             }
220           }
221         }
222 
223         if (filter.IsEmpty()) {
224           // Unfiltered is pretty simple; add stuff from stash that is
225           // missing, then stomp the stash with the new reports.
226           for (auto& pcStats : GetWebrtcGlobalStatsStash()) {
227             if (!pcids.count(pcStats.mPcid)) {
228               // Stats from a closed PC or stopped content process.
229               // Content process may have gone away before we got to update
230               // this.
231               pcStats.mClosed = true;
232               if (!flattened.mReports.AppendElement(std::move(pcStats),
233                                                     fallible)) {
234                 mozalloc_handle_oom(0);
235               }
236             }
237           }
238           GetWebrtcGlobalStatsStash() = flattened.mReports;
239         } else {
240           // Filtered is slightly more complex
241           if (flattened.mReports.IsEmpty()) {
242             // Find entry from stash and add it to report
243             for (auto& pcStats : GetWebrtcGlobalStatsStash()) {
244               if (pcStats.mPcid == filter) {
245                 pcStats.mClosed = true;
246                 if (!flattened.mReports.AppendElement(std::move(pcStats),
247                                                       fallible)) {
248                   mozalloc_handle_oom(0);
249                 }
250               }
251             }
252           } else {
253             // Find entries in stash, remove them, and then add new entries
254             for (size_t i = 0; i < GetWebrtcGlobalStatsStash().Length();) {
255               auto& pcStats = GetWebrtcGlobalStatsStash()[i];
256               if (pcStats.mPcid == filter) {
257                 GetWebrtcGlobalStatsStash().RemoveElementAt(i);
258               } else {
259                 ++i;
260               }
261             }
262             GetWebrtcGlobalStatsStash().AppendElements(flattened.mReports);
263           }
264         }
265 
266         IgnoredErrorResult rv;
267         callbackHandle->Call(flattened, rv);
268       };
269 
270   PWebrtcGlobalParent::GetStatsPromise::AllSettled(
271       GetMainThreadSerialEventTarget(), statsPromises)
272       ->Then(GetMainThreadSerialEventTarget(), __func__,
273              std::move(FlattenThenStashThenCallback));
274 
275   aRv = NS_OK;
276 }
277 
GetLogPromise()278 static RefPtr<PWebrtcGlobalParent::GetLogPromise> GetLogPromise() {
279   PeerConnectionCtx* ctx = GetPeerConnectionCtx();
280   if (!ctx) {
281     // This process has never created a PeerConnection, so no ICE logging.
282     return PWebrtcGlobalParent::GetLogPromise::CreateAndResolve(
283         Sequence<nsString>(), __func__);
284   }
285 
286   nsresult rv;
287   nsCOMPtr<nsISerialEventTarget> stsThread =
288       do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
289 
290   if (NS_WARN_IF(NS_FAILED(rv) || !stsThread)) {
291     return PWebrtcGlobalParent::GetLogPromise::CreateAndResolve(
292         Sequence<nsString>(), __func__);
293   }
294 
295   RefPtr<MediaTransportHandler> transportHandler = ctx->GetTransportHandler();
296 
297   auto AddMarkers =
298       [](MediaTransportHandler::IceLogPromise::ResolveOrRejectValue&& aValue) {
299         nsString pid;
300         pid.AppendInt(getpid());
301         Sequence<nsString> logs;
302         if (aValue.IsResolve() && !aValue.ResolveValue().IsEmpty()) {
303           bool ok = logs.AppendElement(
304               u"+++++++ BEGIN (process id "_ns + pid + u") ++++++++"_ns,
305               fallible);
306           ok &=
307               !!logs.AppendElements(std::move(aValue.ResolveValue()), fallible);
308           ok &= !!logs.AppendElement(
309               u"+++++++ END (process id "_ns + pid + u") ++++++++"_ns,
310               fallible);
311           if (!ok) {
312             mozalloc_handle_oom(0);
313           }
314         }
315         return PWebrtcGlobalParent::GetLogPromise::CreateAndResolve(
316             std::move(logs), __func__);
317       };
318 
319   return transportHandler->GetIceLog(nsCString())
320       ->Then(GetMainThreadSerialEventTarget(), __func__, std::move(AddMarkers));
321 }
322 
RunLogClear()323 static nsresult RunLogClear() {
324   PeerConnectionCtx* ctx = GetPeerConnectionCtx();
325   if (!ctx) {
326     // This process has never created a PeerConnection, so no ICE logging.
327     return NS_OK;
328   }
329 
330   nsresult rv;
331   nsCOMPtr<nsISerialEventTarget> stsThread =
332       do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
333 
334   if (NS_FAILED(rv)) {
335     return rv;
336   }
337   if (!stsThread) {
338     return NS_ERROR_FAILURE;
339   }
340 
341   RefPtr<MediaTransportHandler> transportHandler = ctx->GetTransportHandler();
342 
343   return RUN_ON_THREAD(
344       stsThread,
345       WrapRunnable(transportHandler, &MediaTransportHandler::ClearIceLog),
346       NS_DISPATCH_NORMAL);
347 }
348 
ClearLogging(const GlobalObject & aGlobal)349 void WebrtcGlobalInformation::ClearLogging(const GlobalObject& aGlobal) {
350   if (!NS_IsMainThread()) {
351     return;
352   }
353 
354   // Chrome-only API
355   MOZ_ASSERT(XRE_IsParentProcess());
356   GetWebrtcGlobalLogStash().clear();
357 
358   if (!WebrtcContentParents::Empty()) {
359     // Clear content process signaling logs
360     for (const auto& cp : WebrtcContentParents::GetAll()) {
361       Unused << cp->SendClearLog();
362     }
363   }
364 
365   // Clear chrome process signaling logs
366   Unused << RunLogClear();
367 }
368 
UpdateLogStash()369 static RefPtr<GenericPromise> UpdateLogStash() {
370   nsTArray<RefPtr<GenericPromise>> logPromises;
371   MOZ_ASSERT(XRE_IsParentProcess());
372   for (const auto& cp : WebrtcContentParents::GetAll()) {
373     auto StashLog =
374         [id = cp->Id() * 2 /* Make sure 1 isn't used */](
375             PWebrtcGlobalParent::GetLogPromise::ResolveOrRejectValue&& aValue) {
376           if (aValue.IsResolve() && !aValue.ResolveValue().IsEmpty()) {
377             GetWebrtcGlobalLogStash()[id] = aValue.ResolveValue();
378           }
379           return GenericPromise::CreateAndResolve(true, __func__);
380         };
381     logPromises.AppendElement(cp->SendGetLog()->Then(
382         GetMainThreadSerialEventTarget(), __func__, std::move(StashLog)));
383   }
384 
385   // Get ICE logging for this (the parent) process. How long do we support this?
386   logPromises.AppendElement(GetLogPromise()->Then(
387       GetMainThreadSerialEventTarget(), __func__,
388       [](PWebrtcGlobalParent::GetLogPromise::ResolveOrRejectValue&& aValue) {
389         if (aValue.IsResolve()) {
390           GetWebrtcGlobalLogStash()[1] = aValue.ResolveValue();
391         }
392         return GenericPromise::CreateAndResolve(true, __func__);
393       }));
394 
395   return GenericPromise::AllSettled(GetMainThreadSerialEventTarget(),
396                                     logPromises)
397       ->Then(GetMainThreadSerialEventTarget(), __func__,
398              [](GenericPromise::AllSettledPromiseType::ResolveOrRejectValue&&
399                     aValue) {
400                // We don't care about the value, since we're just going to copy
401                // what is in the stash. This ignores failures too, which is what
402                // we want.
403                return GenericPromise::CreateAndResolve(true, __func__);
404              });
405 }
406 
GetLogging(const GlobalObject & aGlobal,const nsAString & aPattern,WebrtcGlobalLoggingCallback & aLoggingCallback,ErrorResult & aRv)407 void WebrtcGlobalInformation::GetLogging(
408     const GlobalObject& aGlobal, const nsAString& aPattern,
409     WebrtcGlobalLoggingCallback& aLoggingCallback, ErrorResult& aRv) {
410   if (!NS_IsMainThread()) {
411     aRv.Throw(NS_ERROR_NOT_SAME_THREAD);
412     return;
413   }
414 
415   MOZ_ASSERT(XRE_IsParentProcess());
416 
417   nsAutoCString pattern;
418   CopyUTF16toUTF8(aPattern, pattern);
419 
420   // CallbackObject does not support threadsafe refcounting, and must be
421   // destroyed on main.
422   LogRequestCallback callbackHandle(
423       new nsMainThreadPtrHolder<WebrtcGlobalLoggingCallback>(
424           "WebrtcGlobalLoggingCallback", &aLoggingCallback));
425 
426   auto FilterThenCallback =
427       [pattern, callbackHandle](GenericPromise::ResolveOrRejectValue&& aValue)
428           MOZ_CAN_RUN_SCRIPT_BOUNDARY {
429             dom::Sequence<nsString> flattened;
430             for (const auto& [id, log] : GetWebrtcGlobalLogStash()) {
431               (void)id;
432               for (const auto& line : log) {
433                 if (pattern.IsEmpty() || (line.Find(pattern) != kNotFound)) {
434                   if (!flattened.AppendElement(line, fallible)) {
435                     mozalloc_handle_oom(0);
436                   }
437                 }
438               }
439             }
440             IgnoredErrorResult rv;
441             callbackHandle->Call(flattened, rv);
442           };
443 
444   UpdateLogStash()->Then(GetMainThreadSerialEventTarget(), __func__,
445                          std::move(FilterThenCallback));
446   aRv = NS_OK;
447 }
448 
449 static int32_t sLastSetLevel = 0;
450 static bool sLastAECDebug = false;
451 static Maybe<nsCString> sAecDebugLogDir;
452 
SetDebugLevel(const GlobalObject & aGlobal,int32_t aLevel)453 void WebrtcGlobalInformation::SetDebugLevel(const GlobalObject& aGlobal,
454                                             int32_t aLevel) {
455   if (aLevel) {
456     StartWebRtcLog(mozilla::LogLevel(aLevel));
457   } else {
458     StopWebRtcLog();
459   }
460   sLastSetLevel = aLevel;
461 
462   for (const auto& cp : WebrtcContentParents::GetAll()) {
463     Unused << cp->SendSetDebugMode(aLevel);
464   }
465 }
466 
DebugLevel(const GlobalObject & aGlobal)467 int32_t WebrtcGlobalInformation::DebugLevel(const GlobalObject& aGlobal) {
468   return sLastSetLevel;
469 }
470 
SetAecDebug(const GlobalObject & aGlobal,bool aEnable)471 void WebrtcGlobalInformation::SetAecDebug(const GlobalObject& aGlobal,
472                                           bool aEnable) {
473   if (aEnable) {
474     sAecDebugLogDir = Some(StartAecLog());
475   } else {
476     StopAecLog();
477   }
478 
479   sLastAECDebug = aEnable;
480 
481   for (const auto& cp : WebrtcContentParents::GetAll()) {
482     Unused << cp->SendSetAecLogging(aEnable);
483   }
484 }
485 
AecDebug(const GlobalObject & aGlobal)486 bool WebrtcGlobalInformation::AecDebug(const GlobalObject& aGlobal) {
487   return sLastAECDebug;
488 }
489 
GetAecDebugLogDir(const GlobalObject & aGlobal,nsAString & aDir)490 void WebrtcGlobalInformation::GetAecDebugLogDir(const GlobalObject& aGlobal,
491                                                 nsAString& aDir) {
492   aDir = NS_ConvertASCIItoUTF16(sAecDebugLogDir.valueOr(""_ns));
493 }
494 
Alloc()495 WebrtcGlobalParent* WebrtcGlobalParent::Alloc() {
496   return WebrtcContentParents::Alloc();
497 }
498 
Dealloc(WebrtcGlobalParent * aActor)499 bool WebrtcGlobalParent::Dealloc(WebrtcGlobalParent* aActor) {
500   WebrtcContentParents::Dealloc(aActor);
501   return true;
502 }
503 
ActorDestroy(ActorDestroyReason aWhy)504 void WebrtcGlobalParent::ActorDestroy(ActorDestroyReason aWhy) {
505   mShutdown = true;
506 }
507 
Recv__delete__()508 mozilla::ipc::IPCResult WebrtcGlobalParent::Recv__delete__() {
509   return IPC_OK();
510 }
511 
WebrtcGlobalParent()512 MOZ_IMPLICIT WebrtcGlobalParent::WebrtcGlobalParent() : mShutdown(false) {
513   MOZ_COUNT_CTOR(WebrtcGlobalParent);
514 }
515 
~WebrtcGlobalParent()516 MOZ_IMPLICIT WebrtcGlobalParent::~WebrtcGlobalParent() {
517   MOZ_COUNT_DTOR(WebrtcGlobalParent);
518 }
519 
RecvGetStats(const nsString & aPcIdFilter,GetStatsResolver && aResolve)520 mozilla::ipc::IPCResult WebrtcGlobalChild::RecvGetStats(
521     const nsString& aPcIdFilter, GetStatsResolver&& aResolve) {
522   if (!mShutdown) {
523     GetStatsPromiseForThisProcess(aPcIdFilter)
524         ->Then(
525             GetMainThreadSerialEventTarget(), __func__,
526             [resolve = std::move(aResolve)](
527                 nsTArray<dom::RTCStatsReportInternal>&& aReports) {
528               resolve(std::move(aReports));
529             },
530             []() { MOZ_CRASH(); });
531     return IPC_OK();
532   }
533 
534   aResolve(nsTArray<RTCStatsReportInternal>());
535   return IPC_OK();
536 }
537 
RecvClearStats()538 mozilla::ipc::IPCResult WebrtcGlobalChild::RecvClearStats() {
539   if (mShutdown) {
540     return IPC_OK();
541   }
542 
543   ClearClosedStats();
544   return IPC_OK();
545 }
546 
RecvGetLog(GetLogResolver && aResolve)547 mozilla::ipc::IPCResult WebrtcGlobalChild::RecvGetLog(
548     GetLogResolver&& aResolve) {
549   if (mShutdown) {
550     aResolve(Sequence<nsString>());
551     return IPC_OK();
552   }
553 
554   GetLogPromise()->Then(
555       GetMainThreadSerialEventTarget(), __func__,
556       [aResolve = std::move(aResolve)](
557           PWebrtcGlobalParent::GetLogPromise::ResolveOrRejectValue&& aValue) {
558         if (aValue.IsResolve()) {
559           aResolve(aValue.ResolveValue());
560         } else {
561           aResolve(Sequence<nsString>());
562         }
563       });
564 
565   return IPC_OK();
566 }
567 
RecvClearLog()568 mozilla::ipc::IPCResult WebrtcGlobalChild::RecvClearLog() {
569   if (mShutdown) {
570     return IPC_OK();
571   }
572 
573   RunLogClear();
574   return IPC_OK();
575 }
576 
RecvSetAecLogging(const bool & aEnable)577 mozilla::ipc::IPCResult WebrtcGlobalChild::RecvSetAecLogging(
578     const bool& aEnable) {
579   if (!mShutdown) {
580     if (aEnable) {
581       StartAecLog();
582     } else {
583       StopAecLog();
584     }
585   }
586   return IPC_OK();
587 }
588 
RecvSetDebugMode(const int & aLevel)589 mozilla::ipc::IPCResult WebrtcGlobalChild::RecvSetDebugMode(const int& aLevel) {
590   if (!mShutdown) {
591     if (aLevel) {
592       StartWebRtcLog(mozilla::LogLevel(aLevel));
593     } else {
594       StopWebRtcLog();
595     }
596   }
597   return IPC_OK();
598 }
599 
Create()600 WebrtcGlobalChild* WebrtcGlobalChild::Create() {
601   WebrtcGlobalChild* child = static_cast<WebrtcGlobalChild*>(
602       ContentChild::GetSingleton()->SendPWebrtcGlobalConstructor());
603   return child;
604 }
605 
ActorDestroy(ActorDestroyReason aWhy)606 void WebrtcGlobalChild::ActorDestroy(ActorDestroyReason aWhy) {
607   mShutdown = true;
608 }
609 
WebrtcGlobalChild()610 MOZ_IMPLICIT WebrtcGlobalChild::WebrtcGlobalChild() : mShutdown(false) {
611   MOZ_COUNT_CTOR(WebrtcGlobalChild);
612 }
613 
~WebrtcGlobalChild()614 MOZ_IMPLICIT WebrtcGlobalChild::~WebrtcGlobalChild() {
615   MOZ_COUNT_DTOR(WebrtcGlobalChild);
616 }
617 
StoreLongTermICEStatisticsImpl_m(RTCStatsReportInternal * report)618 static void StoreLongTermICEStatisticsImpl_m(RTCStatsReportInternal* report) {
619   using namespace Telemetry;
620 
621   report->mClosed = true;
622 
623   for (const auto& outboundRtpStats : report->mOutboundRtpStreamStats) {
624     bool isVideo = (outboundRtpStats.mId.Value().Find("video") != -1);
625     if (!isVideo) {
626       continue;
627     }
628     if (outboundRtpStats.mBitrateMean.WasPassed()) {
629       Accumulate(WEBRTC_VIDEO_ENCODER_BITRATE_AVG_PER_CALL_KBPS,
630                  uint32_t(outboundRtpStats.mBitrateMean.Value() / 1000));
631     }
632     if (outboundRtpStats.mBitrateStdDev.WasPassed()) {
633       Accumulate(WEBRTC_VIDEO_ENCODER_BITRATE_STD_DEV_PER_CALL_KBPS,
634                  uint32_t(outboundRtpStats.mBitrateStdDev.Value() / 1000));
635     }
636     if (outboundRtpStats.mFramerateMean.WasPassed()) {
637       Accumulate(WEBRTC_VIDEO_ENCODER_FRAMERATE_AVG_PER_CALL,
638                  uint32_t(outboundRtpStats.mFramerateMean.Value()));
639     }
640     if (outboundRtpStats.mFramerateStdDev.WasPassed()) {
641       Accumulate(WEBRTC_VIDEO_ENCODER_FRAMERATE_10X_STD_DEV_PER_CALL,
642                  uint32_t(outboundRtpStats.mFramerateStdDev.Value() * 10));
643     }
644     if (outboundRtpStats.mDroppedFrames.WasPassed() &&
645         report->mCallDurationMs.WasPassed()) {
646       double mins = report->mCallDurationMs.Value() / (1000 * 60);
647       if (mins > 0) {
648         Accumulate(
649             WEBRTC_VIDEO_ENCODER_DROPPED_FRAMES_PER_CALL_FPM,
650             uint32_t(double(outboundRtpStats.mDroppedFrames.Value()) / mins));
651       }
652     }
653   }
654 
655   for (const auto& inboundRtpStats : report->mInboundRtpStreamStats) {
656     bool isVideo = (inboundRtpStats.mId.Value().Find("video") != -1);
657     if (!isVideo) {
658       continue;
659     }
660     if (inboundRtpStats.mBitrateMean.WasPassed()) {
661       Accumulate(WEBRTC_VIDEO_DECODER_BITRATE_AVG_PER_CALL_KBPS,
662                  uint32_t(inboundRtpStats.mBitrateMean.Value() / 1000));
663     }
664     if (inboundRtpStats.mBitrateStdDev.WasPassed()) {
665       Accumulate(WEBRTC_VIDEO_DECODER_BITRATE_STD_DEV_PER_CALL_KBPS,
666                  uint32_t(inboundRtpStats.mBitrateStdDev.Value() / 1000));
667     }
668     if (inboundRtpStats.mFramerateMean.WasPassed()) {
669       Accumulate(WEBRTC_VIDEO_DECODER_FRAMERATE_AVG_PER_CALL,
670                  uint32_t(inboundRtpStats.mFramerateMean.Value()));
671     }
672     if (inboundRtpStats.mFramerateStdDev.WasPassed()) {
673       Accumulate(WEBRTC_VIDEO_DECODER_FRAMERATE_10X_STD_DEV_PER_CALL,
674                  uint32_t(inboundRtpStats.mFramerateStdDev.Value() * 10));
675     }
676     if (inboundRtpStats.mDiscardedPackets.WasPassed() &&
677         report->mCallDurationMs.WasPassed()) {
678       double mins = report->mCallDurationMs.Value() / (1000 * 60);
679       if (mins > 0) {
680         Accumulate(
681             WEBRTC_VIDEO_DECODER_DISCARDED_PACKETS_PER_CALL_PPM,
682             uint32_t(double(inboundRtpStats.mDiscardedPackets.Value()) / mins));
683       }
684     }
685   }
686 
687   // Finally, store the stats
688 
689   PeerConnectionCtx* ctx = GetPeerConnectionCtx();
690   if (ctx) {
691     if (!ctx->mStatsForClosedPeerConnections.AppendElement(*report, fallible)) {
692       mozalloc_handle_oom(0);
693     }
694   }
695 }
696 
StoreLongTermICEStatistics(PeerConnectionImpl & aPc)697 void WebrtcGlobalInformation::StoreLongTermICEStatistics(
698     PeerConnectionImpl& aPc) {
699   if (aPc.IceConnectionState() == RTCIceConnectionState::New) {
700     // ICE has not started; we won't have any remote candidates, so recording
701     // statistics on gathered candidates is pointless.
702     return;
703   }
704 
705   aPc.GetStats(nullptr, true)
706       ->Then(
707           GetMainThreadSerialEventTarget(), __func__,
708           [=](UniquePtr<dom::RTCStatsReportInternal>&& aReport) {
709             StoreLongTermICEStatisticsImpl_m(aReport.get());
710           },
711           [=](nsresult aError) {});
712 }
713 
714 }  // namespace mozilla::dom
715