Add firing of OnRemoveTrack and OnRenegotationNeeded during rollback

Bug: chromium:980875
Change-Id: I71439cea4c79e4a8dae6488404b0c303a9c33a97
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/157581
Commit-Queue: Eldar Rello <elrello@microsoft.com>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29563}
diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc
index f019ec9..494a649 100644
--- a/pc/peer_connection.cc
+++ b/pc/peer_connection.cc
@@ -2264,7 +2264,7 @@
   // For SLD we support only explicit rollback.
   if (desc->GetType() == SdpType::kRollback) {
     if (IsUnifiedPlan()) {
-      RTCError error = Rollback();
+      RTCError error = Rollback(desc->GetType());
       if (error.ok()) {
         PostSetSessionDescriptionSuccess(observer);
       } else {
@@ -2654,12 +2654,12 @@
     if (configuration_.enable_implicit_rollback) {
       if (desc->GetType() == SdpType::kOffer &&
           signaling_state() == kHaveLocalOffer) {
-        Rollback();
+        Rollback(desc->GetType());
       }
     }
     // Explicit rollback.
     if (desc->GetType() == SdpType::kRollback) {
-      observer->OnSetRemoteDescriptionComplete(Rollback());
+      observer->OnSetRemoteDescriptionComplete(Rollback(desc->GetType()));
       return;
     }
   } else if (desc->GetType() == SdpType::kRollback) {
@@ -7610,7 +7610,7 @@
   return false;
 }
 
-RTCError PeerConnection::Rollback() {
+RTCError PeerConnection::Rollback(SdpType sdp_type) {
   auto state = signaling_state();
   if (state != PeerConnectionInterface::kHaveLocalOffer &&
       state != PeerConnectionInterface::kHaveRemoteOffer) {
@@ -7630,6 +7630,10 @@
     transport_controller_->RollbackTransportForMid(mid);
     DestroyTransceiverChannel(transceiver);
 
+    if (signaling_state() == PeerConnectionInterface::kHaveRemoteOffer &&
+        transceiver->receiver()) {
+      Observer()->OnRemoveTrack(transceiver->receiver());
+    }
     if (state.newly_created()) {
       // Remove added transceivers with no added track.
       if (transceiver->internal()->sender()->track()) {
@@ -7654,6 +7658,15 @@
   pending_local_description_.reset();
   pending_remote_description_.reset();
   ChangeSignalingState(PeerConnectionInterface::kStable);
+
+  // The assumption is that in case of implicit rollback UpdateNegotiationNeeded
+  // gets called in SetRemoteDescription.
+  if (sdp_type == SdpType::kRollback) {
+    UpdateNegotiationNeeded();
+    if (is_negotiation_needed_) {
+      Observer()->OnRenegotiationNeeded();
+    }
+  }
   return RTCError::OK();
 }
 
diff --git a/pc/peer_connection.h b/pc/peer_connection.h
index 428c2e8..baaa14d 100644
--- a/pc/peer_connection.h
+++ b/pc/peer_connection.h
@@ -1193,7 +1193,9 @@
 
   void UpdateNegotiationNeeded();
   bool CheckIfNegotiationIsNeeded();
-  RTCError Rollback();
+
+  // | sdp_type | is the type of the SDP that caused the rollback.
+  RTCError Rollback(SdpType sdp_type);
 
   sigslot::signal1<DataChannel*> SignalDataChannelCreated_
       RTC_GUARDED_BY(signaling_thread());
diff --git a/pc/peer_connection_jsep_unittest.cc b/pc/peer_connection_jsep_unittest.cc
index 514374b..bb1039c 100644
--- a/pc/peer_connection_jsep_unittest.cc
+++ b/pc/peer_connection_jsep_unittest.cc
@@ -1786,7 +1786,7 @@
   EXPECT_EQ(callee->pc()->pending_remote_description(), nullptr);
 }
 
-TEST_F(PeerConnectionJsepTest, RollbackLocalOfferImplicitly) {
+TEST_F(PeerConnectionJsepTest, RollbackImplicitly) {
   RTCConfiguration config;
   config.sdp_semantics = SdpSemantics::kUnifiedPlan;
   config.enable_implicit_rollback = true;
@@ -1796,9 +1796,48 @@
   EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
   EXPECT_EQ(callee->signaling_state(),
             PeerConnectionInterface::kHaveRemoteOffer);
+  EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal());
+  EXPECT_FALSE(callee->observer()->negotiation_needed());
 }
 
-TEST_F(PeerConnectionJsepTest, AttemptToRollbackLocalOfferImplicitly) {
+TEST_F(PeerConnectionJsepTest, RollbackImplicitlyNegotatiationNotNeeded) {
+  RTCConfiguration config;
+  config.sdp_semantics = SdpSemantics::kUnifiedPlan;
+  config.enable_implicit_rollback = true;
+  auto caller = CreatePeerConnection(config);
+  auto callee = CreatePeerConnection(config);
+  caller->AddAudioTrack("a");
+  callee->AddAudioTrack("b");
+  EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
+  callee->observer()->clear_negotiation_needed();
+  EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
+  EXPECT_EQ(callee->signaling_state(),
+            PeerConnectionInterface::kHaveRemoteOffer);
+  EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal());
+  // No negotiation needed as track got attached in the answer.
+  EXPECT_FALSE(callee->observer()->negotiation_needed());
+  EXPECT_EQ(callee->observer()->remove_track_events_.size(), 0u);
+}
+
+TEST_F(PeerConnectionJsepTest, RollbackImplicitlyAndNegotiationNeeded) {
+  RTCConfiguration config;
+  config.sdp_semantics = SdpSemantics::kUnifiedPlan;
+  config.enable_implicit_rollback = true;
+  auto caller = CreatePeerConnection(config);
+  auto callee = CreatePeerConnection(config);
+  callee->AddAudioTrack("a");
+  EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
+  callee->observer()->clear_negotiation_needed();
+  EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
+  EXPECT_EQ(callee->signaling_state(),
+            PeerConnectionInterface::kHaveRemoteOffer);
+  EXPECT_FALSE(callee->observer()->negotiation_needed());
+  EXPECT_TRUE(callee->CreateAnswerAndSetAsLocal());
+  EXPECT_TRUE(callee->observer()->negotiation_needed());
+  EXPECT_EQ(callee->observer()->remove_track_events_.size(), 0u);
+}
+
+TEST_F(PeerConnectionJsepTest, AttemptToRollbackImplicitly) {
   RTCConfiguration config;
   config.sdp_semantics = SdpSemantics::kUnifiedPlan;
   config.enable_implicit_rollback = true;
@@ -1816,9 +1855,10 @@
   caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
   auto callee = CreatePeerConnection();
   EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
-  EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{1});
+  EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
   EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
-  EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{0});
+  EXPECT_EQ(callee->pc()->GetTransceivers().size(), 0u);
+  EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u);
 }
 
 TEST_F(PeerConnectionJsepTest, RollbackKeepsTransceiverAndClearsMid) {
@@ -1827,15 +1867,16 @@
   auto callee = CreatePeerConnection();
   EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
   callee->AddAudioTrack("a");
-  EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{1});
+  EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
   EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
   // Transceiver can't be removed as track was added to it.
-  EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{1});
+  EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
   // Mid got cleared to make it reusable.
   EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
   // Transceiver should be counted as addTrack-created after rollback.
   EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
-  EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{1});
+  EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
+  EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u);
 }
 
 TEST_F(PeerConnectionJsepTest, RollbackRestoresMid) {
@@ -1845,7 +1886,7 @@
   callee->AddAudioTrack("a");
   auto offer = callee->CreateOffer();
   EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
-  EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{1});
+  EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
   EXPECT_NE(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
   EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
   EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
@@ -1861,13 +1902,38 @@
   caller->AddVideoTrack("c");
   auto mid = callee->pc()->GetTransceivers()[0]->mid();
   EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
-  EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{2});
+  EXPECT_EQ(callee->pc()->GetTransceivers().size(), 2u);
   EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
