Add configurable STUN binding request interval.

STUN candidates use STUN binding requests to keep NAT bindings open. The
interval at which the STUN keepalive pings are sent is configurable now
via RTCConfiguration.

TBR=sakal@webrtc.org

Bug: None
Change-Id: I5f99ea3fe1e9042fa2bf7dcab0aace78f57739e6
Reviewed-on: https://webrtc-review.googlesource.com/54180
Commit-Queue: Qingsi Wang <qingsi@google.com>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22109}
diff --git a/api/peerconnectioninterface.h b/api/peerconnectioninterface.h
index 63912b2..9463c34 100644
--- a/api/peerconnectioninterface.h
+++ b/api/peerconnectioninterface.h
@@ -493,6 +493,10 @@
     // (STUN pings), in milliseconds.
     rtc::Optional<int> ice_check_min_interval;
 
+    // The interval in milliseconds at which STUN candidates will resend STUN
+    // binding requests to keep NAT bindings open.
+    rtc::Optional<int> stun_candidate_keepalive_interval;
+
     // ICE Periodic Regathering
     // If set, WebRTC will periodically create and propose candidates without
     // starting a new ICE generation. The regathering happens continuously with
diff --git a/p2p/base/icetransportinternal.h b/p2p/base/icetransportinternal.h
index 6888992..13ba331 100644
--- a/p2p/base/icetransportinternal.h
+++ b/p2p/base/icetransportinternal.h
@@ -114,6 +114,9 @@
   // than this, no matter what other settings there are.
   // Measure in milliseconds.
   rtc::Optional<int> ice_check_min_interval;
+  // The interval in milliseconds at which STUN candidates will resend STUN
+  // binding requests to keep NAT bindings open.
+  rtc::Optional<int> stun_keepalive_interval;
 
   rtc::Optional<rtc::AdapterType> network_preference;
 
diff --git a/p2p/base/p2ptransportchannel.cc b/p2p/base/p2ptransportchannel.cc
index 71af657..2ad4114 100644
--- a/p2p/base/p2ptransportchannel.cc
+++ b/p2p/base/p2ptransportchannel.cc
@@ -541,6 +541,18 @@
                              ? config_.network_preference.value()
                              : 0);
   }
+
+  // TODO(qingsi): Resolve the naming conflict of stun_keepalive_delay in
+  // UDPPort and stun_keepalive_interval.
+  if (config_.stun_keepalive_interval != config.stun_keepalive_interval) {
+    config_.stun_keepalive_interval = config.stun_keepalive_interval;
+    allocator_session()->SetStunKeepaliveIntervalForReadyPorts(
+        config_.stun_keepalive_interval);
+    RTC_LOG(LS_INFO) << "Set STUN keepalive interval to "
+                     << (config.stun_keepalive_interval.has_value()
+                             ? config_.stun_keepalive_interval.value()
+                             : -1);
+  }
 }
 
 const IceConfig& P2PTransportChannel::config() const {
diff --git a/p2p/base/portallocator.cc b/p2p/base/portallocator.cc
index 84c1df2..6ef084c 100644
--- a/p2p/base/portallocator.cc
+++ b/p2p/base/portallocator.cc
@@ -103,7 +103,8 @@
     const std::vector<RelayServerConfig>& turn_servers,
     int candidate_pool_size,
     bool prune_turn_ports,
-    webrtc::TurnCustomizer* turn_customizer) {
+    webrtc::TurnCustomizer* turn_customizer,
+    const rtc::Optional<int>& stun_candidate_keepalive_interval) {
   bool ice_servers_changed =
       (stun_servers != stun_servers_ || turn_servers != turn_servers_);
   stun_servers_ = stun_servers;
@@ -142,6 +143,16 @@
     pooled_sessions_.pop_front();
   }
 
