p2p_transport_channel: Add estimated disconnected time to CandidatePairChangeEvent
This patch adds a computed estimate on how long the ice stack
was disconnected before switching to a new connection.
The metric is currently computed as now - max(connection->last_data_recevied())
and has resonably good precision.
Bug: webrtc:11862
Change-Id: I8950d55f0eadcf164de089cdb715b4f7eed0a4c3
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/182002
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Jonas Oreland <jonaso@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#31969}
diff --git a/p2p/base/p2p_transport_channel.cc b/p2p/base/p2p_transport_channel.cc
index 6f0df04..090d5a4 100644
--- a/p2p/base/p2p_transport_channel.cc
+++ b/p2p/base/p2p_transport_channel.cc
@@ -1784,6 +1784,15 @@
pair_change.selected_candidate_pair = *GetSelectedCandidatePair();
pair_change.last_data_received_ms =
selected_connection_->last_data_received();
+
+ if (old_selected_connection) {
+ pair_change.estimated_disconnected_time_ms =
+ ComputeEstimatedDisconnectedTimeMs(rtc::TimeMillis(),
+ old_selected_connection);
+ } else {
+ pair_change.estimated_disconnected_time_ms = 0;
+ }
+
SignalCandidatePairChanged(pair_change);
}
@@ -1792,6 +1801,16 @@
ice_controller_->SetSelectedConnection(selected_connection_);
}
+int64_t P2PTransportChannel::ComputeEstimatedDisconnectedTimeMs(
+ int64_t now_ms,
+ Connection* old_connection) {
+ // TODO(jonaso): nicer keeps estimate of how frequently data _should_ be
+ // received, this could be used to give better estimate (if needed).
+ int64_t last_data_or_old_ping =
+ std::max(old_connection->last_received(), last_data_received_ms_);
+ return (now_ms - last_data_or_old_ping);
+}
+
// Warning: UpdateState should eventually be called whenever a connection
// is added, deleted, or the write state of any connection changes so that the
// transport controller will get the up-to-date channel state. However it
@@ -2110,6 +2129,9 @@
if (connection == selected_connection_) {
// Let the client know of an incoming packet
+ RTC_DCHECK(connection->last_data_received() >= last_data_received_ms_);
+ last_data_received_ms_ =
+ std::max(last_data_received_ms_, connection->last_data_received());
SignalReadPacket(this, data, len, packet_time_us, 0);
return;
}
@@ -2118,6 +2140,10 @@
if (!FindConnection(connection))
return;
+ RTC_DCHECK(connection->last_data_received() >= last_data_received_ms_);
+ last_data_received_ms_ =
+ std::max(last_data_received_ms_, connection->last_data_received());
+
// Let the client know of an incoming packet
SignalReadPacket(this, data, len, packet_time_us, 0);
diff --git a/p2p/base/p2p_transport_channel.h b/p2p/base/p2p_transport_channel.h
index 4f891be..69a32e4 100644
--- a/p2p/base/p2p_transport_channel.h
+++ b/p2p/base/p2p_transport_channel.h
@@ -358,6 +358,9 @@
return const_cast<Connection*>(conn);
}
+ int64_t ComputeEstimatedDisconnectedTimeMs(int64_t now,
+ Connection* old_connection);
+
std::string transport_name_ RTC_GUARDED_BY(network_thread_);
int component_ RTC_GUARDED_BY(network_thread_);
PortAllocator* allocator_ RTC_GUARDED_BY(network_thread_);
@@ -440,6 +443,10 @@
// Number of times the selected_connection_ has been modified.
uint32_t selected_candidate_pair_changes_ = 0;
+ // When was last data received on a existing connection,
+ // from connection->last_data_received() that uses rtc::TimeMillis().
+ int64_t last_data_received_ms_ = 0;
+
IceFieldTrials field_trials_;
RTC_DISALLOW_COPY_AND_ASSIGN(P2PTransportChannel);
diff --git a/p2p/base/p2p_transport_channel_unittest.cc b/p2p/base/p2p_transport_channel_unittest.cc
index cfdee81..ad62920 100644
--- a/p2p/base/p2p_transport_channel_unittest.cc
+++ b/p2p/base/p2p_transport_channel_unittest.cc
@@ -3268,6 +3268,14 @@
}
}
+ int64_t LastEstimatedDisconnectedTimeMs() const {
+ if (!last_candidate_change_event_.has_value()) {
+ return 0;
+ } else {
+ return last_candidate_change_event_->estimated_disconnected_time_ms;
+ }
+ }
+
private:
std::unique_ptr<rtc::VirtualSocketServer> vss_;
rtc::AutoSocketServerThread thread_;
@@ -4088,6 +4096,64 @@
EXPECT_EQ(0, reset_selected_candidate_pair_switches());
}
+TEST_F(P2PTransportChannelPingTest, TestEstimatedDisconnectedTime) {
+ rtc::ScopedFakeClock clock;
+ clock.AdvanceTime(webrtc::TimeDelta::Seconds(1));
+
+ FakePortAllocator pa(rtc::Thread::Current(), nullptr);
+ P2PTransportChannel ch("test", 1, &pa);
+ PrepareChannel(&ch);
+ ch.SetIceRole(ICEROLE_CONTROLLED);
+ ch.MaybeStartGathering();
+ // The connections have decreasing priority.
+ Connection* conn1 =
+ CreateConnectionWithCandidate(&ch, &clock, "1.1.1.1", /* port= */ 1,
+ /* priority= */ 10, /* writable= */ true);
+ ASSERT_TRUE(conn1 != nullptr);
+ Connection* conn2 =
+ CreateConnectionWithCandidate(&ch, &clock, "2.2.2.2", /* port= */ 2,
+ /* priority= */ 9, /* writable= */ true);
+ ASSERT_TRUE(conn2 != nullptr);
+
+ // conn1 is the selected connection because it has a higher priority,
+ EXPECT_EQ_SIMULATED_WAIT(conn1, ch.selected_connection(), kDefaultTimeout,
+ clock);
+ EXPECT_TRUE(CandidatePairMatchesNetworkRoute(conn1));
+ // No estimateded disconnect time at first connect <=> value is 0.
+ EXPECT_EQ(LastEstimatedDisconnectedTimeMs(), 0);
+
+ // Use nomination to force switching of selected connection.
+ int nomination = 1;
+
+ {
+ clock.AdvanceTime(webrtc::TimeDelta::Seconds(1));
+ // This will not parse as STUN, and is considered data
+ conn1->OnReadPacket("XYZ", 3, rtc::TimeMicros());
+ clock.AdvanceTime(webrtc::TimeDelta::Seconds(2));
+
+ // conn2 is nominated; it becomes selected.
+ NominateConnection(conn2, nomination++);
+ EXPECT_EQ(conn2, ch.selected_connection());
+ // We got data 2s ago...guess that we lost 2s of connectivity.
+ EXPECT_EQ(LastEstimatedDisconnectedTimeMs(), 2000);
+ }
+
+ {
+ clock.AdvanceTime(webrtc::TimeDelta::Seconds(1));
+ conn2->OnReadPacket("XYZ", 3, rtc::TimeMicros());
+
+ clock.AdvanceTime(webrtc::TimeDelta::Seconds(2));
+ ReceivePingOnConnection(conn2, kIceUfrag[1], 1, nomination++);
+
+ clock.AdvanceTime(webrtc::TimeDelta::Millis(500));
+
+ ReceivePingOnConnection(conn1, kIceUfrag[1], 1, nomination++);
+ EXPECT_EQ(conn1, ch.selected_connection());
+ // We got ping 500ms ago...guess that we lost 500ms of connectivity.
+ EXPECT_EQ(LastEstimatedDisconnectedTimeMs(), 500);
+ }
+}
+
TEST_F(P2PTransportChannelPingTest,
TestControlledAgentIgnoresSmallerNomination) {
rtc::ScopedFakeClock clock;
diff --git a/p2p/base/port.h b/p2p/base/port.h
index 893e80b..c7be445 100644
--- a/p2p/base/port.h
+++ b/p2p/base/port.h
@@ -150,6 +150,8 @@
CandidatePair selected_candidate_pair;
int64_t last_data_received_ms;
std::string reason;
+ // How long do we estimate that we've been disconnected.
+ int64_t estimated_disconnected_time_ms;
};
typedef std::set<rtc::SocketAddress> ServerAddresses;
diff --git a/sdk/android/api/org/webrtc/CandidatePairChangeEvent.java b/sdk/android/api/org/webrtc/CandidatePairChangeEvent.java
index 395b629..b8e6685 100644
--- a/sdk/android/api/org/webrtc/CandidatePairChangeEvent.java
+++ b/sdk/android/api/org/webrtc/CandidatePairChangeEvent.java
@@ -20,12 +20,20 @@
public final int lastDataReceivedMs;
public final String reason;
+ /**
+ * An estimate from the ICE stack on how long it was disconnected before
+ * changing to the new candidate pair in this event.
+ * The first time an candidate pair is signaled the value will be 0.
+ */
+ public final int estimatedDisconnectedTimeMs;
+
@CalledByNative
- CandidatePairChangeEvent(
- IceCandidate local, IceCandidate remote, int lastDataReceivedMs, String reason) {
+ CandidatePairChangeEvent(IceCandidate local, IceCandidate remote, int lastDataReceivedMs,
+ String reason, int estimatedDisconnectedTimeMs) {
this.local = local;
this.remote = remote;
this.lastDataReceivedMs = lastDataReceivedMs;
this.reason = reason;
+ this.estimatedDisconnectedTimeMs = estimatedDisconnectedTimeMs;
}
}
diff --git a/sdk/android/src/jni/pc/peer_connection.cc b/sdk/android/src/jni/pc/peer_connection.cc
index 1165333..6706782 100644
--- a/sdk/android/src/jni/pc/peer_connection.cc
+++ b/sdk/android/src/jni/pc/peer_connection.cc
@@ -128,7 +128,8 @@
env, NativeToJavaCandidate(env, selected_pair.local_candidate()),
NativeToJavaCandidate(env, selected_pair.remote_candidate()),
static_cast<int>(event.last_data_received_ms),
- NativeToJavaString(env, event.reason));
+ NativeToJavaString(env, event.reason),
+ static_cast<int>(event.estimated_disconnected_time_ms));
}
} // namespace