-  EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{1});
+  EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
   EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), mid);
   EXPECT_EQ(callee->pc()->GetTransceivers()[0]->media_type(),
             cricket::MEDIA_TYPE_VIDEO);
   EXPECT_TRUE(callee->SetLocalDescription(std::move(offer)));
+  EXPECT_EQ(callee->observer()->remove_track_events_.size(),
+            callee->observer()->add_track_events_.size());
+}
+
+TEST_F(PeerConnectionJsepTest, RollbackHasNoEffectOnStableTransceivers) {
+  auto callee = CreatePeerConnection();
+  callee->AddVideoTrack("a");
+  auto caller = CreatePeerConnection();
+  caller->AddAudioTrack("b");
+  caller->AddVideoTrack("c");
+  EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+  EXPECT_TRUE(
+      caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+  // In stable don't add or remove anything.
+  callee->observer()->clear_negotiation_needed();
+  size_t transceiver_count = callee->pc()->GetTransceivers().size();
+  auto mid_0 = callee->pc()->GetTransceivers()[0]->mid();
+  auto mid_1 = callee->pc()->GetTransceivers()[1]->mid();
+  EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOffer()));
+  EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
+  EXPECT_EQ(callee->pc()->GetTransceivers().size(), transceiver_count);
+  EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), mid_0);
+  EXPECT_EQ(callee->pc()->GetTransceivers()[1]->mid(), mid_1);
+  EXPECT_EQ(callee->observer()->remove_track_events_.size(), 0u);
+  EXPECT_FALSE(callee->observer()->negotiation_needed());
 }
 
 TEST_F(PeerConnectionJsepTest, ImplicitlyRollbackTransceiversWithSameMids) {
@@ -1881,7 +1947,7 @@
   EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
   auto initial_mid = callee->pc()->GetTransceivers()[0]->mid();
   EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
-  EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{2});
+  EXPECT_EQ(callee->pc()->GetTransceivers().size(), 2u);
   EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
   EXPECT_EQ(callee->pc()->GetTransceivers()[1]->mid(),
             caller->pc()->GetTransceivers()[0]->mid());
