Log DTLS state changes to RtcEventLog

Bug: webrtc:9972
Change-Id: Icb2cdda87c1c42607ed06653e053699532f25e84
Reviewed-on: https://webrtc-review.googlesource.com/c/111023
Commit-Queue: Zach Stein <zstein@webrtc.org>
Reviewed-by: Qingsi Wang <qingsi@webrtc.org>
Reviewed-by: Björn Terelius <terelius@webrtc.org>
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Elad Alon <eladalon@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25832}
diff --git a/logging/BUILD.gn b/logging/BUILD.gn
index ebfad5c..0688485 100644
--- a/logging/BUILD.gn
+++ b/logging/BUILD.gn
@@ -400,6 +400,8 @@
 
 rtc_source_set("ice_log") {
   sources = [
+    "rtc_event_log/events/rtc_event_dtls_transport_state.cc",
+    "rtc_event_log/events/rtc_event_dtls_transport_state.h",
     "rtc_event_log/events/rtc_event_ice_candidate_pair.cc",
     "rtc_event_log/events/rtc_event_ice_candidate_pair.h",
     "rtc_event_log/events/rtc_event_ice_candidate_pair_config.cc",
diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc b/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc
index 9076500..fa1c28f 100644
--- a/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc
+++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_legacy.cc
@@ -297,6 +297,10 @@
       return EncodeBweUpdateLossBased(rtc_event);
     }
 
+    case RtcEvent::Type::DtlsTransportState: {
+      return "";
+    }
+
     case RtcEvent::Type::IceCandidatePairConfig: {
       auto& rtc_event =
           static_cast<const RtcEventIceCandidatePairConfig&>(event);
diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc b/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc
index 79dc379..1e19120 100644
--- a/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc
+++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.cc
@@ -22,6 +22,7 @@
 #include "logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h"
 #include "logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h"
 #include "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h"
+#include "logging/rtc_event_log/events/rtc_event_dtls_transport_state.h"
 #include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h"
 #include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h"
 #include "logging/rtc_event_log/events/rtc_event_probe_cluster_created.h"
@@ -125,6 +126,26 @@
   return unknown_extensions < extensions.size();
 }
 
+rtclog2::DtlsTransportStateEvent::DtlsTransportState ConvertToProtoFormat(
+    webrtc::DtlsTransportState state) {
+  switch (state) {
+    case webrtc::DtlsTransportState::kNew:
+      return rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_NEW;
+    case webrtc::DtlsTransportState::kConnecting:
+      return rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_CONNECTING;
+    case webrtc::DtlsTransportState::kConnected:
+      return rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_CONNECTED;
+    case webrtc::DtlsTransportState::kClosed:
+      return rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_CLOSED;
+    case webrtc::DtlsTransportState::kFailed:
+      return rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_FAILED;
+    case webrtc::DtlsTransportState::kNumValues:
+      RTC_NOTREACHED();
+  }
+  RTC_NOTREACHED();
+  return rtclog2::DtlsTransportStateEvent::UNKNOWN_DTLS_TRANSPORT_STATE;
+}
+
 rtclog2::IceCandidatePairConfig::IceCandidatePairConfigType
 ConvertToProtoFormat(IceCandidatePairConfigType type) {
   switch (type) {
@@ -648,6 +669,7 @@
     std::vector<const RtcEventAudioSendStreamConfig*> audio_send_stream_configs;
     std::vector<const RtcEventBweUpdateDelayBased*> bwe_delay_based_updates;
     std::vector<const RtcEventBweUpdateLossBased*> bwe_loss_based_updates;
+    std::vector<const RtcEventDtlsTransportState*> dtls_transport_states;
     std::vector<const RtcEventProbeClusterCreated*>
         probe_cluster_created_events;
     std::vector<const RtcEventProbeResultFailure*> probe_result_failure_events;
@@ -711,6 +733,12 @@
           bwe_loss_based_updates.push_back(rtc_event);
           break;
         }
+        case RtcEvent::Type::DtlsTransportState: {
+          auto* rtc_event =
+              static_cast<const RtcEventDtlsTransportState* const>(it->get());
+          dtls_transport_states.push_back(rtc_event);
+          break;
+        }
         case RtcEvent::Type::ProbeClusterCreated: {
           auto* rtc_event =
               static_cast<const RtcEventProbeClusterCreated* const>(it->get());
@@ -793,6 +821,7 @@
     EncodeAudioSendStreamConfig(audio_send_stream_configs, &event_stream);
     EncodeBweUpdateDelayBased(bwe_delay_based_updates, &event_stream);
     EncodeBweUpdateLossBased(bwe_loss_based_updates, &event_stream);
+    EncodeDtlsTransportState(dtls_transport_states, &event_stream);
     EncodeProbeClusterCreated(probe_cluster_created_events, &event_stream);
     EncodeProbeResultFailure(probe_result_failure_events, &event_stream);
     EncodeProbeResultSuccess(probe_result_success_events, &event_stream);
@@ -1172,6 +1201,18 @@
   }
 }
 
+void RtcEventLogEncoderNewFormat::EncodeDtlsTransportState(
+    rtc::ArrayView<const RtcEventDtlsTransportState*> batch,
+    rtclog2::EventStream* event_stream) {
+  for (const RtcEventDtlsTransportState* base_event : batch) {
+    rtclog2::DtlsTransportStateEvent* proto_batch =
+        event_stream->add_dtls_transport_state_events();
+    proto_batch->set_timestamp_ms(base_event->timestamp_ms());
+    proto_batch->set_dtls_transport_state(
+        ConvertToProtoFormat(base_event->dtls_transport_state()));
+  }
+}
+
 void RtcEventLogEncoderNewFormat::EncodeProbeClusterCreated(
     rtc::ArrayView<const RtcEventProbeClusterCreated*> batch,
     rtclog2::EventStream* event_stream) {
diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h b/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h
index 7dfa490..6ffab87 100644
--- a/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h
+++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_new_format.h
@@ -33,6 +33,7 @@
 class RtcEventAudioSendStreamConfig;
 class RtcEventBweUpdateDelayBased;
 class RtcEventBweUpdateLossBased;
+class RtcEventDtlsTransportState;
 class RtcEventLoggingStarted;
 class RtcEventLoggingStopped;
 class RtcEventProbeClusterCreated;
@@ -81,6 +82,9 @@
   void EncodeBweUpdateLossBased(
       rtc::ArrayView<const RtcEventBweUpdateLossBased*> batch,
       rtclog2::EventStream* event_stream);
+  void EncodeDtlsTransportState(
+      rtc::ArrayView<const RtcEventDtlsTransportState*> batch,
+      rtclog2::EventStream* event_stream);
   void EncodeLoggingStarted(rtc::ArrayView<const RtcEventLoggingStarted*> batch,
                             rtclog2::EventStream* event_stream);
   void EncodeLoggingStopped(rtc::ArrayView<const RtcEventLoggingStopped*> batch,
diff --git a/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc b/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc
index 856bec6..ec9687e 100644
--- a/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc
+++ b/logging/rtc_event_log/encoder/rtc_event_log_encoder_unittest.cc
@@ -464,6 +464,32 @@
   }
 }
 
+TEST_P(RtcEventLogEncoderTest, RtcEventDtlsTransportState) {
+  std::vector<std::unique_ptr<RtcEventDtlsTransportState>> events(event_count_);
+  for (size_t i = 0; i < event_count_; ++i) {
+    events[i] = (i == 0 || !force_repeated_fields_)
+                    ? gen_.NewDtlsTransportState()
+                    : events[0]->Copy();
+    history_.push_back(events[i]->Copy());
+  }
+
+  const std::string encoded =
+      encoder_->EncodeBatch(history_.begin(), history_.end());
+  ASSERT_TRUE(parsed_log_.ParseString(encoded));
+
+  const auto& dtls_transport_states = parsed_log_.dtls_transport_states();
+  if (!new_encoding_) {
+    ASSERT_EQ(dtls_transport_states.size(), 0u);
+    return;
+  }
+
+  ASSERT_EQ(dtls_transport_states.size(), event_count_);
+  for (size_t i = 0; i < event_count_; ++i) {
+    verifier_.VerifyLoggedDtlsTransportState(*events[i],
+                                             dtls_transport_states[i]);
+  }
+}
+
 // TODO(eladalon/terelius): Test with multiple events in the batch.
 TEST_P(RtcEventLogEncoderTest, RtcEventIceCandidatePairConfig) {
   std::unique_ptr<RtcEventIceCandidatePairConfig> event =
diff --git a/logging/rtc_event_log/events/rtc_event.h b/logging/rtc_event_log/events/rtc_event.h
index 7ae6375..aa49097 100644
--- a/logging/rtc_event_log/events/rtc_event.h
+++ b/logging/rtc_event_log/events/rtc_event.h
@@ -37,6 +37,7 @@
     AudioSendStreamConfig,
     BweUpdateDelayBased,
     BweUpdateLossBased,
+    DtlsTransportState,
     IceCandidatePairConfig,
     IceCandidatePairEvent,
     ProbeClusterCreated,
diff --git a/logging/rtc_event_log/events/rtc_event_dtls_transport_state.cc b/logging/rtc_event_log/events/rtc_event_dtls_transport_state.cc
new file mode 100644
index 0000000..ac8e642
--- /dev/null
+++ b/logging/rtc_event_log/events/rtc_event_dtls_transport_state.cc
@@ -0,0 +1,41 @@
+/*
+ *  Copyright (c) 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.
+ */
+
+#include "logging/rtc_event_log/events/rtc_event_dtls_transport_state.h"
+
+#include "absl/memory/memory.h"
+
+namespace webrtc {
+
+RtcEventDtlsTransportState::RtcEventDtlsTransportState(DtlsTransportState state)
+    : dtls_transport_state_(state) {}
+
+RtcEventDtlsTransportState::RtcEventDtlsTransportState(
+    const RtcEventDtlsTransportState& other)
+    : RtcEvent(other.timestamp_us_),
+      dtls_transport_state_(other.dtls_transport_state_) {}
+
+RtcEventDtlsTransportState::~RtcEventDtlsTransportState() = default;
+
+RtcEvent::Type RtcEventDtlsTransportState::GetType() const {
+  return RtcEvent::Type::DtlsTransportState;
+}
+
+bool RtcEventDtlsTransportState::IsConfigEvent() const {
+  return false;
+}
+
+std::unique_ptr<RtcEventDtlsTransportState> RtcEventDtlsTransportState::Copy()
+    const {
+  return absl::WrapUnique<RtcEventDtlsTransportState>(
+      new RtcEventDtlsTransportState(*this));
+}
+
+}  // namespace webrtc
diff --git a/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h b/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h
new file mode 100644
index 0000000..e76cfe9
--- /dev/null
+++ b/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h
@@ -0,0 +1,51 @@
+/*
+ *  Copyright (c) 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 LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DTLS_TRANSPORT_STATE_H_
+#define LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DTLS_TRANSPORT_STATE_H_
+
+#include <memory>
+
+#include "logging/rtc_event_log/events/rtc_event.h"
+
+namespace webrtc {
+
+enum class DtlsTransportState {
+  kNew,
+  kConnecting,
+  kConnected,
+  kClosed,
+  kFailed,
+  kNumValues
+};
+
+class RtcEventDtlsTransportState : public RtcEvent {
+ public:
+  explicit RtcEventDtlsTransportState(DtlsTransportState state);
+  ~RtcEventDtlsTransportState() override;
+
+  Type GetType() const override;
+  bool IsConfigEvent() const override;
+
+  std::unique_ptr<RtcEventDtlsTransportState> Copy() const;
+
+  DtlsTransportState dtls_transport_state() const {
+    return dtls_transport_state_;
+  }
+
+ private:
+  RtcEventDtlsTransportState(const RtcEventDtlsTransportState& other);
+
+  const DtlsTransportState dtls_transport_state_;
+};
+
+}  // namespace webrtc
+
+#endif  // LOGGING_RTC_EVENT_LOG_EVENTS_RTC_EVENT_DTLS_TRANSPORT_STATE_H_
diff --git a/logging/rtc_event_log/rtc_event_log2.proto b/logging/rtc_event_log/rtc_event_log2.proto
index 995853b..07e731a 100644
--- a/logging/rtc_event_log/rtc_event_log2.proto
+++ b/logging/rtc_event_log/rtc_event_log2.proto
@@ -32,6 +32,7 @@
   repeated AlrState alr_states = 24;
   repeated IceCandidatePairConfig ice_candidate_configs = 25;
   repeated IceCandidatePairEvent ice_candidate_events = 26;
+  repeated DtlsTransportStateEvent dtls_transport_state_events = 27;
 
   repeated AudioRecvStreamConfig audio_recv_stream_configs = 101;
   repeated AudioSendStreamConfig audio_send_stream_configs = 102;
@@ -571,3 +572,20 @@
   // required
   optional uint32 candidate_pair_id = 3;
 }
+
+message DtlsTransportStateEvent {
+  enum DtlsTransportState {
+    UNKNOWN_DTLS_TRANSPORT_STATE = 0;
+    DTLS_TRANSPORT_NEW = 1;
+    DTLS_TRANSPORT_CONNECTING = 2;
+    DTLS_TRANSPORT_CONNECTED = 3;
+    DTLS_TRANSPORT_CLOSED = 4;
+    DTLS_TRANSPORT_FAILED = 5;
+  }
+
+  // required
+  optional int64 timestamp_ms = 1;
+
+  // required
+  optional DtlsTransportState dtls_transport_state = 2;
+}
\ No newline at end of file
diff --git a/logging/rtc_event_log/rtc_event_log_parser_new.cc b/logging/rtc_event_log/rtc_event_log_parser_new.cc
index 7278ea1..60e5915 100644
--- a/logging/rtc_event_log/rtc_event_log_parser_new.cc
+++ b/logging/rtc_event_log/rtc_event_log_parser_new.cc
@@ -208,6 +208,27 @@
   return ProbeFailureReason::kTimeout;
 }
 
+DtlsTransportState GetRuntimeDtlsTransportState(
+    rtclog2::DtlsTransportStateEvent::DtlsTransportState state) {
+  switch (state) {
+    case rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_NEW:
+      return DtlsTransportState::kNew;
+    case rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_CONNECTING:
+      return DtlsTransportState::kConnecting;
+    case rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_CONNECTED:
+      return DtlsTransportState::kConnected;
+    case rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_CLOSED:
+      return DtlsTransportState::kClosed;
+    case rtclog2::DtlsTransportStateEvent::DTLS_TRANSPORT_FAILED:
+      return DtlsTransportState::kFailed;
+    case rtclog2::DtlsTransportStateEvent::UNKNOWN_DTLS_TRANSPORT_STATE:
+      RTC_NOTREACHED();
+      return DtlsTransportState::kNumValues;
+  }
+  RTC_NOTREACHED();
+  return DtlsTransportState::kNumValues;
+}
+
 IceCandidatePairConfigType GetRuntimeIceCandidatePairConfigType(
     rtclog2::IceCandidatePairConfig::IceCandidatePairConfigType type) {
   switch (type) {
@@ -839,6 +860,7 @@
   bwe_probe_success_events_.clear();
   bwe_delay_updates_.clear();
   bwe_loss_updates_.clear();
+  dtls_transport_states_.clear();
   alr_state_events_.clear();
   ice_candidate_pair_configs_.clear();
   ice_candidate_pair_events_.clear();
@@ -1817,6 +1839,7 @@
           stream.audio_playout_events_size() + stream.begin_log_events_size() +
           stream.end_log_events_size() + stream.loss_based_bwe_updates_size() +
           stream.delay_based_bwe_updates_size() +
+          stream.dtls_transport_state_events_size() +
           stream.audio_network_adaptations_size() +
           stream.probe_clusters_size() + stream.probe_success_size() +
           stream.probe_failure_size() + stream.alr_states_size() +
@@ -1846,6 +1869,8 @@
     StoreBweLossBasedUpdate(stream.loss_based_bwe_updates(0));
   } else if (stream.delay_based_bwe_updates_size() == 1) {
     StoreBweDelayBasedUpdate(stream.delay_based_bwe_updates(0));
+  } else if (stream.dtls_transport_state_events_size() == 1) {
+    StoreDtlsTransportState(stream.dtls_transport_state_events(0));
   } else if (stream.audio_network_adaptations_size() == 1) {
     StoreAudioNetworkAdaptationEvent(stream.audio_network_adaptations(0));
   } else if (stream.probe_clusters_size() == 1) {
@@ -2294,6 +2319,19 @@
   }
 }
 
+void ParsedRtcEventLogNew::StoreDtlsTransportState(
+    const rtclog2::DtlsTransportStateEvent& proto) {
+  LoggedDtlsTransportState dtls_state;
+  RTC_CHECK(proto.has_timestamp_ms());
+  dtls_state.timestamp_us = proto.timestamp_ms() * 1000;
+
+  RTC_CHECK(proto.has_dtls_transport_state());
+  dtls_state.dtls_transport_state =
+      GetRuntimeDtlsTransportState(proto.dtls_transport_state());
+
+  dtls_transport_states_.push_back(dtls_state);
+}
+
 void ParsedRtcEventLogNew::StoreIceCandidatePairConfig(
     const rtclog2::IceCandidatePairConfig& proto) {
   LoggedIceCandidatePairConfig ice_config;
diff --git a/logging/rtc_event_log/rtc_event_log_parser_new.h b/logging/rtc_event_log/rtc_event_log_parser_new.h
index 50e3b99..3d2adf9 100644
--- a/logging/rtc_event_log/rtc_event_log_parser_new.h
+++ b/logging/rtc_event_log/rtc_event_log_parser_new.h
@@ -20,6 +20,7 @@
 
 #include "call/video_receive_stream.h"
 #include "call/video_send_stream.h"
+#include "logging/rtc_event_log/events/rtc_event_dtls_transport_state.h"
 #include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h"
 #include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h"
 #include "logging/rtc_event_log/events/rtc_event_probe_result_failure.h"
@@ -130,6 +131,14 @@
   int32_t expected_packets;
 };
 
+struct LoggedDtlsTransportState {
+  int64_t log_time_us() const { return timestamp_us; }
+  int64_t log_time_ms() const { return timestamp_us / 1000; }
+
+  int64_t timestamp_us;
+  DtlsTransportState dtls_transport_state;
+};
+
 struct LoggedBweProbeClusterCreatedEvent {
   LoggedBweProbeClusterCreatedEvent() = default;
   LoggedBweProbeClusterCreatedEvent(int64_t timestamp_us,
@@ -764,6 +773,11 @@
     return alr_state_events_;
   }
 
+  // DTLS
+  const std::vector<LoggedDtlsTransportState>& dtls_transport_states() const {
+    return dtls_transport_states_;
+  }
+
   // ICE events
   const std::vector<LoggedIceCandidatePairConfig>& ice_candidate_pair_configs()
       const {
@@ -933,6 +947,7 @@
   void StoreBweProbeSuccessEvent(const rtclog2::BweProbeResultSuccess& proto);
   void StoreBweProbeFailureEvent(const rtclog2::BweProbeResultFailure& proto);
   void StoreAlrStateEvent(const rtclog2::AlrState& proto);
+  void StoreDtlsTransportState(const rtclog2::DtlsTransportStateEvent& proto);
   void StoreIceCandidatePairConfig(
       const rtclog2::IceCandidatePairConfig& proto);
   void StoreIceCandidateEvent(const rtclog2::IceCandidatePairEvent& proto);
@@ -1027,6 +1042,8 @@
   // A list of all updates from the send-side loss-based bandwidth estimator.
   std::vector<LoggedBweLossBasedUpdate> bwe_loss_updates_;
 
+  std::vector<LoggedDtlsTransportState> dtls_transport_states_;
+
   std::vector<LoggedAlrStateEvent> alr_state_events_;
 
   std::vector<LoggedIceCandidatePairConfig> ice_candidate_pair_configs_;
diff --git a/logging/rtc_event_log/rtc_event_log_unittest.cc b/logging/rtc_event_log/rtc_event_log_unittest.cc
index f7b7898..7d92a79 100644
--- a/logging/rtc_event_log/rtc_event_log_unittest.cc
+++ b/logging/rtc_event_log/rtc_event_log_unittest.cc
@@ -23,6 +23,7 @@
 #include "logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h"
 #include "logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h"
 #include "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h"
+#include "logging/rtc_event_log/events/rtc_event_dtls_transport_state.h"
 #include "logging/rtc_event_log/events/rtc_event_probe_cluster_created.h"
 #include "logging/rtc_event_log/events/rtc_event_probe_result_failure.h"
 #include "logging/rtc_event_log/events/rtc_event_probe_result_success.h"
@@ -73,6 +74,7 @@
   size_t ana_configs = 0;
   size_t bwe_loss_events = 0;
   size_t bwe_delay_events = 0;
+  size_t dtls_transport_states = 0;
   size_t probe_creations = 0;
   size_t probe_successes = 0;
   size_t probe_failures = 0;
@@ -85,9 +87,10 @@
 
   size_t total_nonconfig_events() const {
     return alr_states + audio_playouts + ana_configs + bwe_loss_events +
-           bwe_delay_events + probe_creations + probe_successes +
-           probe_failures + ice_configs + ice_events + incoming_rtp_packets +
-           outgoing_rtp_packets + incoming_rtcp_packets + outgoing_rtcp_packets;
+           bwe_delay_events + dtls_transport_states + probe_creations +
+           probe_successes + probe_failures + ice_configs + ice_events +
+           incoming_rtp_packets + outgoing_rtp_packets + incoming_rtcp_packets +
+           outgoing_rtcp_packets;
   }
 
   size_t total_config_events() const {
@@ -155,6 +158,8 @@
       ana_configs_list_;
   std::vector<std::unique_ptr<RtcEventBweUpdateLossBased>> bwe_loss_list_;
   std::vector<std::unique_ptr<RtcEventBweUpdateDelayBased>> bwe_delay_list_;
+  std::vector<std::unique_ptr<RtcEventDtlsTransportState>>
+      dtls_transport_state_list_;
   std::vector<std::unique_ptr<RtcEventProbeClusterCreated>>
       probe_creation_list_;
   std::vector<std::unique_ptr<RtcEventProbeResultSuccess>> probe_success_list_;
@@ -392,6 +397,15 @@
     }
     selection -= count.probe_failures;
 
+    if (selection < count.dtls_transport_states) {
+      auto event = gen_.NewDtlsTransportState();
+      event_log->Log(event->Copy());
+      dtls_transport_state_list_.push_back(std::move(event));
+      count.dtls_transport_states--;
+      continue;
+    }
+    selection -= count.dtls_transport_states;
+
     if (selection < count.ice_configs) {
       auto event = gen_.NewIceCandidatePairConfig();
       event_log->Log(event->Copy());
@@ -646,6 +660,7 @@
   count.ana_configs = 3;
   count.bwe_loss_events = 20;
   count.bwe_delay_events = 20;
+  count.dtls_transport_states = 4;
   count.probe_creations = 4;
   count.probe_successes = 2;
   count.probe_failures = 2;
@@ -671,6 +686,7 @@
   count.ana_configs = 10;
   count.bwe_loss_events = 50;
   count.bwe_delay_events = 50;
+  count.dtls_transport_states = 4;
   count.probe_creations = 10;
   count.probe_successes = 5;
   count.probe_failures = 5;
diff --git a/logging/rtc_event_log/rtc_event_log_unittest_helper.cc b/logging/rtc_event_log/rtc_event_log_unittest_helper.cc
index 4e24d68..7e20d43 100644
--- a/logging/rtc_event_log/rtc_event_log_unittest_helper.cc
+++ b/logging/rtc_event_log/rtc_event_log_unittest_helper.cc
@@ -130,6 +130,14 @@
       bitrate_bps, fraction_lost, total_packets);
 }
 
+std::unique_ptr<RtcEventDtlsTransportState>
+EventGenerator::NewDtlsTransportState() {
+  DtlsTransportState state = static_cast<DtlsTransportState>(
+      prng_.Rand(static_cast<uint32_t>(DtlsTransportState::kNumValues) - 1));
+
+  return absl::make_unique<RtcEventDtlsTransportState>(state);
+}
+
 std::unique_ptr<RtcEventProbeClusterCreated>
 EventGenerator::NewProbeClusterCreated() {
   constexpr int kMaxBweBps = 20000000;
@@ -592,6 +600,14 @@
   EXPECT_EQ(original_event.bitrate_bps(), logged_event.bitrate_bps);
 }
 
+void EventVerifier::VerifyLoggedDtlsTransportState(
+    const RtcEventDtlsTransportState& original_event,
+    const LoggedDtlsTransportState& logged_event) const {
+  EXPECT_EQ(original_event.timestamp_ms(), logged_event.log_time_ms());
+  EXPECT_EQ(original_event.dtls_transport_state(),
+            logged_event.dtls_transport_state);
+}
+
 void EventVerifier::VerifyLoggedIceCandidatePairConfig(
     const RtcEventIceCandidatePairConfig& original_event,
     const LoggedIceCandidatePairConfig& logged_event) const {
diff --git a/logging/rtc_event_log/rtc_event_log_unittest_helper.h b/logging/rtc_event_log/rtc_event_log_unittest_helper.h
index 55722e0..f3a7d65 100644
--- a/logging/rtc_event_log/rtc_event_log_unittest_helper.h
+++ b/logging/rtc_event_log/rtc_event_log_unittest_helper.h
@@ -22,6 +22,7 @@
 #include "logging/rtc_event_log/events/rtc_event_audio_send_stream_config.h"
 #include "logging/rtc_event_log/events/rtc_event_bwe_update_delay_based.h"
 #include "logging/rtc_event_log/events/rtc_event_bwe_update_loss_based.h"
+#include "logging/rtc_event_log/events/rtc_event_dtls_transport_state.h"
 #include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair.h"
 #include "logging/rtc_event_log/events/rtc_event_ice_candidate_pair_config.h"
 #include "logging/rtc_event_log/events/rtc_event_probe_cluster_created.h"
@@ -60,6 +61,8 @@
 
   std::unique_ptr<RtcEventBweUpdateLossBased> NewBweUpdateLossBased();
 
+  std::unique_ptr<RtcEventDtlsTransportState> NewDtlsTransportState();
+
   std::unique_ptr<RtcEventProbeClusterCreated> NewProbeClusterCreated();
 
   std::unique_ptr<RtcEventProbeResultFailure> NewProbeResultFailure();
@@ -161,6 +164,10 @@
       const RtcEventProbeResultSuccess& original_event,
       const LoggedBweProbeSuccessEvent& logged_event) const;
 
+  void VerifyLoggedDtlsTransportState(
+      const RtcEventDtlsTransportState& original_event,
+      const LoggedDtlsTransportState& logged_event) const;
+
   void VerifyLoggedIceCandidatePairConfig(
       const RtcEventIceCandidatePairConfig& original_event,
       const LoggedIceCandidatePairConfig& logged_event) const;
diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn
index 688d788..8611a65 100644
--- a/p2p/BUILD.gn
+++ b/p2p/BUILD.gn
@@ -88,6 +88,7 @@
     "../api:ortc_api",
     "../api/transport:enums",
     "../logging:ice_log",
+    "../logging:rtc_event_log_api",
     "../rtc_base:checks",
     "../rtc_base:rtc_base",
     "../rtc_base:safe_minmax",
diff --git a/p2p/base/dtlstransport.cc b/p2p/base/dtlstransport.cc
index 97c6b13..51091a3 100644
--- a/p2p/base/dtlstransport.cc
+++ b/p2p/base/dtlstransport.cc
@@ -14,6 +14,9 @@
 
 #include "p2p/base/dtlstransport.h"
 
+#include "absl/memory/memory.h"
+#include "logging/rtc_event_log/events/rtc_event_dtls_transport_state.h"
+#include "logging/rtc_event_log/rtc_event_log.h"
 #include "p2p/base/packettransportinternal.h"
 #include "rtc_base/buffer.h"
 #include "rtc_base/checks.h"
@@ -116,14 +119,16 @@
 
 DtlsTransport::DtlsTransport(
     std::unique_ptr<IceTransportInternal> ice_transport,
-    const webrtc::CryptoOptions& crypto_options)
+    const webrtc::CryptoOptions& crypto_options,
+    webrtc::RtcEventLog* event_log)
     : transport_name_(ice_transport->transport_name()),
       component_(ice_transport->component()),
       ice_transport_(std::move(ice_transport)),
       downward_(NULL),
       srtp_ciphers_(crypto_options.GetSupportedDtlsSrtpCryptoSuites()),
       ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_12),
-      crypto_options_(crypto_options) {
+      crypto_options_(crypto_options),
+      event_log_(event_log) {
   RTC_DCHECK(ice_transport_);
   ConnectToIceTransport();
 }
@@ -754,10 +759,32 @@
   SignalWritableState(this);
 }
 
+static webrtc::DtlsTransportState ConvertDtlsTransportState(
+    cricket::DtlsTransportState cricket_state) {
+  switch (cricket_state) {
+    case DtlsTransportState::DTLS_TRANSPORT_NEW:
+      return webrtc::DtlsTransportState::kNew;
+    case DtlsTransportState::DTLS_TRANSPORT_CONNECTING:
+      return webrtc::DtlsTransportState::kConnecting;
+    case DtlsTransportState::DTLS_TRANSPORT_CONNECTED:
+      return webrtc::DtlsTransportState::kConnected;
+    case DtlsTransportState::DTLS_TRANSPORT_CLOSED:
+      return webrtc::DtlsTransportState::kClosed;
+    case DtlsTransportState::DTLS_TRANSPORT_FAILED:
+      return webrtc::DtlsTransportState::kFailed;
+  }
+  RTC_NOTREACHED();
+  return webrtc::DtlsTransportState::kNew;
+}
+
 void DtlsTransport::set_dtls_state(DtlsTransportState state) {
   if (dtls_state_ == state) {
     return;
   }
+  if (event_log_) {
+    event_log_->Log(absl::make_unique<webrtc::RtcEventDtlsTransportState>(
+        ConvertDtlsTransportState(state)));
+  }
   RTC_LOG(LS_VERBOSE) << ToString() << ": set_dtls_state from:" << dtls_state_
                       << " to " << state;
   dtls_state_ = state;
diff --git a/p2p/base/dtlstransport.h b/p2p/base/dtlstransport.h
index ce95803..6fb5d6c 100644
--- a/p2p/base/dtlstransport.h
+++ b/p2p/base/dtlstransport.h
@@ -95,8 +95,12 @@
   //
   // |crypto_options| are the options used for the DTLS handshake. This affects
   // whether GCM crypto suites are negotiated.
+  //
+  // |event_log| is an optional RtcEventLog for logging state changes. It should
+  // outlive the DtlsTransport.
   explicit DtlsTransport(std::unique_ptr<IceTransportInternal> ice_transport,
-                         const webrtc::CryptoOptions& crypto_options);
+                         const webrtc::CryptoOptions& crypto_options,
+                         webrtc::RtcEventLog* event_log);
 
   ~DtlsTransport() override;
 
@@ -240,6 +244,8 @@
   bool receiving_ = false;
   bool writable_ = false;
 
+  webrtc::RtcEventLog* const event_log_;
+
   RTC_DISALLOW_COPY_AND_ASSIGN(DtlsTransport);
 };
 
diff --git a/p2p/base/dtlstransport_unittest.cc b/p2p/base/dtlstransport_unittest.cc
index 93f3da3..824c39f 100644
--- a/p2p/base/dtlstransport_unittest.cc
+++ b/p2p/base/dtlstransport_unittest.cc
@@ -88,7 +88,8 @@
         this, &DtlsTestClient::OnFakeIceTransportReadPacket);
 
     dtls_transport_ = absl::make_unique<DtlsTransport>(
-        std::move(fake_ice_transport), webrtc::CryptoOptions());
+        std::move(fake_ice_transport), webrtc::CryptoOptions(),
+        /*event_log=*/nullptr);
     dtls_transport_->SetSslMaxProtocolVersion(ssl_max_version_);
     // Note: Certificate may be null here if testing passthrough.
     dtls_transport_->SetLocalCertificate(certificate_);
diff --git a/pc/jseptransportcontroller.cc b/pc/jseptransportcontroller.cc
index a7a18b4..03899c3 100644
--- a/pc/jseptransportcontroller.cc
+++ b/pc/jseptransportcontroller.cc
@@ -14,6 +14,7 @@
 #include <memory>
 #include <utility>
 
+#include "absl/memory/memory.h"
 #include "p2p/base/port.h"
 #include "pc/srtpfilter.h"
 #include "rtc_base/bind.h"
@@ -425,8 +426,8 @@
     auto ice = absl::make_unique<cricket::P2PTransportChannel>(
         transport_name, component, port_allocator_, async_resolver_factory_,
         config_.event_log);
-    dtls = absl::make_unique<cricket::DtlsTransport>(std::move(ice),
-                                                     config_.crypto_options);
+    dtls = absl::make_unique<cricket::DtlsTransport>(
+        std::move(ice), config_.crypto_options, config_.event_log);
   }
 
   RTC_DCHECK(dtls);