Allow TransportController to create a QuicTransportChannel

A QuicTransport is implemented that subclasses Transport
and takes ownership of the QuicTransportChannel/P2PTransportChannel.

Split from CL https://codereview.webrtc.org/1844803002/.

BUG=

Review-Url: https://codereview.webrtc.org/1856943002
Cr-Commit-Position: refs/heads/master@{#12575}
diff --git a/webrtc/base/sslfingerprint.cc b/webrtc/base/sslfingerprint.cc
index 1939b4f..2c3e1e9 100644
--- a/webrtc/base/sslfingerprint.cc
+++ b/webrtc/base/sslfingerprint.cc
@@ -85,7 +85,7 @@
   return fingerprint;
 }
 
-std::string SSLFingerprint::ToString() {
+std::string SSLFingerprint::ToString() const {
   std::string fp_str = algorithm;
   fp_str.append(" ");
   fp_str.append(GetRfc4572Fingerprint());
diff --git a/webrtc/base/sslfingerprint.h b/webrtc/base/sslfingerprint.h
index 1413a4c..4ffb2b0 100644
--- a/webrtc/base/sslfingerprint.h
+++ b/webrtc/base/sslfingerprint.h
@@ -41,7 +41,7 @@
 
   std::string GetRfc4572Fingerprint() const;
 
-  std::string ToString();
+  std::string ToString() const;
 
   std::string algorithm;
   rtc::CopyOnWriteBuffer digest;
diff --git a/webrtc/p2p/base/dtlstransport.h b/webrtc/p2p/base/dtlstransport.h
index 2ff2ea5..a4bf383 100644
--- a/webrtc/p2p/base/dtlstransport.h
+++ b/webrtc/p2p/base/dtlstransport.h
@@ -66,27 +66,11 @@
     rtc::SSLFingerprint* local_fp =
         Base::local_description()->identity_fingerprint.get();
 
-    if (local_fp) {
-      // Sanity check local fingerprint.
-      if (certificate_) {
-        std::unique_ptr<rtc::SSLFingerprint> local_fp_tmp(
-            rtc::SSLFingerprint::Create(local_fp->algorithm,
-                                        certificate_->identity()));
-        ASSERT(local_fp_tmp.get() != NULL);
-        if (!(*local_fp_tmp == *local_fp)) {
-          std::ostringstream desc;
-          desc << "Local fingerprint does not match identity. Expected: ";
-          desc << local_fp_tmp->ToString();
-          desc << " Got: " << local_fp->ToString();
-          return BadTransportDescription(desc.str(), error_desc);
-        }
-      } else {
-        return BadTransportDescription(
-            "Local fingerprint provided but no identity available.",
-            error_desc);
-      }
-    } else {
+    if (!local_fp) {
       certificate_ = nullptr;
+    } else if (!Base::VerifyCertificateFingerprint(certificate_.get(), local_fp,
+                                                   error_desc)) {
+      return false;
     }
 
     if (!channel->SetLocalCertificate(certificate_)) {
@@ -105,96 +89,23 @@
                               "transport descriptions are negotiated";
       return BadTransportDescription(msg, error_desc);
     }
-
     rtc::SSLFingerprint* local_fp =
         Base::local_description()->identity_fingerprint.get();
     rtc::SSLFingerprint* remote_fp =
         Base::remote_description()->identity_fingerprint.get();
-
     if (remote_fp && local_fp) {
       remote_fingerprint_.reset(new rtc::SSLFingerprint(*remote_fp));
-
-      // From RFC 4145, section-4.1, The following are the values that the
-      // 'setup' attribute can take in an offer/answer exchange:
-      //       Offer      Answer
-      //      ________________
-      //      active     passive / holdconn
-      //      passive    active / holdconn
-      //      actpass    active / passive / holdconn
-      //      holdconn   holdconn
-      //
-      // Set the role that is most conformant with RFC 5763, Section 5, bullet 1
-      // The endpoint MUST use the setup attribute defined in [RFC4145].
-      // The endpoint that is the offerer MUST use the setup attribute
-      // value of setup:actpass and be prepared to receive a client_hello
-      // before it receives the answer.  The answerer MUST use either a
-      // setup attribute value of setup:active or setup:passive.  Note that
-      // if the answerer uses setup:passive, then the DTLS handshake will
-      // not begin until the answerer is received, which adds additional
-      // latency. setup:active allows the answer and the DTLS handshake to
-      // occur in parallel.  Thus, setup:active is RECOMMENDED.  Whichever
-      // party is active MUST initiate a DTLS handshake by sending a
-      // ClientHello over each flow (host/port quartet).
-      // IOW - actpass and passive modes should be treated as server and
-      // active as client.
-      ConnectionRole local_connection_role =
-          Base::local_description()->connection_role;
-      ConnectionRole remote_connection_role =
-          Base::remote_description()->connection_role;
-
-      bool is_remote_server = false;
-      if (local_role == CA_OFFER) {
-        if (local_connection_role != CONNECTIONROLE_ACTPASS) {
-          return BadTransportDescription(
-              "Offerer must use actpass value for setup attribute.",
-              error_desc);
-        }
-
-        if (remote_connection_role == CONNECTIONROLE_ACTIVE ||
-            remote_connection_role == CONNECTIONROLE_PASSIVE ||
-            remote_connection_role == CONNECTIONROLE_NONE) {
-          is_remote_server = (remote_connection_role == CONNECTIONROLE_PASSIVE);
-        } else {
-          const std::string msg =
-              "Answerer must use either active or passive value "
-              "for setup attribute.";
-          return BadTransportDescription(msg, error_desc);
-        }
-        // If remote is NONE or ACTIVE it will act as client.
-      } else {
-        if (remote_connection_role != CONNECTIONROLE_ACTPASS &&
-            remote_connection_role != CONNECTIONROLE_NONE) {
-          return BadTransportDescription(
-              "Offerer must use actpass value for setup attribute.",
-              error_desc);
-        }
-
-        if (local_connection_role == CONNECTIONROLE_ACTIVE ||
-            local_connection_role == CONNECTIONROLE_PASSIVE) {
-          is_remote_server = (local_connection_role == CONNECTIONROLE_ACTIVE);
-        } else {
-          const std::string msg =
-              "Answerer must use either active or passive value "
-              "for setup attribute.";
-          return BadTransportDescription(msg, error_desc);
-        }
-
-        // If local is passive, local will act as server.
+      if (!Base::NegotiateRole(local_role, &secure_role_, error_desc)) {
+        return false;
       }
-
-      secure_role_ = is_remote_server ? rtc::SSL_CLIENT :
-                                        rtc::SSL_SERVER;
-
     } else if (local_fp && (local_role == CA_ANSWER)) {
       return BadTransportDescription(
           "Local fingerprint supplied when caller didn't offer DTLS.",
           error_desc);
     } else {
       // We are not doing DTLS
-      remote_fingerprint_.reset(new rtc::SSLFingerprint(
-          "", NULL, 0));
+      remote_fingerprint_.reset(new rtc::SSLFingerprint("", nullptr, 0));
     }
-
     // Now run the negotiation for the base class.
     return Base::NegotiateTransportDescription(local_role, error_desc);
   }
diff --git a/webrtc/p2p/base/faketransportcontroller.h b/webrtc/p2p/base/faketransportcontroller.h
index e2bdc10..25e5e81 100644
--- a/webrtc/p2p/base/faketransportcontroller.h
+++ b/webrtc/p2p/base/faketransportcontroller.h
@@ -406,6 +406,8 @@
 
   using Transport::local_description;
   using Transport::remote_description;
+  using Transport::VerifyCertificateFingerprint;
+  using Transport::NegotiateRole;
 
  protected:
   TransportChannelImpl* CreateTransportChannel(int component) override {
diff --git a/webrtc/p2p/base/transport.cc b/webrtc/p2p/base/transport.cc
index 89f05e8..3e45d20 100644
--- a/webrtc/p2p/base/transport.cc
+++ b/webrtc/p2p/base/transport.cc
@@ -401,4 +401,107 @@
   return true;
 }
 
+bool Transport::VerifyCertificateFingerprint(
+    const rtc::RTCCertificate* certificate,
+    const rtc::SSLFingerprint* fingerprint,
+    std::string* error_desc) const {
+  if (!fingerprint) {
+    return BadTransportDescription("No fingerprint.", error_desc);
+  }
+  if (!certificate) {
+    return BadTransportDescription(
+        "Fingerprint provided but no identity available.", error_desc);
+  }
+  rtc::scoped_ptr<rtc::SSLFingerprint> fp_tmp(rtc::SSLFingerprint::Create(
+      fingerprint->algorithm, certificate->identity()));
+  ASSERT(fp_tmp.get() != NULL);
+  if (*fp_tmp == *fingerprint) {
+    return true;
+  }
+  std::ostringstream desc;
+  desc << "Local fingerprint does not match identity. Expected: ";
+  desc << fp_tmp->ToString();
+  desc << " Got: " << fingerprint->ToString();
+  return BadTransportDescription(desc.str(), error_desc);
+}
+
+bool Transport::NegotiateRole(ContentAction local_role,
+                              rtc::SSLRole* ssl_role,
+                              std::string* error_desc) const {
+  RTC_DCHECK(ssl_role);
+  if (!local_description() || !remote_description()) {
+    const std::string msg =
+        "Local and Remote description must be set before "
+        "transport descriptions are negotiated";
+    return BadTransportDescription(msg, error_desc);
+  }
+
+  // From RFC 4145, section-4.1, The following are the values that the
+  // 'setup' attribute can take in an offer/answer exchange:
+  //       Offer      Answer
+  //      ________________
+  //      active     passive / holdconn
+  //      passive    active / holdconn
+  //      actpass    active / passive / holdconn
+  //      holdconn   holdconn
+  //
+  // Set the role that is most conformant with RFC 5763, Section 5, bullet 1
+  // The endpoint MUST use the setup attribute defined in [RFC4145].
+  // The endpoint that is the offerer MUST use the setup attribute
+  // value of setup:actpass and be prepared to receive a client_hello
+  // before it receives the answer.  The answerer MUST use either a
+  // setup attribute value of setup:active or setup:passive.  Note that
+  // if the answerer uses setup:passive, then the DTLS handshake will
+  // not begin until the answerer is received, which adds additional
+  // latency. setup:active allows the answer and the DTLS handshake to
+  // occur in parallel.  Thus, setup:active is RECOMMENDED.  Whichever
+  // party is active MUST initiate a DTLS handshake by sending a
+  // ClientHello over each flow (host/port quartet).
+  // IOW - actpass and passive modes should be treated as server and
+  // active as client.
+  ConnectionRole local_connection_role = local_description()->connection_role;
+  ConnectionRole remote_connection_role = remote_description()->connection_role;
+
+  bool is_remote_server = false;
+  if (local_role == CA_OFFER) {
+    if (local_connection_role != CONNECTIONROLE_ACTPASS) {
+      return BadTransportDescription(
+          "Offerer must use actpass value for setup attribute.", error_desc);
+    }
+
+    if (remote_connection_role == CONNECTIONROLE_ACTIVE ||
+        remote_connection_role == CONNECTIONROLE_PASSIVE ||
+        remote_connection_role == CONNECTIONROLE_NONE) {
+      is_remote_server = (remote_connection_role == CONNECTIONROLE_PASSIVE);
+    } else {
+      const std::string msg =
+          "Answerer must use either active or passive value "
+          "for setup attribute.";
+      return BadTransportDescription(msg, error_desc);
+    }
+    // If remote is NONE or ACTIVE it will act as client.
+  } else {
+    if (remote_connection_role != CONNECTIONROLE_ACTPASS &&
+        remote_connection_role != CONNECTIONROLE_NONE) {
+      return BadTransportDescription(
+          "Offerer must use actpass value for setup attribute.", error_desc);
+    }
+
+    if (local_connection_role == CONNECTIONROLE_ACTIVE ||
+        local_connection_role == CONNECTIONROLE_PASSIVE) {
+      is_remote_server = (local_connection_role == CONNECTIONROLE_ACTIVE);
+    } else {
+      const std::string msg =
+          "Answerer must use either active or passive value "
+          "for setup attribute.";
+      return BadTransportDescription(msg, error_desc);
+    }
+
+    // If local is passive, local will act as server.
+  }
+
+  *ssl_role = is_remote_server ? rtc::SSL_CLIENT : rtc::SSL_SERVER;
+  return true;
+}
+
 }  // namespace cricket
diff --git a/webrtc/p2p/base/transport.h b/webrtc/p2p/base/transport.h
index 46360cc..e31d37a 100644
--- a/webrtc/p2p/base/transport.h
+++ b/webrtc/p2p/base/transport.h
@@ -315,6 +315,20 @@
       TransportChannelImpl* channel,
       std::string* error_desc);
 
+  // Returns false if the certificate's identity does not match the fingerprint,
+  // or either is NULL.
+  virtual bool VerifyCertificateFingerprint(
+      const rtc::RTCCertificate* certificate,
+      const rtc::SSLFingerprint* fingerprint,
+      std::string* error_desc) const;
+
+  // Negotiates the SSL role based off the offer and answer as specified by
+  // RFC 4145, section-4.1. Returns false if the SSL role cannot be determined
+  // from the local description and remote description.
+  virtual bool NegotiateRole(ContentAction local_role,
+                             rtc::SSLRole* ssl_role,
+                             std::string* error_desc) const;
+
  private:
   // If a candidate is not acceptable, returns false and sets error.
   // Call this before calling OnRemoteCandidates.
diff --git a/webrtc/p2p/base/transport_unittest.cc b/webrtc/p2p/base/transport_unittest.cc
index 658c143..fba72b4 100644
--- a/webrtc/p2p/base/transport_unittest.cc
+++ b/webrtc/p2p/base/transport_unittest.cc
@@ -235,3 +235,180 @@
   EXPECT_EQ(1, stats.channel_stats[0].component);
 }
 