@@ -1902,7 +1968,7 @@
   caller->AddVideoTrack("a");
   callee->AddVideoTrack("b");
   EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
-  EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{2});
+  EXPECT_EQ(callee->pc()->GetTransceivers().size(), 2u);
   auto audio_transport =
       callee->pc()->GetTransceivers()[0]->sender()->dtls_transport();
   EXPECT_EQ(callee->pc()->GetTransceivers()[0]->sender()->dtls_transport(),
@@ -1920,14 +1986,68 @@
             audio_transport);  // Audio transport is still the same.
 }
 
+TEST_F(PeerConnectionJsepTest, RollbackLocalDirectionChange) {
+  auto caller = CreatePeerConnection();
+  caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
+  auto callee = CreatePeerConnection();
+  EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+  EXPECT_TRUE(
+      caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+  callee->AddAudioTrack("a");
+  callee->pc()->GetTransceivers()[0]->SetDirection(
+      RtpTransceiverDirection::kSendOnly);
+  EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
+  EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
+  auto audio_transport =
+      callee->pc()->GetTransceivers()[0]->receiver()->dtls_transport();
+  EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
+  EXPECT_EQ(callee->pc()->GetTransceivers()[0]->direction(),
+            RtpTransceiverDirection::kSendOnly);
+  // One way audio must remain working after rollback as local direction change
+  // comes in effect after completing full negotiation round.
+  EXPECT_EQ(callee->pc()->GetTransceivers()[0]->receiver()->dtls_transport(),
+            audio_transport);
+}
+
+TEST_F(PeerConnectionJsepTest, RollbackRemoteDirectionChange) {
+  auto caller = CreatePeerConnection();
+  auto caller_transceiver = caller->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
+  auto callee = CreatePeerConnection();
+  callee->AddAudioTrack("a");
+  EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+  EXPECT_TRUE(
+      caller->SetRemoteDescription(callee->CreateAnswerAndSetAsLocal()));
+  // In stable make remote audio receive only.
+  caller_transceiver->SetDirection(RtpTransceiverDirection::kRecvOnly);
+  EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal()));
+  EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
+  // The direction attribute is not modified by the offer.
+  EXPECT_EQ(callee->pc()->GetTransceivers()[0]->direction(),
+            RtpTransceiverDirection::kSendRecv);
+  auto audio_transport =
+      callee->pc()->GetTransceivers()[0]->sender()->dtls_transport();
+  EXPECT_EQ(callee->observer()->add_track_events_.size(), 1u);
+  EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u);
+  EXPECT_TRUE(callee->SetRemoteDescription(caller->CreateRollback()));
+  EXPECT_EQ(callee->pc()->GetTransceivers().size(), 1u);
+  EXPECT_EQ(callee->pc()->GetTransceivers()[0]->direction(),
+            RtpTransceiverDirection::kSendRecv);
+  // One way audio must remain working after rollback.
+  EXPECT_EQ(callee->pc()->GetTransceivers()[0]->sender()->dtls_transport(),
+            audio_transport);
+  EXPECT_EQ(callee->observer()->remove_track_events_.size(), 1u);
+}
+
 TEST_F(PeerConnectionJsepTest, RollbackAfterMultipleSLD) {
   auto callee = CreatePeerConnection();
   callee->AddTransceiver(cricket::MEDIA_TYPE_AUDIO);
   EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
   callee->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
   EXPECT_TRUE(callee->CreateOfferAndSetAsLocal());
+  callee->observer()->clear_negotiation_needed();
   EXPECT_TRUE(callee->SetRemoteDescription(callee->CreateRollback()));
-  EXPECT_EQ(callee->pc()->GetTransceivers().size(), size_t{2});
+  EXPECT_TRUE(callee->observer()->negotiation_needed());
+  EXPECT_EQ(callee->pc()->GetTransceivers().size(), 2u);
   EXPECT_EQ(callee->pc()->GetTransceivers()[0]->mid(), absl::nullopt);
   EXPECT_EQ(callee->pc()->GetTransceivers()[1]->mid(), absl::nullopt);
 }