snap: add RTCConfiguration for enabling SNAP

which allows enabling the feature from a Blink feature / origin trial

Bug: webrtc:426480601
Change-Id: Ifc340c5e72b9e291cdfce7906834785e1251000a
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/468560
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Philipp Hancke <philipp.hancke@googlemail.com>
Reviewed-by: Guido Urdaneta <guidou@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#47637}
diff --git a/api/peer_connection_interface.cc b/api/peer_connection_interface.cc
index a994e3c..cd72e39 100644
--- a/api/peer_connection_interface.cc
+++ b/api/peer_connection_interface.cc
@@ -125,6 +125,7 @@
     std::optional<TimeDelta> pacer_burst_interval;
     bool always_negotiate_data_channels;
     int max_sctp_streams;
+    bool enable_sctp_snap;
   };
   static_assert(sizeof(stuff_being_tested_for_equality) == sizeof(*this),
                 "Did you add something to RTCConfiguration and forget to "
diff --git a/api/peer_connection_interface.h b/api/peer_connection_interface.h
index dc2834a..6c51d77 100644
--- a/api/peer_connection_interface.h
+++ b/api/peer_connection_interface.h
@@ -694,6 +694,10 @@
     // of the DcSctpOptions struct.
     int max_sctp_streams = kMaxSctpStreams;
 
+    // https://www.ietf.org/archive/id/draft-hancke-tsvwg-snap-00.html
+    // Option for origin trial / rollout.
+    bool enable_sctp_snap = false;
+
     //
     // Don't forget to update operator== if adding something.
     //
diff --git a/media/sctp/dcsctp_transport.cc b/media/sctp/dcsctp_transport.cc
index 2799280..96f02da 100644
--- a/media/sctp/dcsctp_transport.cc
+++ b/media/sctp/dcsctp_transport.cc
@@ -796,8 +796,6 @@
 
 std::vector<uint8_t> DcSctpTransport::GenerateConnectionToken(
     const Environment& env) {
-  RTC_DCHECK(env.field_trials().IsEnabled("WebRTC-Sctp-Snap"))
-      << "Only implemented under field trial.";
   Random random(env.clock().TimeInMicroseconds());
   auto temp_factory = std::make_unique<dcsctp::DcSctpSocketFactory>();
   return temp_factory->GenerateConnectionToken(
diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc
index ca22054..2dd0328 100644
--- a/pc/peer_connection.cc
+++ b/pc/peer_connection.cc
@@ -499,6 +499,12 @@
   // Field trials specific to the peerconnection should be owned by the `env`,
   RTC_DCHECK(dependencies.trials == nullptr);
 
+  // Enable SNAP from field trial unless enabled explicitly.
+  if (!configuration_.enable_sctp_snap) {
+    configuration_.enable_sctp_snap =
+        env.field_trials().IsEnabled("WebRTC-Sctp-Snap");
+  }
+
   std::vector<IceParameters> pooled_credentials;
   std::tie(transport_controller_copy_, pooled_credentials) =
       InitializeNetworkThread(stun_servers, turn_servers);
diff --git a/pc/sdp_offer_answer.cc b/pc/sdp_offer_answer.cc
index d576bcf..7e0ba7f 100644
--- a/pc/sdp_offer_answer.cc
+++ b/pc/sdp_offer_answer.cc
@@ -4663,7 +4663,7 @@
   session_options->use_obsolete_sctp_sdp =
       offer_answer_options.use_obsolete_sctp_sdp;
   // draft-hancke-tsvwg-snap.
-  session_options->use_sctp_snap = pc_->trials().IsEnabled("WebRTC-Sctp-Snap");
+  session_options->use_sctp_snap = pc_->configuration()->enable_sctp_snap;
 }
 
 void SdpOfferAnswerHandler::GetOptionsForPlanBOffer(
@@ -4948,7 +4948,7 @@
   session_options->crypto_options = pc_->GetCryptoOptions();
   session_options->pooled_ice_credentials = cached_pooled_ice_credentials_;
   // draft-hancke-tsvwg-snap.
-  session_options->use_sctp_snap = pc_->trials().IsEnabled("WebRTC-Sctp-Snap");
+  session_options->use_sctp_snap = pc_->configuration()->enable_sctp_snap;
 }
 
 void SdpOfferAnswerHandler::GetOptionsForPlanBAnswer(
diff --git a/pc/sdp_offer_answer_unittest.cc b/pc/sdp_offer_answer_unittest.cc
index 6d10096..dc3765c 100644
--- a/pc/sdp_offer_answer_unittest.cc
+++ b/pc/sdp_offer_answer_unittest.cc
@@ -2146,6 +2146,44 @@
   EXPECT_TRUE(pc1->SetRemoteDescription(std::move(answer)));
 }
 
+TEST_F(SdpOfferAnswerTest, SctpInitWithConfig) {
+  RTCConfiguration config;
+  config.enable_sctp_snap = true;
+  auto pc1 = CreatePeerConnection(config, "WebRTC-Sctp-Snap/Enabled/");
+  auto pc2 = CreatePeerConnection(
+      config, "WebRTC-Sctp-Snap/Disabled/");  // config beats field trial.
+  EXPECT_TRUE(pc1->pc()->CreateDataChannelOrError("dc", nullptr).ok());
+  auto offer = pc1->CreateOfferAndSetAsLocal();
+  ASSERT_THAT(offer, NotNull());
+
+  {
+    auto& contents = offer->description()->contents();
+    ASSERT_EQ(contents.size(), 1u);
+    auto* media_description = contents[0].media_description();
+    ASSERT_TRUE(media_description);
+    auto* sctp_description = media_description->as_sctp();
+    ASSERT_TRUE(sctp_description);
+    EXPECT_TRUE(sctp_description->sctp_init());
+  }
+
+  RTCError error;
+  EXPECT_TRUE(pc2->SetRemoteDescription(std::move(offer)));
+  auto answer = pc2->CreateAnswerAndSetAsLocal();
+  ASSERT_THAT(answer, NotNull());
+
+  {
+    auto& contents = answer->description()->contents();
+    ASSERT_EQ(contents.size(), 1u);
+    auto* media_description = contents[0].media_description();
+    ASSERT_TRUE(media_description);
+    auto* sctp_description = media_description->as_sctp();
+    ASSERT_TRUE(sctp_description);
+    EXPECT_TRUE(sctp_description->sctp_init());
+  }
+
+  EXPECT_TRUE(pc1->SetRemoteDescription(std::move(answer)));
+}
+
 TEST_F(SdpOfferAnswerTest, SctpInitWithTrial) {
   auto pc1 = CreatePeerConnection("WebRTC-Sctp-Snap/Enabled/");
   auto pc2 = CreatePeerConnection("WebRTC-Sctp-Snap/Enabled/");