+// Tests that VerifyCertificateFingerprint only returns true when the
+// certificate matches the fingerprint.
+TEST_F(TransportTest, TestVerifyCertificateFingerprint) {
+  std::string error_desc;
+  EXPECT_FALSE(
+      transport_->VerifyCertificateFingerprint(nullptr, nullptr, &error_desc));
+  rtc::KeyType key_types[] = {rtc::KT_RSA, rtc::KT_ECDSA};
+
+  for (auto& key_type : key_types) {
+    rtc::scoped_refptr<rtc::RTCCertificate> certificate =
+        rtc::RTCCertificate::Create(rtc::scoped_ptr<rtc::SSLIdentity>(
+            rtc::SSLIdentity::Generate("testing", key_type)));
+    ASSERT_NE(nullptr, certificate);
+
+    std::string digest_algorithm;
+    ASSERT_TRUE(certificate->ssl_certificate().GetSignatureDigestAlgorithm(
+        &digest_algorithm));
+    ASSERT_FALSE(digest_algorithm.empty());
+    rtc::scoped_ptr<rtc::SSLFingerprint> good_fingerprint(
+        rtc::SSLFingerprint::Create(digest_algorithm, certificate->identity()));
+    ASSERT_NE(nullptr, good_fingerprint);
+
+    EXPECT_TRUE(transport_->VerifyCertificateFingerprint(
+        certificate.get(), good_fingerprint.get(), &error_desc));
+    EXPECT_FALSE(transport_->VerifyCertificateFingerprint(
+        certificate.get(), nullptr, &error_desc));
+    EXPECT_FALSE(transport_->VerifyCertificateFingerprint(
+        nullptr, good_fingerprint.get(), &error_desc));
+
+    rtc::SSLFingerprint bad_fingerprint = *good_fingerprint;
+    bad_fingerprint.digest.AppendData("0", 1);
+    EXPECT_FALSE(transport_->VerifyCertificateFingerprint(
+        certificate.get(), &bad_fingerprint, &error_desc));
+  }
+}
+
+// Tests that NegotiateRole sets the SSL role correctly.
+TEST_F(TransportTest, TestNegotiateRole) {
+  TransportDescription local_desc(kIceUfrag1, kIcePwd1);
+  TransportDescription remote_desc(kIceUfrag2, kIcePwd2);
+
+  struct NegotiateRoleParams {
+    cricket::ConnectionRole local_role;
+    cricket::ConnectionRole remote_role;
+    cricket::ContentAction local_action;
+    cricket::ContentAction remote_action;
+  };
+
+  rtc::SSLRole ssl_role;
+  std::string error_desc;
+
+  // Parameters which set the SSL role to SSL_CLIENT.
+  NegotiateRoleParams valid_client_params[] = {
+      {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_ACTPASS,
+       cricket::CA_ANSWER, cricket::CA_OFFER},
+      {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_ACTPASS,
+       cricket::CA_PRANSWER, cricket::CA_OFFER},
+      {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_PASSIVE,
+       cricket::CA_OFFER, cricket::CA_ANSWER},
+      {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_PASSIVE,
+       cricket::CA_OFFER, cricket::CA_PRANSWER}};
+
+  for (auto& param : valid_client_params) {
+    local_desc.connection_role = param.local_role;
+    remote_desc.connection_role = param.remote_role;
+
+    ASSERT_TRUE(transport_->SetRemoteTransportDescription(
+        remote_desc, param.remote_action, nullptr));
+    ASSERT_TRUE(transport_->SetLocalTransportDescription(
+        local_desc, param.local_action, nullptr));
+    EXPECT_TRUE(
+        transport_->NegotiateRole(param.local_action, &ssl_role, &error_desc));
+    EXPECT_EQ(rtc::SSL_CLIENT, ssl_role);
+  }
+
+  // Parameters which set the SSL role to SSL_SERVER.
+  NegotiateRoleParams valid_server_params[] = {
+      {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTPASS,
+       cricket::CA_ANSWER, cricket::CA_OFFER},
+      {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTPASS,
+       cricket::CA_PRANSWER, cricket::CA_OFFER},
+      {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTIVE,
+       cricket::CA_OFFER, cricket::CA_ANSWER},
+      {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTIVE,
+       cricket::CA_OFFER, cricket::CA_PRANSWER}};
+
+  for (auto& param : valid_server_params) {
+    local_desc.connection_role = param.local_role;
+    remote_desc.connection_role = param.remote_role;
+
+    ASSERT_TRUE(transport_->SetRemoteTransportDescription(
+        remote_desc, param.remote_action, nullptr));
+    ASSERT_TRUE(transport_->SetLocalTransportDescription(
+        local_desc, param.local_action, nullptr));
+    EXPECT_TRUE(
+        transport_->NegotiateRole(param.local_action, &ssl_role, &error_desc));
+    EXPECT_EQ(rtc::SSL_SERVER, ssl_role);
+  }
+
+  // Invalid parameters due to both peers having a duplicate role.
+  NegotiateRoleParams duplicate_params[] = {
+      {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_ACTIVE,
+       cricket::CA_ANSWER, cricket::CA_OFFER},
+      {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTPASS,
+       cricket::CA_ANSWER, cricket::CA_OFFER},
+      {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_PASSIVE,
+       cricket::CA_ANSWER, cricket::CA_OFFER},
+      {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_ACTIVE,
+       cricket::CA_PRANSWER, cricket::CA_OFFER},
+      {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTPASS,
+       cricket::CA_PRANSWER, cricket::CA_OFFER},
+      {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_PASSIVE,
+       cricket::CA_PRANSWER, cricket::CA_OFFER},
+      {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_ACTIVE,
+       cricket::CA_OFFER, cricket::CA_ANSWER},
+      {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTPASS,
+       cricket::CA_OFFER, cricket::CA_ANSWER},
+      {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_PASSIVE,
+       cricket::CA_OFFER, cricket::CA_ANSWER},
+      {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_ACTIVE,
+       cricket::CA_OFFER, cricket::CA_PRANSWER},
+      {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_ACTPASS,
+       cricket::CA_OFFER, cricket::CA_PRANSWER},
+      {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_PASSIVE,
+       cricket::CA_OFFER, cricket::CA_PRANSWER}};
+
+  for (auto& param : duplicate_params) {
+    local_desc.connection_role = param.local_role;
+    remote_desc.connection_role = param.remote_role;
+
+    ASSERT_TRUE(transport_->SetRemoteTransportDescription(
+        remote_desc, param.remote_action, nullptr));
+    ASSERT_TRUE(transport_->SetLocalTransportDescription(
+        local_desc, param.local_action, nullptr));
+    EXPECT_FALSE(
+        transport_->NegotiateRole(param.local_action, &ssl_role, &error_desc));
+  }
+
+  // Invalid parameters due to the offerer not using ACTPASS.
+  NegotiateRoleParams offerer_without_actpass_params[] = {
+      {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_PASSIVE,
+       cricket::CA_ANSWER, cricket::CA_OFFER},
+      {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTIVE,
+       cricket::CA_ANSWER, cricket::CA_OFFER},
+      {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_PASSIVE,
+       cricket::CA_ANSWER, cricket::CA_OFFER},
+      {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_PASSIVE,
+       cricket::CA_PRANSWER, cricket::CA_OFFER},
+      {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTIVE,
+       cricket::CA_PRANSWER, cricket::CA_OFFER},
+      {cricket::CONNECTIONROLE_ACTPASS, cricket::CONNECTIONROLE_PASSIVE,
+       cricket::CA_PRANSWER, cricket::CA_OFFER},
+      {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_PASSIVE,
+       cricket::CA_OFFER, cricket::CA_ANSWER},
+      {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTIVE,
+       cricket::CA_OFFER, cricket::CA_ANSWER},
+      {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTPASS,
+       cricket::CA_OFFER, cricket::CA_ANSWER},
+      {cricket::CONNECTIONROLE_ACTIVE, cricket::CONNECTIONROLE_PASSIVE,
+       cricket::CA_OFFER, cricket::CA_PRANSWER},
+      {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTIVE,
+       cricket::CA_OFFER, cricket::CA_PRANSWER},
+      {cricket::CONNECTIONROLE_PASSIVE, cricket::CONNECTIONROLE_ACTPASS,
+       cricket::CA_OFFER, cricket::CA_PRANSWER}};
+
+  for (auto& param : offerer_without_actpass_params) {
+    local_desc.connection_role = param.local_role;
+    remote_desc.connection_role = param.remote_role;
+
+    ASSERT_TRUE(transport_->SetRemoteTransportDescription(
+        remote_desc, param.remote_action, nullptr));
+    ASSERT_TRUE(transport_->SetLocalTransportDescription(
+        local_desc, param.local_action, nullptr));
+    EXPECT_FALSE(
+        transport_->NegotiateRole(param.local_action, &ssl_role, &error_desc));
+  }
+}
diff --git a/webrtc/p2p/base/transportcontroller.cc b/webrtc/p2p/base/transportcontroller.cc
index 24f6481..2eb198e 100644
--- a/webrtc/p2p/base/transportcontroller.cc
+++ b/webrtc/p2p/base/transportcontroller.cc
@@ -20,6 +20,10 @@
 #include "webrtc/p2p/base/p2ptransport.h"
 #include "webrtc/p2p/base/port.h"
 