+  // |stun_candidate_keepalive_interval_| will be used in STUN port allocation
+  // in future sessions. We also update the ready ports in the pooled sessions.
+  // Ports in sessions that are taken and owned by P2PTransportChannel will be
+  // updated there via IceConfig.
+  stun_candidate_keepalive_interval_ = stun_candidate_keepalive_interval;
+  for (const auto& session : pooled_sessions_) {
+    session->SetStunKeepaliveIntervalForReadyPorts(
+        stun_candidate_keepalive_interval_);
+  }
+
   // If |candidate_pool_size_| is greater than the number of pooled sessions,
   // create new sessions.
   while (static_cast<int>(pooled_sessions_.size()) < candidate_pool_size_) {
diff --git a/p2p/base/portallocator.h b/p2p/base/portallocator.h
index 1081448..f32fa07 100644
--- a/p2p/base/portallocator.h
+++ b/p2p/base/portallocator.h
@@ -244,7 +244,12 @@
   virtual void RegatherOnFailedNetworks() {}
   // Re-gathers candidates on all networks.
   virtual void RegatherOnAllNetworks() {}
-
+  // Set the interval at which STUN candidates will resend STUN binding requests
+  // on the underlying ports to keep NAT bindings open.
+  // The default value of the interval in implementation is restored if a null
+  // optional value is passed.
+  virtual void SetStunKeepaliveIntervalForReadyPorts(
+      const rtc::Optional<int>& stun_keepalive_interval) {}
   // Another way of getting the information provided by the signals below.
   //
   // Ports and candidates are not guaranteed to be in the same order as the
@@ -347,7 +352,9 @@
                         const std::vector<RelayServerConfig>& turn_servers,
                         int candidate_pool_size,
                         bool prune_turn_ports,
-                        webrtc::TurnCustomizer* turn_customizer = nullptr);
+                        webrtc::TurnCustomizer* turn_customizer = nullptr,
+                        const rtc::Optional<int>&
+                            stun_candidate_keepalive_interval = rtc::nullopt);
 
   const ServerAddresses& stun_servers() const { return stun_servers_; }
 
@@ -356,6 +363,9 @@
   }
 
   int candidate_pool_size() const { return candidate_pool_size_; }
+  const rtc::Optional<int>& stun_candidate_keepalive_interval() const {
+    return stun_candidate_keepalive_interval_;
+  }
 
   // Sets the network types to ignore.
   // Values are defined by the AdapterType enum.
@@ -506,6 +516,8 @@
   // The instance is owned by application and will be shared among
   // all TurnPort(s) created.
   webrtc::TurnCustomizer* turn_customizer_ = nullptr;
+
+  rtc::Optional<int> stun_candidate_keepalive_interval_;
 };
 
 }  // namespace cricket
diff --git a/p2p/base/stunport.cc b/p2p/base/stunport.cc
index 69196cf..2df14b7 100644
--- a/p2p/base/stunport.cc
+++ b/p2p/base/stunport.cc
@@ -317,6 +317,10 @@
   return PROTO_UDP;
 }
 
+void UDPPort::set_stun_keepalive_delay(const rtc::Optional<int>& delay) {
+  stun_keepalive_delay_ = (delay.has_value() ? delay.value() : KEEPALIVE_DELAY);
+}
+
 void UDPPort::OnLocalAddressReady(rtc::AsyncPacketSocket* socket,
                                   const rtc::SocketAddress& address) {
   // When adapter enumeration is disabled and binding to the any address, the
diff --git a/p2p/base/stunport.h b/p2p/base/stunport.h
index cf45d1d..eaeea3a 100644
--- a/p2p/base/stunport.h
+++ b/p2p/base/stunport.h
@@ -101,9 +101,7 @@
   bool SupportsProtocol(const std::string& protocol) const override;
   ProtocolType GetProtocol() const override;
 
-  void set_stun_keepalive_delay(int delay) {
-    stun_keepalive_delay_ = delay;
-  }
+  void set_stun_keepalive_delay(const rtc::Optional<int>& delay);
   int stun_keepalive_delay() const {
     return stun_keepalive_delay_;
   }
diff --git a/p2p/client/basicportallocator.cc b/p2p/client/basicportallocator.cc
index 86cec8f..712d595 100644
--- a/p2p/client/basicportallocator.cc
+++ b/p2p/client/basicportallocator.cc
@@ -429,6 +429,17 @@
   }
 }
 
