Support changing the tagged BUNDLE media section section

The behavior implemented in this CL matches Firefox:

1. If there are no common media sections from the previous
    BUNDLE group, then the previous transport is stopped
    and a new transport created.

2. If there is at least one common media section from the
    previous BUNDLE group, then the existing transport is
    reused.

This will only happen if the tagged media section is rejected.

Bug: webrtc:9954
Change-Id: If0f0733c0ab91858594304828d126640e2ab9520
Reviewed-on: https://webrtc-review.googlesource.com/c/114920
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#26150}
diff --git a/pc/jseptransportcontroller_unittest.cc b/pc/jseptransportcontroller_unittest.cc
index 3031b8d..c019013 100644
--- a/pc/jseptransportcontroller_unittest.cc
+++ b/pc/jseptransportcontroller_unittest.cc
@@ -11,6 +11,7 @@
 #include <map>
 #include <memory>
 
+#include "absl/memory/memory.h"
 #include "api/media_transport_interface.h"
 #include "api/test/fake_media_transport.h"
 #include "p2p/base/fakedtlstransport.h"
@@ -1730,4 +1731,51 @@
                   .ok());
 }
 
+// Test that the JsepTransportController can process a new local and remote
+// description that changes the tagged BUNDLE group with the max-bundle policy
+// specified.
+// This is a regression test for bugs.webrtc.org/9954
+TEST_F(JsepTransportControllerTest, ChangeTaggedMediaSectionMaxBundle) {
+  CreateJsepTransportController(JsepTransportController::Config());
+
+  auto local_offer = absl::make_unique<cricket::SessionDescription>();
+  AddAudioSection(local_offer.get(), kAudioMid1, kIceUfrag1, kIcePwd1,
+                  cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
+                  nullptr);
+  cricket::ContentGroup bundle_group(cricket::GROUP_TYPE_BUNDLE);
+  bundle_group.AddContentName(kAudioMid1);
+  local_offer->AddGroup(bundle_group);
+  EXPECT_TRUE(transport_controller_
+                  ->SetLocalDescription(SdpType::kOffer, local_offer.get())
+                  .ok());
+
+  std::unique_ptr<cricket::SessionDescription> remote_answer(
+      local_offer->Copy());
+  EXPECT_TRUE(transport_controller_
+                  ->SetRemoteDescription(SdpType::kAnswer, remote_answer.get())
+                  .ok());
+
+  std::unique_ptr<cricket::SessionDescription> local_reoffer(
+      local_offer->Copy());
+  local_reoffer->contents()[0].rejected = true;
+  AddVideoSection(local_reoffer.get(), kVideoMid1, kIceUfrag1, kIcePwd1,
+                  cricket::ICEMODE_FULL, cricket::CONNECTIONROLE_ACTPASS,
+                  nullptr);
+  local_reoffer->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
+  cricket::ContentGroup new_bundle_group(cricket::GROUP_TYPE_BUNDLE);
+  new_bundle_group.AddContentName(kVideoMid1);
+  local_reoffer->AddGroup(new_bundle_group);
+
+  EXPECT_TRUE(transport_controller_
+                  ->SetLocalDescription(SdpType::kOffer, local_reoffer.get())
+                  .ok());
+
+  std::unique_ptr<cricket::SessionDescription> remote_reanswer(
+      local_reoffer->Copy());
+  EXPECT_TRUE(
+      transport_controller_
+          ->SetRemoteDescription(SdpType::kAnswer, remote_reanswer.get())
+          .ok());
+}
+
 }  // namespace webrtc