+#ifdef HAVE_QUIC
+#include "webrtc/p2p/quic/quictransport.h"
+#endif  // HAVE_QUIC
+
 namespace cricket {
 
 enum {
@@ -219,6 +223,11 @@
     const std::string& transport_name) {
   RTC_DCHECK(worker_thread_->IsCurrent());
 
+#ifdef HAVE_QUIC
+  if (quic_) {
+    return new QuicTransport(transport_name, port_allocator(), certificate_);
+  }
+#endif  // HAVE_QUIC
   Transport* transport = new DtlsTransport<P2PTransport>(
       transport_name, port_allocator(), certificate_);
   return transport;
diff --git a/webrtc/p2p/base/transportcontroller.h b/webrtc/p2p/base/transportcontroller.h
index 8cb5986..f84c551 100644
--- a/webrtc/p2p/base/transportcontroller.h
+++ b/webrtc/p2p/base/transportcontroller.h
@@ -91,6 +91,9 @@
   virtual void DestroyTransportChannel_w(const std::string& transport_name,
                                          int component);
 
+  void use_quic() { quic_ = true; }
+  bool quic() const { return quic_; }
+
   // All of these signals are fired on the signalling thread.
 
   // If any transport failed => failed,
@@ -222,6 +225,8 @@
   uint64_t ice_tiebreaker_ = rtc::CreateRandomId64();
   rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
   rtc::AsyncInvoker invoker_;
+  // True if QUIC is used instead of DTLS.
+  bool quic_ = false;
 };
 
 }  // namespace cricket