+void BasicPortAllocatorSession::SetStunKeepaliveIntervalForReadyPorts(
+    const rtc::Optional<int>& stun_keepalive_interval) {
+  auto ports = ReadyPorts();
+  for (PortInterface* port : ports) {
+    if (port->Type() == STUN_PORT_TYPE) {
+      static_cast<UDPPort*>(port)->set_stun_keepalive_delay(
+          stun_keepalive_interval);
+    }
+  }
+}
+
 std::vector<PortInterface*> BasicPortAllocatorSession::ReadyPorts() const {
   std::vector<PortInterface*> ret;
   for (const PortData& data : ports_) {
@@ -1357,6 +1368,8 @@
       session_->username(), session_->password(), config_->StunServers(),
       session_->allocator()->origin());
   if (port) {
+    port->set_stun_keepalive_delay(
+        session_->allocator()->stun_candidate_keepalive_interval());
     session_->AddAllocatedPort(port, this, true);
     // Since StunPort is not created using shared socket, |port| will not be
     // added to the dequeue.
diff --git a/p2p/client/basicportallocator.h b/p2p/client/basicportallocator.h
index 23faff1..fb74ba5 100644
--- a/p2p/client/basicportallocator.h
+++ b/p2p/client/basicportallocator.h
@@ -127,6 +127,8 @@
   bool CandidatesAllocationDone() const override;
   void RegatherOnFailedNetworks() override;
   void RegatherOnAllNetworks() override;
+  void SetStunKeepaliveIntervalForReadyPorts(
+      const rtc::Optional<int>& stun_keepalive_interval) override;
   void PruneAllPorts() override;
 
  protected:
diff --git a/pc/peerconnection.cc b/pc/peerconnection.cc
index 73f79c8..f32b478 100644
--- a/pc/peerconnection.cc
+++ b/pc/peerconnection.cc
@@ -636,6 +636,7 @@
     bool enable_ice_renomination;
     bool redetermine_role_on_ice_restart;
     rtc::Optional<int> ice_check_min_interval;
+    rtc::Optional<int> stun_candidate_keepalive_interval;
     rtc::Optional<rtc::IntervalRange> ice_regather_interval_range;
     webrtc::TurnCustomizer* turn_customizer;
     SdpSemantics sdp_semantics;
@@ -675,6 +676,8 @@
          enable_ice_renomination == o.enable_ice_renomination &&
          redetermine_role_on_ice_restart == o.redetermine_role_on_ice_restart &&
          ice_check_min_interval == o.ice_check_min_interval &&
+         stun_candidate_keepalive_interval ==
+             o.stun_candidate_keepalive_interval &&
          ice_regather_interval_range == o.ice_regather_interval_range &&
          turn_customizer == o.turn_customizer &&
          sdp_semantics == o.sdp_semantics &&
@@ -2654,6 +2657,8 @@
       configuration.ice_candidate_pool_size;
   modified_config.prune_turn_ports = configuration.prune_turn_ports;
   modified_config.ice_check_min_interval = configuration.ice_check_min_interval;
+  modified_config.stun_candidate_keepalive_interval =
+      configuration.stun_candidate_keepalive_interval;
   modified_config.turn_customizer = configuration.turn_customizer;
   modified_config.network_preference = configuration.network_preference;
   if (configuration != modified_config) {
@@ -2690,7 +2695,8 @@
                     stun_servers, turn_servers, modified_config.type,
                     modified_config.ice_candidate_pool_size,
                     modified_config.prune_turn_ports,
-                    modified_config.turn_customizer))) {
+                    modified_config.turn_customizer,
+                    modified_config.stun_candidate_keepalive_interval))) {
     RTC_LOG(LS_ERROR) << "Failed to apply configuration to PortAllocator.";
     return SafeSetError(RTCErrorType::INTERNAL_ERROR, error);
   }
@@ -4394,10 +4400,10 @@
 
   // Call this last since it may create pooled allocator sessions using the
   // properties set above.
-  port_allocator_->SetConfiguration(stun_servers, turn_servers,
-                                    configuration.ice_candidate_pool_size,
-                                    configuration.prune_turn_ports,
-                                    configuration.turn_customizer);
+  port_allocator_->SetConfiguration(
+      stun_servers, turn_servers, configuration.ice_candidate_pool_size,
+      configuration.prune_turn_ports, configuration.turn_customizer,
+      configuration.stun_candidate_keepalive_interval);
   return true;
 }
 
@@ -4407,14 +4413,15 @@
     IceTransportsType type,
     int candidate_pool_size,
     bool prune_turn_ports,
