When doing DisableEquivalentPhases, exclude those AllocationSequences
whose network has ever been removed. It is unlikely the sockets/ports/candidates created from
those AllocationSequences will still be valid.

BUG=

Review URL: https://codereview.webrtc.org/1361183004

Cr-Commit-Position: refs/heads/master@{#10093}
diff --git a/webrtc/base/fakenetwork.h b/webrtc/base/fakenetwork.h
index 065d08d..fb99d59 100644
--- a/webrtc/base/fakenetwork.h
+++ b/webrtc/base/fakenetwork.h
@@ -34,8 +34,12 @@
   typedef std::vector<SocketAddress> IfaceList;
 
   void AddInterface(const SocketAddress& iface) {
-    // ensure a unique name for the interface
-    SocketAddress address("test" + rtc::ToString(next_index_++), 0);
+    // Ensure a unique name for the interface if its name is not given.
+    AddInterface(iface, "test" + rtc::ToString(next_index_++));
+  }
+
+  void AddInterface(const SocketAddress& iface, const std::string& if_name) {
+    SocketAddress address(if_name, 0);
     address.SetResolvedIP(iface.ipaddr());
     ifaces_.push_back(address);
     DoUpdateNetworks();
diff --git a/webrtc/p2p/client/basicportallocator.cc b/webrtc/p2p/client/basicportallocator.cc
index 9820252..11d0cef 100644
--- a/webrtc/p2p/client/basicportallocator.cc
+++ b/webrtc/p2p/client/basicportallocator.cc
@@ -298,28 +298,35 @@
   allocation_started_ = true;
 }
 
-// For each network, see if we have a sequence that covers it already.  If not,
-// create a new sequence to create the appropriate ports.
-void BasicPortAllocatorSession::DoAllocate() {
-  bool done_signal_needed = false;
-  std::vector<rtc::Network*> networks;
-
+void BasicPortAllocatorSession::GetNetworks(
+    std::vector<rtc::Network*>* networks) {
+  networks->clear();
+  rtc::NetworkManager* network_manager = allocator_->network_manager();
+  ASSERT(network_manager != nullptr);
   // If the network permission state is BLOCKED, we just act as if the flag has
   // been passed in.
-  if (allocator_->network_manager()->enumeration_permission() ==
+  if (network_manager->enumeration_permission() ==
       rtc::NetworkManager::ENUMERATION_BLOCKED) {
     set_flags(flags() | PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION);
   }
-
   // If the adapter enumeration is disabled, we'll just bind to any address
   // instead of specific NIC. This is to ensure the same routing for http
   // traffic by OS is also used here to avoid any local or public IP leakage
   // during stun process.
   if (flags() & PORTALLOCATOR_DISABLE_ADAPTER_ENUMERATION) {
-    allocator_->network_manager()->GetAnyAddressNetworks(&networks);
+    network_manager->GetAnyAddressNetworks(networks);
   } else {
-    allocator_->network_manager()->GetNetworks(&networks);
+    network_manager->GetNetworks(networks);
   }
+}
+
+// For each network, see if we have a sequence that covers it already.  If not,
+// create a new sequence to create the appropriate ports.
+void BasicPortAllocatorSession::DoAllocate() {
+  bool done_signal_needed = false;
+  std::vector<rtc::Network*> networks;
+  GetNetworks(&networks);
+
   if (networks.empty()) {
     LOG(LS_WARNING) << "Machine has no networks; no ports will be allocated";
     done_signal_needed = true;
@@ -377,6 +384,18 @@
 }
 
 void BasicPortAllocatorSession::OnNetworksChanged() {
+  std::vector<rtc::Network*> networks;
+  GetNetworks(&networks);
+  for (AllocationSequence* sequence : sequences_) {
+    // Remove the network from the allocation sequence if it is not in
+    // |networks|.
+    if (!sequence->network_removed() &&
+        std::find(networks.begin(), networks.end(), sequence->network()) ==
+            networks.end()) {
+      sequence->OnNetworkRemoved();
+    }
+  }
+
   network_manager_started_ = true;
   if (allocation_started_)
     DoAllocate();
@@ -711,12 +730,24 @@
   turn_ports_.clear();
 }
 
+void AllocationSequence::OnNetworkRemoved() {
+  // Stop the allocation sequence if its network is gone.
+  Stop();
+  network_removed_ = true;
+}
+
 AllocationSequence::~AllocationSequence() {
   session_->network_thread()->Clear(this);
 }
 
 void AllocationSequence::DisableEquivalentPhases(rtc::Network* network,
     PortConfiguration* config, uint32* flags) {
+  if (network_removed_) {
+    // If the network of this allocation sequence has ever gone away,
+    // it won't be equivalent to the new network.
+    return;
+  }
+
   if (!((network == network_) && (ip_ == network->GetBestIP()))) {
     // Different network setup; nothing is equivalent.
     return;
diff --git a/webrtc/p2p/client/basicportallocator.h b/webrtc/p2p/client/basicportallocator.h
index a27ff4c..37ce1a0 100644
--- a/webrtc/p2p/client/basicportallocator.h
+++ b/webrtc/p2p/client/basicportallocator.h
@@ -182,6 +182,7 @@
   void MaybeSignalCandidatesAllocationDone();
   void OnPortAllocationComplete(AllocationSequence* seq);
   PortData* FindPort(Port* port);
+  void GetNetworks(std::vector<rtc::Network*>* networks);
 
   bool CheckCandidateFilter(const Candidate& c);
 
@@ -260,8 +261,11 @@
   ~AllocationSequence();
   bool Init();
   void Clear();
+  void OnNetworkRemoved();
 
   State state() const { return state_; }
+  const rtc::Network* network() const { return network_; }
+  bool network_removed() const { return network_removed_; }
 
   // Disables the phases for a new sequence that this one already covers for an
   // equivalent network setup.
@@ -311,6 +315,7 @@
   void OnPortDestroyed(PortInterface* port);
 
   BasicPortAllocatorSession* session_;
+  bool network_removed_ = false;
   rtc::Network* network_;
   rtc::IPAddress ip_;
   PortConfiguration* config_;
diff --git a/webrtc/p2p/client/portallocator_unittest.cc b/webrtc/p2p/client/portallocator_unittest.cc
index 9ea22dd..2893807 100644
--- a/webrtc/p2p/client/portallocator_unittest.cc
+++ b/webrtc/p2p/client/portallocator_unittest.cc
@@ -111,12 +111,18 @@
   void AddInterface(const SocketAddress& addr) {
     network_manager_.AddInterface(addr);
   }
+  void AddInterface(const SocketAddress& addr, const std::string& if_name) {
+    network_manager_.AddInterface(addr, if_name);
+  }
   void AddInterfaceAsDefaultRoute(const SocketAddress& addr) {
     AddInterface(addr);
     // When a binding comes from the any address, the |addr| will be used as the
     // srflx address.
     vss_->SetDefaultRoute(addr.ipaddr());
   }
+  void RemoveInterface(const SocketAddress& addr) {
+    network_manager_.RemoveInterface(addr);
+  }
   bool SetPortRange(int min_port, int max_port) {
     return allocator_->SetPortRange(min_port, max_port);
   }
@@ -432,6 +438,61 @@
   EXPECT_TRUE(candidate_allocation_done_);
 }
 
+// Test that when the same network interface is brought down and up, the
+// port allocator session will restart a new allocation sequence if
+// it is not stopped.
+TEST_F(PortAllocatorTest, TestSameNetworkDownAndUpWhenSessionNotStopped) {
+  std::string if_name("test_net0");
+  AddInterface(kClientAddr, if_name);
+  EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP));
+  session_->StartGettingPorts();
+  ASSERT_EQ_WAIT(7U, candidates_.size(), kDefaultAllocationTimeout);
+  EXPECT_EQ(4U, ports_.size());
+  EXPECT_TRUE(candidate_allocation_done_);
+  candidate_allocation_done_ = false;
+  candidates_.clear();
+  ports_.clear();
+
+  RemoveInterface(kClientAddr);
+  ASSERT_EQ_WAIT(0U, candidates_.size(), kDefaultAllocationTimeout);
+  EXPECT_EQ(0U, ports_.size());
+  EXPECT_FALSE(candidate_allocation_done_);
+
+  // When the same interfaces are added again, new candidates/ports should be
+  // generated.
+  AddInterface(kClientAddr, if_name);
+  ASSERT_EQ_WAIT(7U, candidates_.size(), kDefaultAllocationTimeout);
+  EXPECT_EQ(4U, ports_.size());
+  EXPECT_TRUE(candidate_allocation_done_);
+}
+
+// Test that when the same network interface is brought down and up, the
+// port allocator session will not restart a new allocation sequence if
+// it is stopped.
+TEST_F(PortAllocatorTest, TestSameNetworkDownAndUpWhenSessionStopped) {
+  std::string if_name("test_net0");
+  AddInterface(kClientAddr, if_name);
+  EXPECT_TRUE(CreateSession(cricket::ICE_CANDIDATE_COMPONENT_RTP));
+  session_->StartGettingPorts();
+  ASSERT_EQ_WAIT(7U, candidates_.size(), kDefaultAllocationTimeout);
+  EXPECT_EQ(4U, ports_.size());
+  EXPECT_TRUE(candidate_allocation_done_);
+  session_->StopGettingPorts();
+  candidates_.clear();
+  ports_.clear();
+
+  RemoveInterface(kClientAddr);
+  ASSERT_EQ_WAIT(0U, candidates_.size(), kDefaultAllocationTimeout);
+  EXPECT_EQ(0U, ports_.size());
+
+  // When the same interfaces are added again, new candidates/ports should not
+  // be generated because the session has stopped.
+  AddInterface(kClientAddr, if_name);
+  ASSERT_EQ_WAIT(0U, candidates_.size(), kDefaultAllocationTimeout);
+  EXPECT_EQ(0U, ports_.size());
+  EXPECT_TRUE(candidate_allocation_done_);
+}
+
 // Verify candidates with default step delay of 1sec.
 TEST_F(PortAllocatorTest, TestGetAllPortsWithOneSecondStepDelay) {
   AddInterface(kClientAddr);