diff --git a/webrtc/p2p/p2p.gyp b/webrtc/p2p/p2p.gyp
index 5c9a575..50941c8 100644
--- a/webrtc/p2p/p2p.gyp
+++ b/webrtc/p2p/p2p.gyp
@@ -107,6 +107,8 @@
             'quic/quicconnectionhelper.h',
             'quic/quicsession.cc',
             'quic/quicsession.h',
+            'quic/quictransport.cc',
+            'quic/quictransport.h',
             'quic/quictransportchannel.cc',
             'quic/quictransportchannel.h',
             'quic/reliablequicstream.cc',
@@ -182,6 +184,7 @@
                 'sources': [
                   'quic/quicconnectionhelper_unittest.cc',
                   'quic/quicsession_unittest.cc',
+		  'quic/quictransport_unittest.cc',
                   'quic/quictransportchannel_unittest.cc',
                   'quic/reliablequicstream_unittest.cc',
                 ],
diff --git a/webrtc/p2p/quic/quictransport.cc b/webrtc/p2p/quic/quictransport.cc
new file mode 100644
index 0000000..51f9a2b
--- /dev/null
+++ b/webrtc/p2p/quic/quictransport.cc
@@ -0,0 +1,114 @@
+/*
+ *  Copyright 2016 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 "webrtc/p2p/quic/quictransport.h"
+
+#include "webrtc/p2p/base/p2ptransportchannel.h"
+
+namespace cricket {
+
+QuicTransport::QuicTransport(
+    const std::string& name,
+    PortAllocator* allocator,
+    const rtc::scoped_refptr<rtc::RTCCertificate>& certificate)
+    : Transport(name, allocator), local_certificate_(certificate) {}
+
+QuicTransport::~QuicTransport() {
+  DestroyAllChannels();
+}
+
+void QuicTransport::SetLocalCertificate(
+    const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
+  local_certificate_ = certificate;
+}
+bool QuicTransport::GetLocalCertificate(
+    rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
+  if (!local_certificate_) {
+    return false;
+  }
+  *certificate = local_certificate_;
+  return true;
+}
+
+bool QuicTransport::ApplyLocalTransportDescription(
+    TransportChannelImpl* channel,
+    std::string* error_desc) {
+  rtc::SSLFingerprint* local_fp =
+      local_description()->identity_fingerprint.get();
+  if (!VerifyCertificateFingerprint(local_certificate_.get(), local_fp,
+                                    error_desc)) {
+    return false;
+  }
+  if (!channel->SetLocalCertificate(local_certificate_)) {
+    return BadTransportDescription("Failed to set local identity.", error_desc);
+  }
+  return Transport::ApplyLocalTransportDescription(channel, error_desc);
+}
+
+bool QuicTransport::NegotiateTransportDescription(ContentAction action,
+                                                  std::string* error_desc) {
+  if (!local_description() || !remote_description()) {
+    const std::string msg =
+        "Local and Remote description must be set before "
+        "transport descriptions are negotiated";
+    return BadTransportDescription(msg, error_desc);
+  }
+  rtc::SSLFingerprint* local_fp =
+      local_description()->identity_fingerprint.get();
+  rtc::SSLFingerprint* remote_fp =
+      remote_description()->identity_fingerprint.get();
+  if (!local_fp || !remote_fp) {
+    return BadTransportDescription("Fingerprints must be supplied for QUIC.",
+                                   error_desc);
+  }
+  remote_fingerprint_.reset(new rtc::SSLFingerprint(*remote_fp));
+  if (!NegotiateRole(action, &local_role_, error_desc)) {
+    return false;
+  }
+  // Now run the negotiation for the Transport class.
+  return Transport::NegotiateTransportDescription(action, error_desc);
+}
+
+QuicTransportChannel* QuicTransport::CreateTransportChannel(int component) {
+  P2PTransportChannel* ice_channel =
+      new P2PTransportChannel(name(), component, port_allocator());
+  return new QuicTransportChannel(ice_channel);
+}
+
+void QuicTransport::DestroyTransportChannel(TransportChannelImpl* channel) {
+  delete channel;
+}
+
+bool QuicTransport::GetSslRole(rtc::SSLRole* ssl_role) const {
+  ASSERT(ssl_role != NULL);
+  *ssl_role = local_role_;
+  return true;
+}
+
+bool QuicTransport::ApplyNegotiatedTransportDescription(
+    TransportChannelImpl* channel,
+    std::string* error_desc) {
+  // Set ssl role and remote fingerprint. These are required for QUIC setup.
+  if (!channel->SetSslRole(local_role_)) {
+    return BadTransportDescription("Failed to set ssl role for the channel.",
+                                   error_desc);
+  }
+  // Apply remote fingerprint.
+  if (!channel->SetRemoteFingerprint(
+          remote_fingerprint_->algorithm,
+          reinterpret_cast<const uint8_t*>(remote_fingerprint_->digest.data()),
+          remote_fingerprint_->digest.size())) {
+    return BadTransportDescription("Failed to apply remote fingerprint.",
+                                   error_desc);
+  }
+  return Transport::ApplyNegotiatedTransportDescription(channel, error_desc);
+}
+
+}  // namespace cricket
diff --git a/webrtc/p2p/quic/quictransport.h b/webrtc/p2p/quic/quictransport.h
new file mode 100644
index 0000000..053ba61
--- /dev/null
+++ b/webrtc/p2p/quic/quictransport.h
@@ -0,0 +1,63 @@
+/*
+ *  Copyright 2016 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 WEBRTC_P2P_QUIC_QUICTRANSPORT_H_
+#define WEBRTC_P2P_QUIC_QUICTRANSPORT_H_
+
+#include <string>
+#include <map>
+
+#include "webrtc/p2p/base/transport.h"
+#include "webrtc/p2p/quic/quictransportchannel.h"
+
+namespace cricket {
+
+class P2PTransportChannel;
+class PortAllocator;
+
+// TODO(mikescarlett): Refactor to avoid code duplication with DtlsTransport.
+class QuicTransport : public Transport {
+ public:
+  QuicTransport(const std::string& name,
+                PortAllocator* allocator,
+                const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
+
+  ~QuicTransport() override;
+
+  // Transport overrides.
+  void SetLocalCertificate(
+      const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override;
+  bool GetLocalCertificate(
+      rtc::scoped_refptr<rtc::RTCCertificate>* certificate) override;
+  bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) override {
+    return true;  // Not needed by QUIC
+  }
+  bool GetSslRole(rtc::SSLRole* ssl_role) const override;
+
+ protected:
+  // Transport overrides.
+  QuicTransportChannel* CreateTransportChannel(int component) override;
+  void DestroyTransportChannel(TransportChannelImpl* channel) override;
+  bool ApplyLocalTransportDescription(TransportChannelImpl* channel,
+                                      std::string* error_desc) override;
+  bool NegotiateTransportDescription(ContentAction action,
+                                     std::string* error_desc) override;
+  bool ApplyNegotiatedTransportDescription(TransportChannelImpl* channel,
+                                           std::string* error_desc) override;
+
+ private:
+  rtc::scoped_refptr<rtc::RTCCertificate> local_certificate_;
+  rtc::SSLRole local_role_ = rtc::SSL_CLIENT;
+  rtc::scoped_ptr<rtc::SSLFingerprint> remote_fingerprint_;
+};
+
+}  // namespace cricket
+
+#endif  // WEBRTC_P2P_QUIC_QUICTRANSPORT_H_
diff --git a/webrtc/p2p/quic/quictransport_unittest.cc b/webrtc/p2p/quic/quictransport_unittest.cc
new file mode 100644
index 0000000..bc99f6b
--- /dev/null
+++ b/webrtc/p2p/quic/quictransport_unittest.cc
@@ -0,0 +1,159 @@
+/*
+ *  Copyright 2016 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 "webrtc/p2p/quic/quictransport.h"
+
+#include <string>
+#include <vector>
+
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/rtccertificate.h"
+#include "webrtc/base/sslidentity.h"
+
+using cricket::TransportChannelImpl;
+using cricket::QuicTransport;
+using cricket::Transport;
+using cricket::TransportDescription;
+
+static const char kIceUfrag1[] = "TESTICEUFRAG0001";
+static const char kIcePwd1[] = "TESTICEPWD00000000000001";
+
+static const char kIceUfrag2[] = "TESTICEUFRAG0002";
+static const char kIcePwd2[] = "TESTICEPWD00000000000002";
+
+static rtc::scoped_refptr<rtc::RTCCertificate> CreateCertificate(
+    std::string name) {
+  return rtc::RTCCertificate::Create(rtc::scoped_ptr<rtc::SSLIdentity>(
+      rtc::SSLIdentity::Generate(name, rtc::KT_DEFAULT)));
+}
+
+static rtc::scoped_ptr<rtc::SSLFingerprint> CreateFingerprint(
+    rtc::RTCCertificate* cert) {
+  std::string digest_algorithm;
+  cert->ssl_certificate().GetSignatureDigestAlgorithm(&digest_algorithm);
+  return rtc::scoped_ptr<rtc::SSLFingerprint>(
+      rtc::SSLFingerprint::Create(digest_algorithm, cert->identity()));
+}
+
+class QuicTransportTest : public testing::Test {
+ public:
+  QuicTransportTest() : transport_("testing", nullptr, nullptr) {}
+
+  void SetTransportDescription(cricket::ConnectionRole local_role,
+                               cricket::ConnectionRole remote_role,
+                               cricket::ContentAction local_action,
+                               cricket::ContentAction remote_action,
+                               rtc::SSLRole expected_ssl_role) {
+    TransportChannelImpl* channel = transport_.CreateChannel(1);
+    ASSERT_NE(nullptr, channel);
+
+    rtc::scoped_refptr<rtc::RTCCertificate> local_certificate(
+        CreateCertificate("local"));
+    ASSERT_NE(nullptr, local_certificate);
+    transport_.SetLocalCertificate(local_certificate);
+
+    rtc::scoped_ptr<rtc::SSLFingerprint> local_fingerprint =
+        CreateFingerprint(local_certificate.get());
+    ASSERT_NE(nullptr, local_fingerprint);
+    TransportDescription local_desc(std::vector<std::string>(), kIceUfrag1,
+                                    kIcePwd1, cricket::ICEMODE_FULL, local_role,
+                                    local_fingerprint.get());
+    ASSERT_TRUE(transport_.SetLocalTransportDescription(local_desc,
+                                                        local_action, nullptr));
+    // The certificate is applied to QuicTransportChannel when the local
+    // description is set.
+    rtc::scoped_refptr<rtc::RTCCertificate> channel_local_certificate =
+        channel->GetLocalCertificate();
+    ASSERT_NE(nullptr, channel_local_certificate);
+    EXPECT_EQ(local_certificate, channel_local_certificate);
+    rtc::scoped_ptr<rtc::SSLFingerprint> remote_fingerprint =
+        CreateFingerprint(CreateCertificate("remote").get());
+    // NegotiateTransportDescription was not called yet. The SSL role should
+    // not be set and neither should the remote fingerprint.
+    rtc::scoped_ptr<rtc::SSLRole> role(new rtc::SSLRole());
+    EXPECT_FALSE(channel->GetSslRole(role.get()));
+    // Setting the remote description should set the SSL role.
+    ASSERT_NE(nullptr, remote_fingerprint);
+    TransportDescription remote_desc(std::vector<std::string>(), kIceUfrag2,
+                                     kIcePwd2, cricket::ICEMODE_FULL,
+                                     remote_role, remote_fingerprint.get());
+    ASSERT_TRUE(transport_.SetRemoteTransportDescription(
+        remote_desc, remote_action, nullptr));
+    ASSERT_TRUE(channel->GetSslRole(role.get()));
+    // SSL role should be client because the remote description is an ANSWER.
+    EXPECT_EQ(expected_ssl_role, *role);
+  }
+
+ protected:
+  QuicTransport transport_;
+};
+
+// Test setting the local certificate.
+TEST_F(QuicTransportTest, SetLocalCertificate) {
+  rtc::scoped_refptr<rtc::RTCCertificate> local_certificate(
+      CreateCertificate("local"));
+  ASSERT_NE(nullptr, local_certificate);
+  rtc::scoped_refptr<rtc::RTCCertificate> transport_local_certificate;
+  EXPECT_FALSE(transport_.GetLocalCertificate(&transport_local_certificate));
+  transport_.SetLocalCertificate(local_certificate);
+  ASSERT_TRUE(transport_.GetLocalCertificate(&transport_local_certificate));
+  ASSERT_NE(nullptr, transport_local_certificate);
+  EXPECT_EQ(local_certificate, transport_local_certificate);
+}
+
+// Test setting the ICE role.
+TEST_F(QuicTransportTest, SetIceRole) {
+  TransportChannelImpl* channel1 = transport_.CreateChannel(1);
+  ASSERT_NE(nullptr, channel1);
+  transport_.SetIceRole(cricket::ICEROLE_CONTROLLING);
+  EXPECT_EQ(cricket::ICEROLE_CONTROLLING, transport_.ice_role());
+  TransportChannelImpl* channel2 = transport_.CreateChannel(2);
+  ASSERT_NE(nullptr, channel2);
+  EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel1->GetIceRole());
+  EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel2->GetIceRole());
+}
+
+// Test setting the ICE tie breaker.
+TEST_F(QuicTransportTest, SetIceTiebreaker) {
+  transport_.SetIceTiebreaker(1u);
+  EXPECT_EQ(1u, transport_.IceTiebreaker());
+}
+
+// Test setting the local and remote descriptions for a SSL client.
+TEST_F(QuicTransportTest, SetLocalAndRemoteTransportDescriptionClient) {
+  SetTransportDescription(cricket::CONNECTIONROLE_ACTPASS,
+                          cricket::CONNECTIONROLE_PASSIVE, cricket::CA_OFFER,
+                          cricket::CA_ANSWER, rtc::SSL_CLIENT);
+}
+
+// Test setting the local and remote descriptions for a SSL server.
+TEST_F(QuicTransportTest, SetLocalAndRemoteTransportDescriptionServer) {
+  SetTransportDescription(cricket::CONNECTIONROLE_ACTPASS,
+                          cricket::CONNECTIONROLE_ACTIVE, cricket::CA_OFFER,
+                          cricket::CA_ANSWER, rtc::SSL_SERVER);
+}
+
+// Test creation and destruction of channels.
+TEST_F(QuicTransportTest, CreateAndDestroyChannels) {
+  TransportChannelImpl* channel1 = transport_.CreateChannel(1);
+  ASSERT_NE(nullptr, channel1);
+  EXPECT_TRUE(transport_.HasChannel(1));
+  EXPECT_EQ(channel1, transport_.GetChannel(1));
+  TransportChannelImpl* channel2 = transport_.CreateChannel(2);
+  ASSERT_NE(nullptr, channel2);
+  EXPECT_TRUE(transport_.HasChannel(2));
+  EXPECT_EQ(channel2, transport_.GetChannel(2));
+  transport_.DestroyChannel(1);
+  EXPECT_FALSE(transport_.HasChannel(1));
+  EXPECT_EQ(nullptr, transport_.GetChannel(1));
+  transport_.DestroyChannel(2);
+  EXPECT_FALSE(transport_.HasChannel(2));
+  EXPECT_EQ(nullptr, transport_.GetChannel(2));
+}
diff --git a/webrtc/p2p/quic/quictransportchannel.cc b/webrtc/p2p/quic/quictransportchannel.cc
index b3f9180..968faee 100644
--- a/webrtc/p2p/quic/quictransportchannel.cc
+++ b/webrtc/p2p/quic/quictransportchannel.cc
@@ -274,7 +274,7 @@
 //       |channel_| again.
 void QuicTransportChannel::OnWritableState(TransportChannel* channel) {
   ASSERT(rtc::Thread::Current() == worker_thread_);
-  ASSERT(channel == channel_);
+  ASSERT(channel == channel_.get());
   LOG_J(LS_VERBOSE, this)
       << "QuicTransportChannel: channel writable state changed to "
       << channel_->writable();
@@ -308,7 +308,7 @@
 
 void QuicTransportChannel::OnReceivingState(TransportChannel* channel) {
   ASSERT(rtc::Thread::Current() == worker_thread_);
-  ASSERT(channel == channel_);
+  ASSERT(channel == channel_.get());
   LOG_J(LS_VERBOSE, this)
       << "QuicTransportChannel: channel receiving state changed to "
       << channel_->receiving();
@@ -324,7 +324,7 @@
                                         const rtc::PacketTime& packet_time,
                                         int flags) {
   ASSERT(rtc::Thread::Current() == worker_thread_);
-  ASSERT(channel == channel_);
+  ASSERT(channel == channel_.get());
   ASSERT(flags == 0);
 
   switch (quic_state_) {
@@ -371,24 +371,24 @@
 }
 
 void QuicTransportChannel::OnGatheringState(TransportChannelImpl* channel) {
-  ASSERT(channel == channel_);
+  ASSERT(channel == channel_.get());
   SignalGatheringState(this);
 }
 
 void QuicTransportChannel::OnCandidateGathered(TransportChannelImpl* channel,
                                                const Candidate& c) {
-  ASSERT(channel == channel_);
+  ASSERT(channel == channel_.get());
   SignalCandidateGathered(this, c);
 }
 
 void QuicTransportChannel::OnRoleConflict(TransportChannelImpl* channel) {
-  ASSERT(channel == channel_);
+  ASSERT(channel == channel_.get());
   SignalRoleConflict(this);
 }
 
 void QuicTransportChannel::OnRouteChange(TransportChannel* channel,
                                          const Candidate& candidate) {
-  ASSERT(channel == channel_);
+  ASSERT(channel == channel_.get());
   SignalRouteChange(this, candidate);
 }
 
@@ -396,13 +396,13 @@
     TransportChannel* channel,
     CandidatePairInterface* selected_candidate_pair,
     int last_sent_packet_id) {
-  ASSERT(channel == channel_);
+  ASSERT(channel == channel_.get());
   SignalSelectedCandidatePairChanged(this, selected_candidate_pair,
                                      last_sent_packet_id);
 }
 
 void QuicTransportChannel::OnConnectionRemoved(TransportChannelImpl* channel) {
-  ASSERT(channel == channel_);
+  ASSERT(channel == channel_.get());
   SignalConnectionRemoved(this);
 }
 
diff --git a/webrtc/p2p/quic/quictransportchannel.h b/webrtc/p2p/quic/quictransportchannel.h
index 847af7f..2ce17f8 100644
--- a/webrtc/p2p/quic/quictransportchannel.h
+++ b/webrtc/p2p/quic/quictransportchannel.h
@@ -273,7 +273,7 @@
   rtc::Thread* worker_thread_;
   // Underlying channel which is responsible for connecting with the remote peer
   // and sending/receiving packets across the network.
-  TransportChannelImpl* const channel_;
+  std::unique_ptr<TransportChannelImpl> channel_;
   // Connectivity state of QuicTransportChannel.
   QuicTransportState quic_state_ = QUIC_TRANSPORT_NEW;
   // QUIC session which establishes the crypto handshake and converts data
diff --git a/webrtc/p2p/quic/quictransportchannel_unittest.cc b/webrtc/p2p/quic/quictransportchannel_unittest.cc
index dba07eb..0e16390 100644
--- a/webrtc/p2p/quic/quictransportchannel_unittest.cc
+++ b/webrtc/p2p/quic/quictransportchannel_unittest.cc
@@ -94,15 +94,15 @@
   explicit QuicTestPeer(const std::string& name)
       : name_(name),
         bytes_sent_(0),
-        ice_channel_(name_, 0),
-        quic_channel_(&ice_channel_),
+        ice_channel_(new FailableTransportChannel(name_, 0)),
+        quic_channel_(ice_channel_),
         incoming_stream_count_(0) {
     quic_channel_.SignalReadPacket.connect(
         this, &QuicTestPeer::OnTransportChannelReadPacket);
     quic_channel_.SignalIncomingStream.connect(this,
                                                &QuicTestPeer::OnIncomingStream);
     quic_channel_.SignalClosed.connect(this, &QuicTestPeer::OnClosed);
-    ice_channel_.SetAsync(true);
+    ice_channel_->SetAsync(true);
     rtc::scoped_refptr<rtc::RTCCertificate> local_cert =
         rtc::RTCCertificate::Create(std::unique_ptr<rtc::SSLIdentity>(
             rtc::SSLIdentity::Generate(name_, rtc::KT_DEFAULT)));
@@ -112,13 +112,13 @@
 
   // Connects |ice_channel_| to that of the other peer.
   void Connect(QuicTestPeer* other_peer) {
-    ice_channel_.Connect();
-    other_peer->ice_channel_.Connect();
-    ice_channel_.SetDestination(&other_peer->ice_channel_);
+    ice_channel_->Connect();
+    other_peer->ice_channel_->Connect();
+    ice_channel_->SetDestination(other_peer->ice_channel_);
   }
 
   // Disconnects |ice_channel_|.
-  void Disconnect() { ice_channel_.SetDestination(nullptr); }
+  void Disconnect() { ice_channel_->SetDestination(nullptr); }
 
   // Generates ICE credentials and passes them to |quic_channel_|.
   void SetIceParameters(IceRole local_ice_role,
@@ -189,13 +189,13 @@
 
   void ClearBytesReceived() { bytes_received_ = 0; }
 
-  void SetWriteError(int error) { ice_channel_.SetError(error); }
+  void SetWriteError(int error) { ice_channel_->SetError(error); }
 
   size_t bytes_received() const { return bytes_received_; }
 
   size_t bytes_sent() const { return bytes_sent_; }
 
-  FailableTransportChannel* ice_channel() { return &ice_channel_; }
+  FailableTransportChannel* ice_channel() { return ice_channel_; }
 
   QuicTransportChannel* quic_channel() { return &quic_channel_; }
 
@@ -230,7 +230,7 @@
   std::string name_;                      // Channel name.
   size_t bytes_sent_;                     // Bytes sent by QUIC channel.
   size_t bytes_received_;                 // Bytes received by QUIC channel.
-  FailableTransportChannel ice_channel_;  // Simulates an ICE channel.
+  FailableTransportChannel* ice_channel_;  // Simulates an ICE channel.
   QuicTransportChannel quic_channel_;     // QUIC channel to test.
   std::unique_ptr<rtc::SSLFingerprint> local_fingerprint_;
   ReliableQuicStream* incoming_quic_stream_ = nullptr;