-    webrtc::TurnCustomizer* turn_customizer) {
+    webrtc::TurnCustomizer* turn_customizer,
+    rtc::Optional<int> stun_candidate_keepalive_interval) {
   port_allocator_->set_candidate_filter(
       ConvertIceTransportTypeToCandidateFilter(type));
   // Call this last since it may create pooled allocator sessions using the
   // candidate filter set above.
   return port_allocator_->SetConfiguration(
       stun_servers, turn_servers, candidate_pool_size, prune_turn_ports,
-      turn_customizer);
+      turn_customizer, stun_candidate_keepalive_interval);
 }
 
 cricket::ChannelManager* PeerConnection::channel_manager() const {
@@ -4778,6 +4785,7 @@
   ice_config.presume_writable_when_fully_relayed =
       config.presume_writable_when_fully_relayed;
   ice_config.ice_check_min_interval = config.ice_check_min_interval;
+  ice_config.stun_keepalive_interval = config.stun_candidate_keepalive_interval;
   ice_config.regather_all_networks_interval_range =
       config.ice_regather_interval_range;
   ice_config.network_preference = config.network_preference;
diff --git a/pc/peerconnection.h b/pc/peerconnection.h
index 3eccb96..d788b1e 100644
--- a/pc/peerconnection.h
+++ b/pc/peerconnection.h
@@ -661,7 +661,8 @@
       IceTransportsType type,
       int candidate_pool_size,
       bool prune_turn_ports,
-      webrtc::TurnCustomizer* turn_customizer);
+      webrtc::TurnCustomizer* turn_customizer,
+      rtc::Optional<int> stun_candidate_keepalive_interval);
 
   // Starts output of an RTC event log to the given output object.
   // This function should only be called from the worker thread.
diff --git a/sdk/android/api/org/webrtc/PeerConnection.java b/sdk/android/api/org/webrtc/PeerConnection.java
index c6598b7..de1e57d 100644
--- a/sdk/android/api/org/webrtc/PeerConnection.java
+++ b/sdk/android/api/org/webrtc/PeerConnection.java
@@ -342,6 +342,7 @@
   }
 
   /** Java version of PeerConnectionInterface.RTCConfiguration */
+  // TODO(qingsi): Resolve the naming inconsistency of fields with/without units.
   public static class RTCConfiguration {
     public IceTransportsType iceTransportsType;
     public List<IceServer> iceServers;
@@ -359,6 +360,10 @@
     public boolean pruneTurnPorts;
     public boolean presumeWritableWhenFullyRelayed;
     public Integer iceCheckMinInterval;
+    // The interval in milliseconds at which STUN candidates will resend STUN binding requests
+    // to keep NAT bindings open.
+    // The default value in the implementation is used if this field is null.
+    public Integer stunCandidateKeepaliveIntervalMs;
     public boolean disableIPv6OnWifi;
     // By default, PeerConnection will use a limited number of IPv6 network
     // interfaces, in order to avoid too many ICE candidate pairs being created
@@ -405,6 +410,7 @@
       pruneTurnPorts = false;
       presumeWritableWhenFullyRelayed = false;
       iceCheckMinInterval = null;
+      stunCandidateKeepaliveIntervalMs = null;
       disableIPv6OnWifi = false;
       maxIPv6Networks = 5;
       iceRegatherIntervalRange = null;
@@ -500,6 +506,11 @@
     }
 
     @CalledByNative("RTCConfiguration")
+    Integer getStunCandidateKeepaliveInterval() {
+      return stunCandidateKeepaliveIntervalMs;
+    }
+
+    @CalledByNative("RTCConfiguration")
     boolean getDisableIPv6OnWifi() {
       return disableIPv6OnWifi;
     }
diff --git a/sdk/android/src/jni/pc/peerconnection.cc b/sdk/android/src/jni/pc/peerconnection.cc
index f6f3932..f954a43 100644
--- a/sdk/android/src/jni/pc/peerconnection.cc
+++ b/sdk/android/src/jni/pc/peerconnection.cc
@@ -156,6 +156,11 @@
       Java_RTCConfiguration_getIceCheckMinInterval(jni, j_rtc_config);
   rtc_config->ice_check_min_interval =
       JavaToNativeOptionalInt(jni, j_ice_check_min_interval);
+  ScopedJavaLocalRef<jobject> j_stun_candidate_keepalive_interval =
+      Java_RTCConfiguration_getStunCandidateKeepaliveInterval(jni,
+                                                              j_rtc_config);
+  rtc_config->stun_candidate_keepalive_interval =
+      JavaToNativeOptionalInt(jni, j_stun_candidate_keepalive_interval);
   rtc_config->disable_ipv6_on_wifi =
       Java_RTCConfiguration_getDisableIPv6OnWifi(jni, j_rtc_config);
   rtc_config->max_ipv6_networks =