Implement (mostly) standards-compliant RTCIceTransportState.

In order to correctly implement RTCPeerConnectionState and RTCIceConnectionState the ice transports need to support RTCIceTransportState.
This CL adds an implementation parallel to the current non-standard IceTransportState. It's not currently used anywhere. The old implementation will remain in place until we're ready to switch RTCIceConnectionState over.

Bug: webrtc:9308
Change-Id: I30e2bbb5b4fafa410261bcd9d5e3b76c03435feb
Reviewed-on: https://webrtc-review.googlesource.com/c/103220
Reviewed-by: Qingsi Wang <qingsi@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Commit-Queue: Jonas Olsson <jonasolsson@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25078}
diff --git a/api/transport/BUILD.gn b/api/transport/BUILD.gn
index 5466061..9341da9 100644
--- a/api/transport/BUILD.gn
+++ b/api/transport/BUILD.gn
@@ -19,6 +19,13 @@
   ]
 }
 
+rtc_source_set("enums") {
+  visibility = [ "*" ]
+  sources = [
+    "enums.h",
+  ]
+}
+
 rtc_static_library("network_control") {
   sources = [
     "network_control.h",
diff --git a/api/transport/enums.h b/api/transport/enums.h
new file mode 100644
index 0000000..b1d5770
--- /dev/null
+++ b/api/transport/enums.h
@@ -0,0 +1,32 @@
+/*
+ *  Copyright 2018 The WebRTC Project Authors. All rights reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_TRANSPORT_ENUMS_H_
+#define API_TRANSPORT_ENUMS_H_
+
+namespace webrtc {
+
+// See https://w3c.github.io/webrtc-pc/#rtcicetransportstate
+// Note that kFailed is currently not a terminal state, and a transport might
+// incorrectly be marked as failed while gathering candidates, see
+// bugs.webrtc.org/8833
+enum class IceTransportState {
+  kNew,
+  kChecking,
+  kConnected,
+  kCompleted,
+  kFailed,
+  kDisconnected,
+  kClosed,
+};
+
+}  // namespace webrtc
+
+#endif  // API_TRANSPORT_ENUMS_H_
diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn
index 2d351bb..5d4e706 100644
--- a/p2p/BUILD.gn
+++ b/p2p/BUILD.gn
@@ -86,6 +86,7 @@
   deps = [
     "../api:libjingle_peerconnection_api",
     "../api:ortc_api",
+    "../api/transport:enums",
     "../logging:ice_log",
     "../rtc_base:checks",
     "../rtc_base:rtc_base",
diff --git a/p2p/base/fakeicetransport.h b/p2p/base/fakeicetransport.h
index 4815745..4ba0a2d 100644
--- a/p2p/base/fakeicetransport.h
+++ b/p2p/base/fakeicetransport.h
@@ -114,6 +114,10 @@
     return IceTransportState::STATE_CONNECTING;
   }
 
+  webrtc::IceTransportState GetIceTransportState() const override {
+    return webrtc::IceTransportState::kConnected;
+  }
+
   void SetIceRole(IceRole role) override { role_ = role; }
   IceRole GetIceRole() const override { return role_; }
   void SetIceTiebreaker(uint64_t tiebreaker) override {
diff --git a/p2p/base/icetransportinternal.h b/p2p/base/icetransportinternal.h
index 7483c3b..31e7078 100644
--- a/p2p/base/icetransportinternal.h
+++ b/p2p/base/icetransportinternal.h
@@ -15,6 +15,7 @@
 #include <vector>
 
 #include "api/candidate.h"
+#include "api/transport/enums.h"
 #include "p2p/base/candidatepairinterface.h"
 #include "p2p/base/packettransportinternal.h"
 #include "p2p/base/port.h"
@@ -188,7 +189,10 @@
   IceTransportInternal();
   ~IceTransportInternal() override;
 
+  // TODO(bugs.webrtc.org/9308): Remove GetState once all uses have been
+  // migrated to GetIceTransportState.
   virtual IceTransportState GetState() const = 0;
+  virtual webrtc::IceTransportState GetIceTransportState() const = 0;
 
   virtual int component() const = 0;
 
@@ -258,8 +262,13 @@
   sigslot::signal1<IceTransportInternal*> SignalRoleConflict;
 
   // Emitted whenever the transport state changed.
+  // TODO(bugs.webrtc.org/9308): Remove once all uses have migrated to the new
+  // IceTransportState.
   sigslot::signal1<IceTransportInternal*> SignalStateChanged;
 
+  // Emitted whenever the new standards-compliant transport state changed.
+  sigslot::signal1<IceTransportInternal*> SignalIceTransportStateChanged;
+
   // Invoked when the transport is being destroyed.
   sigslot::signal1<IceTransportInternal*> SignalDestroyed;
 };
diff --git a/p2p/base/mockicetransport.h b/p2p/base/mockicetransport.h
index d1863c5..b18ce3d 100644
--- a/p2p/base/mockicetransport.h
+++ b/p2p/base/mockicetransport.h
@@ -47,6 +47,10 @@
   IceTransportState GetState() const override {
     return IceTransportState::STATE_INIT;
   }
+  webrtc::IceTransportState GetIceTransportState() const override {
+    return webrtc::IceTransportState::kNew;
+  }
+
   const std::string& transport_name() const override { return transport_name_; }
   int component() const override { return 0; }
   void SetIceRole(IceRole role) override {}
diff --git a/p2p/base/p2ptransportchannel.cc b/p2p/base/p2ptransportchannel.cc
index b0abe24..b5de0e6 100644
--- a/p2p/base/p2ptransportchannel.cc
+++ b/p2p/base/p2ptransportchannel.cc
@@ -323,6 +323,10 @@
   return state_;
 }
 
+webrtc::IceTransportState P2PTransportChannel::GetIceTransportState() const {
+  return standardized_state_;
+}
+
 const std::string& P2PTransportChannel::transport_name() const {
   return transport_name_;
 }
@@ -387,6 +391,41 @@
   return IceTransportState::STATE_COMPLETED;
 }
 
+// Compute the current RTCIceTransportState as described in
+// https://www.w3.org/TR/webrtc/#dom-rtcicetransportstate
+// TODO(bugs.webrtc.org/9308): Return IceTransportState::kDisconnected when it
+// makes sense.
+// TODO(bugs.webrtc.org/9218): Avoid prematurely signalling kFailed once we have
+// implemented end-of-candidates signalling.
+webrtc::IceTransportState P2PTransportChannel::ComputeIceTransportState()
+    const {
+  bool has_connection = false;
+  for (Connection* connection : connections_) {
+    if (connection->active()) {
+      has_connection = true;
+      break;
+    }
+  }
+
+  switch (gathering_state_) {
+    case kIceGatheringComplete:
+      if (has_connection)
+        return webrtc::IceTransportState::kCompleted;
+      else
+        return webrtc::IceTransportState::kFailed;
+    case kIceGatheringNew:
+      return webrtc::IceTransportState::kNew;
+    case kIceGatheringGathering:
+      if (has_connection)
+        return webrtc::IceTransportState::kConnected;
+      else
+        return webrtc::IceTransportState::kChecking;
+    default:
+      RTC_NOTREACHED();
+      return webrtc::IceTransportState::kFailed;
+  }
+}
+
 void P2PTransportChannel::SetIceParameters(const IceParameters& ice_params) {
   RTC_DCHECK(network_thread_ == rtc::Thread::Current());
   RTC_LOG(LS_INFO) << "Set ICE ufrag: " << ice_params.ufrag
@@ -1827,6 +1866,9 @@
 // example, we call this at the end of SortConnectionsAndUpdateState.
 void P2PTransportChannel::UpdateState() {
   IceTransportState state = ComputeState();
+  webrtc::IceTransportState current_standardized_state =
+      ComputeIceTransportState();
+
   if (state_ != state) {
     RTC_LOG(LS_INFO) << ToString()
                      << ": Transport channel state changed from "
@@ -1868,6 +1910,10 @@
     SignalStateChanged(this);
   }
 
+  if (standardized_state_ != current_standardized_state) {
+    standardized_state_ = current_standardized_state;
+    SignalIceTransportStateChanged(this);
+  }
   // If our selected connection is "presumed writable" (TURN-TURN with no
   // CreatePermission required), act like we're already writable to the upper
   // layers, so they can start media quicker.
diff --git a/p2p/base/p2ptransportchannel.h b/p2p/base/p2ptransportchannel.h
index fbed344..84d2879 100644
--- a/p2p/base/p2ptransportchannel.h
+++ b/p2p/base/p2ptransportchannel.h
@@ -91,6 +91,8 @@
 
   // From TransportChannelImpl:
   IceTransportState GetState() const override;
+  webrtc::IceTransportState GetIceTransportState() const override;
+
   const std::string& transport_name() const override;
   int component() const override;
   bool writable() const override;
@@ -243,7 +245,13 @@
   void UpdateState();
   void HandleAllTimedOut();
   void MaybeStopPortAllocatorSessions();
+
+  // ComputeIceTransportState computes the RTCIceTransportState as described in
+  // https://w3c.github.io/webrtc-pc/#dom-rtcicetransportstate. ComputeState
+  // computes the value we currently export as RTCIceTransportState.
+  // TODO(bugs.webrtc.org/9308): Remove ComputeState once it's no longer used.
   IceTransportState ComputeState() const;
+  webrtc::IceTransportState ComputeIceTransportState() const;
 
   Connection* GetBestConnectionOnNetwork(rtc::Network* network) const;
   bool CreateConnections(const Candidate& remote_candidate,
@@ -407,7 +415,11 @@
   std::unique_ptr<webrtc::BasicRegatheringController> regathering_controller_;
   int64_t last_ping_sent_ms_ = 0;
   int weak_ping_interval_ = WEAK_PING_INTERVAL;
+  // TODO(jonasolsson): Remove state_ and rename standardized_state_ once state_
+  // is no longer used to compute the ICE connection state.
   IceTransportState state_ = IceTransportState::STATE_INIT;
+  webrtc::IceTransportState standardized_state_ =
+      webrtc::IceTransportState::kNew;
   IceConfig config_;
   int last_sent_packet_id_ = -1;  // -1 indicates no packet was sent before.
   bool started_pinging_ = false;
diff --git a/p2p/base/p2ptransportchannel_unittest.cc b/p2p/base/p2ptransportchannel_unittest.cc
index 76b918c..9b0bcb2 100644
--- a/p2p/base/p2ptransportchannel_unittest.cc
+++ b/p2p/base/p2ptransportchannel_unittest.cc
@@ -4137,6 +4137,7 @@
   clock.AdvanceTime(webrtc::TimeDelta::seconds(1));
   FakePortAllocator pa(rtc::Thread::Current(), nullptr);
   P2PTransportChannel ch("test channel", 1, &pa);
+  EXPECT_EQ(webrtc::IceTransportState::kNew, ch.GetIceTransportState());
   PrepareChannel(&ch);
   ch.MaybeStartGathering();
   EXPECT_EQ(IceTransportState::STATE_INIT, ch.GetState());
@@ -4144,6 +4145,8 @@
   ch.AddRemoteCandidate(CreateUdpCandidate(LOCAL_PORT_TYPE, "2.2.2.2", 2, 1));
   Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1, &clock);
   Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2, &clock);
+  // Gathering complete with candidates.
+  EXPECT_EQ(webrtc::IceTransportState::kCompleted, ch.GetIceTransportState());
   ASSERT_TRUE(conn1 != nullptr);
   ASSERT_TRUE(conn2 != nullptr);
   // Now there are two connections, so the transport channel is connecting.
@@ -4156,6 +4159,7 @@
   // Need to wait until the channel state is updated.
   EXPECT_EQ_SIMULATED_WAIT(IceTransportState::STATE_FAILED, ch.GetState(),
                            kShortTimeout, clock);
+  EXPECT_EQ(webrtc::IceTransportState::kFailed, ch.GetIceTransportState());
 }
 
 // Test that when a low-priority connection is pruned, it is not deleted