add metrics for bundle usage

adds metrics for bundle usage, differentiating between
* BUNDLE is not negotiated and there is only a datachannel,
* BUNDLE is not negotiated and there is at most one m-line per media type,
for unified-plan
* BUNDLE is not negotiated and there are multiple m-lines per media type,
* BUNDLE is negotiated and there is only a datachannel,
* BUNDLE is negotiated but there is at most one m-line per media type,
* BUNDLE is negotiated and there are multiple m-lines per media type,
and for plan-b
* BUNDLE is negotiated
* BUNDLE is not negotiated

BUG=webrtc:12383

Change-Id: I41afc4b08fd97239f3b16a8638d9753c69b46d22
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/202254
Commit-Queue: Philipp Hancke <philipp.hancke@googlemail.com>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#33090}
diff --git a/api/uma_metrics.h b/api/uma_metrics.h
index 30543b6..5edb1f4 100644
--- a/api/uma_metrics.h
+++ b/api/uma_metrics.h
@@ -167,6 +167,30 @@
   kSimulcastApiVersionMax
 };
 
+// Metrics for reporting usage of BUNDLE.
+// These values are persisted to logs. Entries should not be renumbered and
+// numeric values should never be reused.
+enum BundleUsage {
+  // There are no m-lines in the SDP, only a session description.
+  kBundleUsageEmpty = 0,
+  // Only a data channel is negotiated but BUNDLE is not negotiated.
+  kBundleUsageNoBundleDatachannelOnly = 1,
+  // BUNDLE is not negotiated and there is at most one m-line per media type,
+  kBundleUsageNoBundleSimple = 2,
+  // BUNDLE is not negotiated and there are multiple m-lines per media type,
+  kBundleUsageNoBundleComplex = 3,
+  // Only a data channel is negotiated and BUNDLE is negotiated.
+  kBundleUsageBundleDatachannelOnly = 4,
+  // BUNDLE is negotiated but there is at most one m-line per media type,
+  kBundleUsageBundleSimple = 5,
+  // BUNDLE is negotiated and there are multiple m-lines per media type,
+  kBundleUsageBundleComplex = 6,
+  // Legacy plan-b metrics.
+  kBundleUsageNoBundlePlanB = 7,
+  kBundleUsageBundlePlanB = 8,
+  kBundleUsageMax
+};
+
 // When adding new metrics please consider using the style described in
 // https://chromium.googlesource.com/chromium/src.git/+/HEAD/tools/metrics/histograms/README.md#usage
 // instead of the legacy enums used above.
diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc
index 23be9b9..68fa06e 100644
--- a/pc/peer_connection.cc
+++ b/pc/peer_connection.cc
@@ -2344,6 +2344,52 @@
   }
 }
 
+void PeerConnection::ReportSdpBundleUsage(
+    const SessionDescriptionInterface& remote_description) {
+  RTC_DCHECK_RUN_ON(signaling_thread());
+
+  bool using_bundle =
+      remote_description.description()->HasGroup(cricket::GROUP_TYPE_BUNDLE);
+  int num_audio_mlines = 0;
+  int num_video_mlines = 0;
+  int num_data_mlines = 0;
+  for (const ContentInfo& content :
+       remote_description.description()->contents()) {
+    cricket::MediaType media_type = content.media_description()->type();
+    if (media_type == cricket::MEDIA_TYPE_AUDIO) {
+      num_audio_mlines += 1;
+    } else if (media_type == cricket::MEDIA_TYPE_VIDEO) {
+      num_video_mlines += 1;
+    } else if (media_type == cricket::MEDIA_TYPE_DATA) {
+      num_data_mlines += 1;
+    }
+  }
+  bool simple = num_audio_mlines <= 1 && num_video_mlines <= 1;
+  BundleUsage usage = kBundleUsageMax;
+  if (num_audio_mlines == 0 && num_video_mlines == 0) {
+    if (num_data_mlines > 0) {
+      usage = using_bundle ? kBundleUsageBundleDatachannelOnly
+                           : kBundleUsageNoBundleDatachannelOnly;
+    } else {
+      usage = kBundleUsageEmpty;
+    }
+  } else if (configuration_.sdp_semantics == SdpSemantics::kPlanB) {
+    // In plan-b, simple/complex usage will not show up in the number of
+    // m-lines or BUNDLE.
+    usage = using_bundle ? kBundleUsageBundlePlanB : kBundleUsageNoBundlePlanB;
+  } else {
+    if (simple) {
+      usage =
+          using_bundle ? kBundleUsageBundleSimple : kBundleUsageNoBundleSimple;
+    } else {
+      usage = using_bundle ? kBundleUsageBundleComplex
+                           : kBundleUsageNoBundleComplex;
+    }
+  }
+  RTC_HISTOGRAM_ENUMERATION("WebRTC.PeerConnection.BundleUsage", usage,
+                            kBundleUsageMax);
+}
+
 void PeerConnection::ReportIceCandidateCollected(
     const cricket::Candidate& candidate) {
   NoteUsageEvent(UsageEvent::CANDIDATE_COLLECTED);
diff --git a/pc/peer_connection.h b/pc/peer_connection.h
index f3aa9b3..4bab90a 100644
--- a/pc/peer_connection.h
+++ b/pc/peer_connection.h
@@ -383,6 +383,10 @@
   void ReportSdpFormatReceived(
       const SessionDescriptionInterface& remote_description);
 
+  // Report the UMA metric BundleUsage for the given remote description.
+  void ReportSdpBundleUsage(
+      const SessionDescriptionInterface& remote_description);
+
   // Returns true if the PeerConnection is configured to use Unified Plan
   // semantics for creating offers/answers and setting local/remote
   // descriptions. If this is true the RtpTransceiver API will also be available
diff --git a/pc/sdp_offer_answer.cc b/pc/sdp_offer_answer.cc
index 8885276..07097a9 100644
--- a/pc/sdp_offer_answer.cc
+++ b/pc/sdp_offer_answer.cc
@@ -2156,6 +2156,7 @@
       desc->GetType() == SdpType::kAnswer) {
     // Report to UMA the format of the received offer or answer.
     pc_->ReportSdpFormatReceived(*desc);
+    pc_->ReportSdpBundleUsage(*desc);
   }
 
   // Handle remote descriptions missing a=mid lines for interop with legacy end