Reland of TransportController refactoring. (patchset #1 id:1 of https://codereview.webrtc.org/1358413003/ )

Reason for revert:
This CL just landed: https://codereview.chromium.org/1323243006/

Which fixes the FYI bots for the original CL, and breaks them for this revert.

Original issue's description:
> Revert of TransportController refactoring. (patchset #6 id:100001 of https://codereview.webrtc.org/1350523003/ )
>
> Reason for revert:
> This CL causes problems with the WebRTC-in-Chromium FYI bots. Presumably it needs to be done in several steps, where removed files are emptied instead of removed in the first step.
>
> Original issue's description:
> > TransportController refactoring.
> >
> > Getting rid of TransportProxy, and in its place adding a
> > TransportController class which will facilitate access to and manage
> > the lifetimes of Transports. These Transports will now be accessed
> > solely from the worker thread, simplifying their implementation.
> >
> > This refactoring also pulls Transport-related code out of BaseSession.
> > Which means that BaseChannels will now rely on the TransportController
> > interface to create channels, rather than BaseSession.
> >
> > Committed: https://crrev.com/47ee2f3b9f33e8938948c482c921d4e13a3acd83
> > Cr-Commit-Position: refs/heads/master@{#10022}
>
> TBR=pthatcher@webrtc.org,deadbeef@webrtc.org
> NOPRESUBMIT=true
> NOTREECHECKS=true
> NOTRY=true
>
> Committed: https://crrev.com/a81a42f584baa0d93a4b93da9632415e8922450c
> Cr-Commit-Position: refs/heads/master@{#10024}

TBR=pthatcher@webrtc.org,torbjorng@webrtc.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true

Review URL: https://codereview.webrtc.org/1361773005

Cr-Commit-Position: refs/heads/master@{#10036}
diff --git a/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m b/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m
index 5070b78..daebc95 100644
--- a/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m
+++ b/talk/app/webrtc/objctests/RTCPeerConnectionSyncObserver.m
@@ -196,8 +196,11 @@
   if (newState == RTCICEGatheringGathering) {
     return;
   }
+  NSAssert([_expectedICEGatheringChanges count] > 0,
+           @"Unexpected ICE gathering state change");
   int expectedState = [self popFirstElementAsInt:_expectedICEGatheringChanges];
-  NSAssert(expectedState == (int)newState, @"Empty expectation array");
+  NSAssert(expectedState == (int)newState,
+           @"ICE gathering state should match expectation");
 }
 
 - (void)peerConnection:(RTCPeerConnection*)peerConnection
@@ -205,8 +208,11 @@
   // See TODO(fischman) in RTCPeerConnectionTest.mm about Completed.
   if (newState == RTCICEConnectionCompleted)
     return;
+  NSAssert([_expectedICEConnectionChanges count] > 0,
+           @"Unexpected ICE connection state change");
   int expectedState = [self popFirstElementAsInt:_expectedICEConnectionChanges];
-  NSAssert(expectedState == (int)newState, @"Empty expectation array");
+  NSAssert(expectedState == (int)newState,
+           @"ICE connection state should match expectation");
 }
 
 - (void)peerConnection:(RTCPeerConnection*)peerConnection
diff --git a/talk/app/webrtc/objctests/RTCPeerConnectionTest.mm b/talk/app/webrtc/objctests/RTCPeerConnectionTest.mm
index 050c9f4..f0703f5 100644
--- a/talk/app/webrtc/objctests/RTCPeerConnectionTest.mm
+++ b/talk/app/webrtc/objctests/RTCPeerConnectionTest.mm
@@ -182,7 +182,10 @@
   EXPECT_GT([answerSDP.description length], 0);
 
   [offeringExpectations expectICECandidates:2];
-  [answeringExpectations expectICECandidates:2];
+  // It's possible to only have 1 ICE candidate for the answerer, since we use
+  // BUNDLE and rtcp-mux by default, and don't provide any ICE servers in this
+  // test.
+  [answeringExpectations expectICECandidates:1];
 
   sdpObserver = [[RTCSessionDescriptionSyncObserver alloc] init];
   [answeringExpectations expectSignalingChange:RTCSignalingStable];
diff --git a/talk/app/webrtc/peerconnection.cc b/talk/app/webrtc/peerconnection.cc
index 41df847..1215655 100644
--- a/talk/app/webrtc/peerconnection.cc
+++ b/talk/app/webrtc/peerconnection.cc
@@ -616,6 +616,10 @@
   }
   SetSessionDescriptionMsg* msg =  new SetSessionDescriptionMsg(observer);
   signaling_thread()->Post(this, MSG_SET_SESSIONDESCRIPTION_SUCCESS, msg);
+  // MaybeStartGathering needs to be called after posting
+  // MSG_SET_SESSIONDESCRIPTION_SUCCESS, so that we don't signal any candidates
+  // before signaling that SetLocalDescription completed.
+  session_->MaybeStartGathering();
 }
 
 void PeerConnection::SetRemoteDescription(
@@ -868,6 +872,11 @@
 void PeerConnection::OnIceConnectionChange(
     PeerConnectionInterface::IceConnectionState new_state) {
   ASSERT(signaling_thread()->IsCurrent());
+  // After transitioning to "closed", ignore any additional states from
+  // WebRtcSession (such as "disconnected").
+  if (ice_connection_state_ == kIceConnectionClosed) {
+    return;
+  }
   ice_connection_state_ = new_state;
   observer_->OnIceConnectionChange(ice_connection_state_);
 }
diff --git a/talk/app/webrtc/statscollector.cc b/talk/app/webrtc/statscollector.cc
index 76ac76d..0212347 100644
--- a/talk/app/webrtc/statscollector.cc
+++ b/talk/app/webrtc/statscollector.cc
@@ -697,24 +697,18 @@
     // expose them in stats reports.  All channels in a transport share the
     // same local and remote certificates.
     //
-    // Note that Transport::GetCertificate and Transport::GetRemoteCertificate
-    // invoke method calls on the worker thread and block this thread, but
-    // messages are still processed on this thread, which may blow way the
-    // existing transports. So we cannot reuse |transport| after these calls.
     StatsReport::Id local_cert_report_id, remote_cert_report_id;
-
-    cricket::Transport* transport =
-        session_->GetTransport(transport_iter.second.content_name);
     rtc::scoped_refptr<rtc::RTCCertificate> certificate;
-    if (transport && transport->GetCertificate(&certificate)) {
+    if (session_->GetLocalCertificate(transport_iter.second.transport_name,
+                                      &certificate)) {
       StatsReport* r = AddCertificateReports(&(certificate->ssl_certificate()));
       if (r)
         local_cert_report_id = r->id();
     }
 
-    transport = session_->GetTransport(transport_iter.second.content_name);
     rtc::scoped_ptr<rtc::SSLCertificate> cert;
-    if (transport && transport->GetRemoteSSLCertificate(cert.accept())) {
+    if (session_->GetRemoteSSLCertificate(transport_iter.second.transport_name,
+                                          cert.accept())) {
       StatsReport* r = AddCertificateReports(cert.get());
       if (r)
         remote_cert_report_id = r->id();
@@ -722,7 +716,7 @@
 
     for (const auto& channel_iter : transport_iter.second.channel_stats) {
       StatsReport::Id id(StatsReport::NewComponentId(
-          transport_iter.second.content_name, channel_iter.component));
+          transport_iter.second.transport_name, channel_iter.component));
       StatsReport* channel_report = reports_.ReplaceOrAddNew(id);
       channel_report->set_timestamp(stats_gathering_started_);
       channel_report->AddInt(StatsReport::kStatsValueNameComponent,
@@ -939,7 +933,6 @@
     StatsReport* report = entry.second;
     report->set_timestamp(stats_gathering_started_);
   }
-
 }
 
 void StatsCollector::ClearUpdateStatsCacheForTest() {
diff --git a/talk/app/webrtc/statscollector_unittest.cc b/talk/app/webrtc/statscollector_unittest.cc
index 9b037c4..0a9d947 100644
--- a/talk/app/webrtc/statscollector_unittest.cc
+++ b/talk/app/webrtc/statscollector_unittest.cc
@@ -27,6 +27,8 @@
 
 #include <stdio.h>
 
+#include <algorithm>
+
 #include "talk/app/webrtc/statscollector.h"
 
 #include "talk/app/webrtc/mediastream.h"
@@ -45,7 +47,7 @@
 #include "webrtc/base/fakesslidentity.h"
 #include "webrtc/base/gunit.h"
 #include "webrtc/base/network.h"
-#include "webrtc/p2p/base/fakesession.h"
+#include "webrtc/p2p/base/faketransportcontroller.h"
 
 using rtc::scoped_ptr;
 using testing::_;
@@ -89,7 +91,12 @@
   MOCK_METHOD2(GetLocalTrackIdBySsrc, bool(uint32, std::string*));
   MOCK_METHOD2(GetRemoteTrackIdBySsrc, bool(uint32, std::string*));
   MOCK_METHOD1(GetTransportStats, bool(cricket::SessionStats*));
-  MOCK_METHOD1(GetTransport, cricket::Transport*(const std::string&));
+  MOCK_METHOD2(GetLocalCertificate,
+               bool(const std::string& transport_name,
+                    rtc::scoped_refptr<rtc::RTCCertificate>* certificate));
+  MOCK_METHOD2(GetRemoteSSLCertificate,
+               bool(const std::string& transport_name,
+                    rtc::SSLCertificate** cert));
 };
 
 class MockVideoMediaChannel : public cricket::FakeVideoMediaChannel {
@@ -500,7 +507,7 @@
     cricket::TransportStats transport_stats;
     cricket::TransportChannelStats channel_stats;
     channel_stats.component = 1;
-    transport_stats.content_name = kTransportName;
+    transport_stats.transport_name = kTransportName;
     transport_stats.channel_stats.push_back(channel_stats);
 
     session_stats_.transport_stats[kTransportName] = transport_stats;
@@ -647,36 +654,27 @@
     channel_stats.ssl_cipher = "the-ssl-cipher";
 
     cricket::TransportStats transport_stats;
-    transport_stats.content_name = "audio";
+    transport_stats.transport_name = "audio";
     transport_stats.channel_stats.push_back(channel_stats);
 
     cricket::SessionStats session_stats;
-    session_stats.transport_stats[transport_stats.content_name] =
+    session_stats.transport_stats[transport_stats.transport_name] =
         transport_stats;
 
-    // Fake certificates to report.
+    // Fake certificate to report
     rtc::scoped_refptr<rtc::RTCCertificate> local_certificate(
         rtc::RTCCertificate::Create(rtc::scoped_ptr<rtc::FakeSSLIdentity>(
-            new rtc::FakeSSLIdentity(local_cert)).Pass()));
-    rtc::scoped_ptr<rtc::FakeSSLCertificate> remote_cert_copy(
-        remote_cert.GetReference());
-
-    // Fake transport object.
-    rtc::scoped_ptr<cricket::FakeTransport> transport(
-        new cricket::FakeTransport(
-            session_.signaling_thread(),
-            session_.worker_thread(),
-            transport_stats.content_name));
-    transport->SetCertificate(local_certificate);
-    cricket::FakeTransportChannel* channel =
-        static_cast<cricket::FakeTransportChannel*>(
-            transport->CreateChannel(channel_stats.component));
-    EXPECT_FALSE(channel == NULL);
-    channel->SetRemoteSSLCertificate(remote_cert_copy.get());
+                                        new rtc::FakeSSLIdentity(local_cert))
+                                        .Pass()));
 
     // Configure MockWebRtcSession
-    EXPECT_CALL(session_, GetTransport(transport_stats.content_name))
-      .WillRepeatedly(Return(transport.get()));
+    EXPECT_CALL(session_,
+                GetLocalCertificate(transport_stats.transport_name, _))
+        .WillOnce(DoAll(SetArgPointee<1>(local_certificate), Return(true)));
+    EXPECT_CALL(session_,
+                GetRemoteSSLCertificate(transport_stats.transport_name, _))
+        .WillOnce(
+            DoAll(SetArgPointee<1>(remote_cert.GetReference()), Return(true)));
     EXPECT_CALL(session_, GetTransportStats(_))
       .WillOnce(DoAll(SetArgPointee<0>(session_stats),
                       Return(true)));
@@ -790,14 +788,17 @@
 TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) {
   StatsCollectorForTest stats(&session_);
 
+  EXPECT_CALL(session_, GetLocalCertificate(_, _))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
+      .WillRepeatedly(Return(false));
+
   const char kVideoChannelName[] = "video";
 
   InitSessionStats(kVideoChannelName);
   EXPECT_CALL(session_, GetTransportStats(_))
       .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_),
                             Return(true)));
-  EXPECT_CALL(session_, GetTransport(_))
-      .WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
 
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   cricket::VideoChannel video_channel(rtc::Thread::Current(),
@@ -833,14 +834,17 @@
 TEST_F(StatsCollectorTest, BandwidthEstimationInfoIsReported) {
   StatsCollectorForTest stats(&session_);
 
+  EXPECT_CALL(session_, GetLocalCertificate(_, _))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
+      .WillRepeatedly(Return(false));
+
   const char kVideoChannelName[] = "video";
 
   InitSessionStats(kVideoChannelName);
   EXPECT_CALL(session_, GetTransportStats(_))
       .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_),
                             Return(true)));
-  EXPECT_CALL(session_, GetTransport(_))
-      .WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
 
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   cricket::VideoChannel video_channel(rtc::Thread::Current(),
@@ -946,13 +950,16 @@
 TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) {
   StatsCollectorForTest stats(&session_);
 
+  EXPECT_CALL(session_, GetLocalCertificate(_, _))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
+      .WillRepeatedly(Return(false));
+
   const char kVideoChannelName[] = "video";
   InitSessionStats(kVideoChannelName);
   EXPECT_CALL(session_, GetTransportStats(_))
       .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_),
                             Return(true)));
-  EXPECT_CALL(session_, GetTransport(_))
-      .WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
 
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   cricket::VideoChannel video_channel(rtc::Thread::Current(),
@@ -1011,11 +1018,13 @@
 TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) {
   StatsCollectorForTest stats(&session_);
 
-  // Ignore unused callback (logspam).
-  EXPECT_CALL(session_, GetTransport(_))
-      .WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
+  EXPECT_CALL(session_, GetLocalCertificate(_, _))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
+      .WillRepeatedly(Return(false));
+
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
-  // The content_name known by the video channel.
+  // The transport_name known by the video channel.
   const std::string kVcName("vcname");
   cricket::VideoChannel video_channel(rtc::Thread::Current(),
       media_channel, NULL, kVcName, false);
@@ -1073,7 +1082,7 @@
   StatsCollectorForTest stats(&session_);
 
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
-  // The content_name known by the video channel.
+  // The transport_name known by the video channel.
   const std::string kVcName("vcname");
   cricket::VideoChannel video_channel(rtc::Thread::Current(),
       media_channel, NULL, kVcName, false);
@@ -1096,11 +1105,13 @@
 TEST_F(StatsCollectorTest, RemoteSsrcInfoIsPresent) {
   StatsCollectorForTest stats(&session_);
 
-  // Ignore unused callback (logspam).
-  EXPECT_CALL(session_, GetTransport(_))
-      .WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
+  EXPECT_CALL(session_, GetLocalCertificate(_, _))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
+      .WillRepeatedly(Return(false));
+
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
-  // The content_name known by the video channel.
+  // The transport_name known by the video channel.
   const std::string kVcName("vcname");
   cricket::VideoChannel video_channel(rtc::Thread::Current(),
       media_channel, NULL, kVcName, false);
@@ -1145,13 +1156,16 @@
 TEST_F(StatsCollectorTest, ReportsFromRemoteTrack) {
   StatsCollectorForTest stats(&session_);
 
+  EXPECT_CALL(session_, GetLocalCertificate(_, _))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
+      .WillRepeatedly(Return(false));
+
   const char kVideoChannelName[] = "video";
   InitSessionStats(kVideoChannelName);
   EXPECT_CALL(session_, GetTransportStats(_))
       .WillRepeatedly(DoAll(SetArgPointee<0>(session_stats_),
                             Return(true)));
-  EXPECT_CALL(session_, GetTransport(_))
-      .WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
 
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   cricket::VideoChannel video_channel(rtc::Thread::Current(),
@@ -1330,6 +1344,11 @@
 TEST_F(StatsCollectorTest, NoTransport) {
   StatsCollectorForTest stats(&session_);
 
+  EXPECT_CALL(session_, GetLocalCertificate(_, _))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
+      .WillRepeatedly(Return(false));
+
   StatsReports reports;  // returned values.
 
   // Fake stats to process.
@@ -1337,16 +1356,14 @@
   channel_stats.component = 1;
 
   cricket::TransportStats transport_stats;
-  transport_stats.content_name = "audio";
+  transport_stats.transport_name = "audio";
   transport_stats.channel_stats.push_back(channel_stats);
 
   cricket::SessionStats session_stats;
-  session_stats.transport_stats[transport_stats.content_name] =
+  session_stats.transport_stats[transport_stats.transport_name] =
       transport_stats;
 
   // Configure MockWebRtcSession
-  EXPECT_CALL(session_, GetTransport(transport_stats.content_name))
-    .WillRepeatedly(ReturnNull());
   EXPECT_CALL(session_, GetTransportStats(_))
     .WillOnce(DoAll(SetArgPointee<0>(session_stats),
                     Return(true)));
@@ -1389,6 +1406,11 @@
 TEST_F(StatsCollectorTest, NoCertificates) {
   StatsCollectorForTest stats(&session_);
 
+  EXPECT_CALL(session_, GetLocalCertificate(_, _))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
+      .WillRepeatedly(Return(false));
+
   StatsReports reports;  // returned values.
 
   // Fake stats to process.
@@ -1396,23 +1418,18 @@
   channel_stats.component = 1;
 
   cricket::TransportStats transport_stats;
-  transport_stats.content_name = "audio";
+  transport_stats.transport_name = "audio";
   transport_stats.channel_stats.push_back(channel_stats);
 
   cricket::SessionStats session_stats;
-  session_stats.transport_stats[transport_stats.content_name] =
+  session_stats.transport_stats[transport_stats.transport_name] =
       transport_stats;
 
   // Fake transport object.
   rtc::scoped_ptr<cricket::FakeTransport> transport(
-      new cricket::FakeTransport(
-          session_.signaling_thread(),
-          session_.worker_thread(),
-          transport_stats.content_name));
+      new cricket::FakeTransport(transport_stats.transport_name));
 
   // Configure MockWebRtcSession
-  EXPECT_CALL(session_, GetTransport(transport_stats.content_name))
-    .WillRepeatedly(Return(transport.get()));
   EXPECT_CALL(session_, GetTransportStats(_))
     .WillOnce(DoAll(SetArgPointee<0>(session_stats),
                     Return(true)));
@@ -1458,12 +1475,13 @@
 TEST_F(StatsCollectorTest, GetStatsFromLocalAudioTrack) {
   StatsCollectorForTest stats(&session_);
 
-  // Ignore unused callback (logspam).
-  EXPECT_CALL(session_, GetTransport(_))
-      .WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
+  EXPECT_CALL(session_, GetLocalCertificate(_, _))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
+      .WillRepeatedly(Return(false));
 
   MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel();
-  // The content_name known by the voice channel.
+  // The transport_name known by the voice channel.
   const std::string kVcName("vcname");
   cricket::VoiceChannel voice_channel(rtc::Thread::Current(),
       media_engine_, media_channel, NULL, kVcName, false);
@@ -1492,11 +1510,13 @@
 TEST_F(StatsCollectorTest, GetStatsFromRemoteStream) {
   StatsCollectorForTest stats(&session_);
 
-  // Ignore unused callback (logspam).
-  EXPECT_CALL(session_, GetTransport(_))
-      .WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
+  EXPECT_CALL(session_, GetLocalCertificate(_, _))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
+      .WillRepeatedly(Return(false));
+
   MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel();
-  // The content_name known by the voice channel.
+  // The transport_name known by the voice channel.
   const std::string kVcName("vcname");
   cricket::VoiceChannel voice_channel(rtc::Thread::Current(),
       media_engine_, media_channel, NULL, kVcName, false);
@@ -1519,11 +1539,13 @@
 TEST_F(StatsCollectorTest, GetStatsAfterRemoveAudioStream) {
   StatsCollectorForTest stats(&session_);
 
-  // Ignore unused callback (logspam).
-  EXPECT_CALL(session_, GetTransport(_))
-      .WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
+  EXPECT_CALL(session_, GetLocalCertificate(_, _))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
+      .WillRepeatedly(Return(false));
+
   MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel();
-  // The content_name known by the voice channel.
+  // The transport_name known by the voice channel.
   const std::string kVcName("vcname");
   cricket::VoiceChannel voice_channel(rtc::Thread::Current(),
       media_engine_, media_channel, NULL, kVcName, false);
@@ -1578,11 +1600,13 @@
 TEST_F(StatsCollectorTest, LocalAndRemoteTracksWithSameSsrc) {
   StatsCollectorForTest stats(&session_);
 
-  // Ignore unused callback (logspam).
-  EXPECT_CALL(session_, GetTransport(_))
-      .WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
+  EXPECT_CALL(session_, GetLocalCertificate(_, _))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
+      .WillRepeatedly(Return(false));
+
   MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel();
-  // The content_name known by the voice channel.
+  // The transport_name known by the voice channel.
   const std::string kVcName("vcname");
   cricket::VoiceChannel voice_channel(rtc::Thread::Current(),
       media_engine_, media_channel, NULL, kVcName, false);
@@ -1663,11 +1687,13 @@
 TEST_F(StatsCollectorTest, TwoLocalTracksWithSameSsrc) {
   StatsCollectorForTest stats(&session_);
 
-  // Ignore unused callback (logspam).
-  EXPECT_CALL(session_, GetTransport(_))
-      .WillRepeatedly(Return(static_cast<cricket::Transport*>(NULL)));
+  EXPECT_CALL(session_, GetLocalCertificate(_, _))
+      .WillRepeatedly(Return(false));
+  EXPECT_CALL(session_, GetRemoteSSLCertificate(_, _))
+      .WillRepeatedly(Return(false));
+
   MockVoiceMediaChannel* media_channel = new MockVoiceMediaChannel();
-  // The content_name known by the voice channel.
+  // The transport_name known by the voice channel.
   const std::string kVcName("vcname");
   cricket::VoiceChannel voice_channel(rtc::Thread::Current(),
       media_engine_, media_channel, NULL, kVcName, false);
diff --git a/talk/app/webrtc/webrtcsession.cc b/talk/app/webrtc/webrtcsession.cc
index 0c0e44d..bd1cd1a 100644
--- a/talk/app/webrtc/webrtcsession.cc
+++ b/talk/app/webrtc/webrtcsession.cc
@@ -31,6 +31,7 @@
 
 #include <algorithm>
 #include <vector>
+#include <set>
 
 #include "talk/app/webrtc/jsepicecandidate.h"
 #include "talk/app/webrtc/jsepsessiondescription.h"
@@ -86,6 +87,7 @@
     "Couldn't set up DTLS-SRTP on RTP channel.";
 const char kDtlsSetupFailureRtcp[] =
     "Couldn't set up DTLS-SRTP on RTCP channel.";
+const char kEnableBundleFailed[] = "Failed to enable BUNDLE.";
 const int kMaxUnsignalledRecvStreams = 20;
 
 IceCandidatePairType GetIceCandidatePairCounter(
@@ -543,7 +545,6 @@
                            worker_thread,
                            port_allocator,
                            rtc::ToString(rtc::CreateRandomId64() & LLONG_MAX),
-                           cricket::NS_JINGLE_RTP,
                            false),
       // RFC 3264: The numeric value of the session id and version in the
       // o line MUST be representable with a "64 bit signed integer".
@@ -558,6 +559,14 @@
       data_channel_type_(cricket::DCT_NONE),
       ice_restart_latch_(new IceRestartAnswerLatch),
       metrics_observer_(NULL) {
+  transport_controller()->SignalConnectionState.connect(
+      this, &WebRtcSession::OnTransportControllerConnectionState);
+  transport_controller()->SignalReceiving.connect(
+      this, &WebRtcSession::OnTransportControllerReceiving);
+  transport_controller()->SignalGatheringState.connect(
+      this, &WebRtcSession::OnTransportControllerGatheringState);
+  transport_controller()->SignalCandidatesGathered.connect(
+      this, &WebRtcSession::OnTransportControllerCandidatesGathered);
 }
 
 WebRtcSession::~WebRtcSession() {
@@ -583,12 +592,12 @@
 
 bool WebRtcSession::Initialize(
     const PeerConnectionFactoryInterface::Options& options,
-    const MediaConstraintsInterface*  constraints,
+    const MediaConstraintsInterface* constraints,
     rtc::scoped_ptr<DtlsIdentityStoreInterface> dtls_identity_store,
     const PeerConnectionInterface::RTCConfiguration& rtc_configuration) {
   bundle_policy_ = rtc_configuration.bundle_policy;
   rtcp_mux_policy_ = rtc_configuration.rtcp_mux_policy;
-  SetSslMaxProtocolVersion(options.ssl_max_version);
+  transport_controller()->SetSslMaxProtocolVersion(options.ssl_max_version);
 
   // Obtain a certificate from RTCConfiguration if any were provided (optional).
   rtc::scoped_refptr<rtc::RTCCertificate> certificate;
@@ -613,10 +622,8 @@
     // Enable DTLS by default if we have an identity store or a certificate.
     dtls_enabled_ = (dtls_identity_store || certificate);
     // |constraints| can override the default |dtls_enabled_| value.
-    if (FindConstraint(
-          constraints,
-          MediaConstraintsInterface::kEnableDtlsSrtp,
-          &value, nullptr)) {
+    if (FindConstraint(constraints, MediaConstraintsInterface::kEnableDtlsSrtp,
+                       &value, nullptr)) {
       dtls_enabled_ = value;
     }
   }
@@ -736,35 +743,21 @@
   if (!dtls_enabled_) {
     // Construct with DTLS disabled.
     webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory(
-        signaling_thread(),
-        channel_manager_,
-        mediastream_signaling_,
-        this,
-        id(),
-        data_channel_type_));
+        signaling_thread(), channel_manager_, mediastream_signaling_, this,
+        id(), data_channel_type_));
   } else {
     // Construct with DTLS enabled.
     if (!certificate) {
       // Use the |dtls_identity_store| to generate a certificate.
       RTC_DCHECK(dtls_identity_store);
       webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory(
-          signaling_thread(),
-          channel_manager_,
-          mediastream_signaling_,
-          dtls_identity_store.Pass(),
-          this,
-          id(),
-          data_channel_type_));
+          signaling_thread(), channel_manager_, mediastream_signaling_,
+          dtls_identity_store.Pass(), this, id(), data_channel_type_));
     } else {
       // Use the already generated certificate.
       webrtc_session_desc_factory_.reset(new WebRtcSessionDescriptionFactory(
-          signaling_thread(),
-          channel_manager_,
-          mediastream_signaling_,
-          certificate,
-          this,
-          id(),
-          data_channel_type_));
+          signaling_thread(), channel_manager_, mediastream_signaling_,
+          certificate, this, id(), data_channel_type_));
     }
   }
 
@@ -791,26 +784,12 @@
 
 void WebRtcSession::Terminate() {
   SetState(STATE_RECEIVEDTERMINATE);
-  RemoveUnusedChannelsAndTransports(NULL);
+  RemoveUnusedChannels(NULL);
   ASSERT(!voice_channel_);
   ASSERT(!video_channel_);
   ASSERT(!data_channel_);
 }
 
-bool WebRtcSession::StartCandidatesAllocation() {
-  // SpeculativelyConnectTransportChannels, will call ConnectChannels method
-  // from TransportProxy to start gathering ice candidates.
-  SpeculativelyConnectAllTransportChannels();
-  if (!saved_candidates_.empty()) {
-    // If there are saved candidates which arrived before local description is
-    // set, copy those to remote description.
-    CopySavedCandidates(remote_desc_.get());
-  }
-  // Push remote candidates present in remote description to transport channels.
-  UseCandidatesInSessionDescription(remote_desc_.get());
-  return true;
-}
-
 void WebRtcSession::SetSdesPolicy(cricket::SecurePolicy secure_policy) {
   webrtc_session_desc_factory_->SetSdesPolicy(secure_policy);
 }
@@ -826,17 +805,7 @@
     return false;
   }
 
-  // TODO(mallinath) - Return role of each transport, as role may differ from
-  // one another.
-  // In current implementaion we just return the role of first transport in the
-  // transport map.
-  for (cricket::TransportMap::const_iterator iter = transport_proxies().begin();
-       iter != transport_proxies().end(); ++iter) {
-    if (iter->second->impl()) {
-      return iter->second->impl()->GetSslRole(role);
-    }
-  }
-  return false;
+  return transport_controller()->GetSslRole(role);
 }
 
 void WebRtcSession::CreateOffer(
@@ -852,6 +821,8 @@
 
 bool WebRtcSession::SetLocalDescription(SessionDescriptionInterface* desc,
                                         std::string* err_desc) {
+  ASSERT(signaling_thread()->IsCurrent());
+
   // Takes the ownership of |desc| regardless of the result.
   rtc::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
 
@@ -884,16 +855,24 @@
     return BadLocalSdp(desc->type(), kCreateChannelFailed, err_desc);
   }
 
-  // Remove channel and transport proxies, if MediaContentDescription is
-  // rejected.
-  RemoveUnusedChannelsAndTransports(local_desc_->description());
+  // Remove unused channels if MediaContentDescription is rejected.
+  RemoveUnusedChannels(local_desc_->description());
 
   if (!UpdateSessionState(action, cricket::CS_LOCAL, err_desc)) {
     return false;
   }
 
-  // Kick starting the ice candidates allocation.
-  StartCandidatesAllocation();
+  if (remote_description()) {
+    // Now that we have a local description, we can push down remote candidates
+    // that we stored, and those from the remote description.
+    if (!saved_candidates_.empty()) {
+      // If there are saved candidates which arrived before the local
+      // description was set, copy those to the remote description.
+      CopySavedCandidates(remote_desc_.get());
+    }
+    // Push remote candidates in remote description to transport channels.
+    UseCandidatesInSessionDescription(remote_desc_.get());
+  }
 
   // Update state and SSRC of local MediaStreams and DataChannels based on the
   // local session description.
@@ -911,6 +890,8 @@
 
 bool WebRtcSession::SetRemoteDescription(SessionDescriptionInterface* desc,
                                          std::string* err_desc) {
+  ASSERT(signaling_thread()->IsCurrent());
+
   // Takes the ownership of |desc| regardless of the result.
   rtc::scoped_ptr<SessionDescriptionInterface> desc_temp(desc);
 
@@ -927,9 +908,8 @@
     return BadRemoteSdp(desc->type(), kCreateChannelFailed, err_desc);
   }
 
-  // Remove channel and transport proxies, if MediaContentDescription is
-  // rejected.
-  RemoveUnusedChannelsAndTransports(desc->description());
+  // Remove unused channels if MediaContentDescription is rejected.
+  RemoveUnusedChannels(desc->description());
 
   // NOTE: Candidates allocation will be initiated only when SetLocalDescription
   // is called.
@@ -988,6 +968,8 @@
 bool WebRtcSession::UpdateSessionState(
     Action action, cricket::ContentSource source,
     std::string* err_desc) {
+  ASSERT(signaling_thread()->IsCurrent());
+
   // If there's already a pending error then no state transition should happen.
   // But all call-sites should be verifying this before calling us!
   ASSERT(error() == cricket::BaseSession::ERROR_NONE);
@@ -1021,7 +1003,21 @@
     if (!PushdownTransportDescription(source, cricket::CA_ANSWER, &td_err)) {
       return BadAnswerSdp(source, MakeTdErrorString(td_err), err_desc);
     }
-    MaybeEnableMuxingSupport();
+    const cricket::ContentGroup* local_bundle =
+        BaseSession::local_description()->GetGroupByName(
+            cricket::GROUP_TYPE_BUNDLE);
+    const cricket::ContentGroup* remote_bundle =
+        BaseSession::remote_description()->GetGroupByName(
+            cricket::GROUP_TYPE_BUNDLE);
+    if (local_bundle && remote_bundle) {
+      // The answerer decides the transport to bundle on
+      const cricket::ContentGroup* answer_bundle =
+          (source == cricket::CS_LOCAL ? local_bundle : remote_bundle);
+      if (!EnableBundle(*answer_bundle)) {
+        LOG(LS_WARNING) << "Failed to enable BUNDLE.";
+        return BadAnswerSdp(source, kEnableBundleFailed, err_desc);
+      }
+    }
     EnableChannels();
     SetState(source == cricket::CS_LOCAL ?
         STATE_SENTACCEPT : STATE_RECEIVEDACCEPT);
@@ -1070,32 +1066,101 @@
 
 bool WebRtcSession::GetTransportStats(cricket::SessionStats* stats) {
   ASSERT(signaling_thread()->IsCurrent());
+  return (GetChannelTransportStats(voice_channel(), stats) &&
+          GetChannelTransportStats(video_channel(), stats) &&
+          GetChannelTransportStats(data_channel(), stats));
+}
 
-  const auto get_transport_stats = [stats](const std::string& content_name,
-                                           cricket::Transport* transport) {
-    const std::string& transport_id = transport->content_name();
-    stats->proxy_to_transport[content_name] = transport_id;
-    if (stats->transport_stats.find(transport_id)
-        != stats->transport_stats.end()) {
-      // Transport stats already done for this transport.
+bool WebRtcSession::GetChannelTransportStats(cricket::BaseChannel* ch,
+                                             cricket::SessionStats* stats) {
+  ASSERT(signaling_thread()->IsCurrent());
+  if (!ch) {
+    // Not using this channel.
+    return true;
+  }
+
+  const std::string& content_name = ch->content_name();
+  const std::string& transport_name = ch->transport_name();
+  stats->proxy_to_transport[content_name] = transport_name;
+  if (stats->transport_stats.find(transport_name) !=
+      stats->transport_stats.end()) {
+    // Transport stats already done for this transport.
+    return true;
+  }
+
+  cricket::TransportStats tstats;
+  if (!transport_controller()->GetStats(transport_name, &tstats)) {
+    return false;
+  }
+
+  stats->transport_stats[transport_name] = tstats;
+  return true;
+}
+
+bool WebRtcSession::GetLocalCertificate(
+    const std::string& transport_name,
+    rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
+  ASSERT(signaling_thread()->IsCurrent());
+  return transport_controller()->GetLocalCertificate(transport_name,
+                                                     certificate);
+}
+
+bool WebRtcSession::GetRemoteSSLCertificate(const std::string& transport_name,
+                                            rtc::SSLCertificate** cert) {
+  ASSERT(signaling_thread()->IsCurrent());
+  return transport_controller()->GetRemoteSSLCertificate(transport_name, cert);
+}
+
+cricket::BaseChannel* WebRtcSession::GetChannel(
+    const std::string& content_name) {
+  if (voice_channel() && voice_channel()->content_name() == content_name) {
+    return voice_channel();
+  }
+  if (video_channel() && video_channel()->content_name() == content_name) {
+    return video_channel();
+  }
+  if (data_channel() && data_channel()->content_name() == content_name) {
+    return data_channel();
+  }
+  return nullptr;
+}
+
+bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) {
+  const std::string* first_content_name = bundle.FirstContentName();
+  if (!first_content_name) {
+    LOG(LS_WARNING) << "Tried to BUNDLE with no contents.";
+    return false;
+  }
+  const std::string& transport_name = *first_content_name;
+  cricket::BaseChannel* first_channel = GetChannel(transport_name);
+
+  auto maybe_set_transport = [this, bundle, transport_name,
+                              first_channel](cricket::BaseChannel* ch) {
+    if (!ch || !bundle.HasContentName(ch->content_name())) {
       return true;
     }
 
-    cricket::TransportStats tstats;
-    if (!transport->GetStats(&tstats)) {
-      return false;
+    if (ch->transport_name() == transport_name) {
+      LOG(LS_INFO) << "BUNDLE already enabled for " << ch->content_name()
+                   << " on " << transport_name << ".";
+      return true;
     }
 
-    stats->transport_stats[transport_id] = tstats;
+    if (!ch->SetTransport(transport_name)) {
+      LOG(LS_WARNING) << "Failed to enable BUNDLE for " << ch->content_name();
+      return false;
+    }
+    LOG(LS_INFO) << "Enabled BUNDLE for " << ch->content_name() << " on "
+                 << transport_name << ".";
     return true;
   };
 
-  for (const auto& kv : transport_proxies()) {
-    cricket::Transport* transport = kv.second->impl();
-    if (transport && !get_transport_stats(kv.first, transport)) {
-      return false;
-    }
+  if (!maybe_set_transport(voice_channel()) ||
+      !maybe_set_transport(video_channel()) ||
+      !maybe_set_transport(data_channel())) {
+    return false;
   }
+
   return true;
 }
 
@@ -1401,13 +1466,18 @@
 
 void WebRtcSession::OnCertificateReady(
     const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
-  SetCertificate(certificate);
+  transport_controller()->SetLocalCertificate(certificate);
 }
 
 bool WebRtcSession::waiting_for_certificate_for_testing() const {
   return webrtc_session_desc_factory_->waiting_for_certificate_for_testing();
 }
 
+const rtc::scoped_refptr<rtc::RTCCertificate>&
+WebRtcSession::certificate_for_testing() {
+  return transport_controller()->certificate_for_testing();
+}
+
 void WebRtcSession::SetIceConnectionState(
       PeerConnectionInterface::IceConnectionState state) {
   if (ice_connection_state_ == state) {
@@ -1418,6 +1488,8 @@
   // WebRtcSession does not implement "kIceConnectionClosed" (that is handled
   // within PeerConnection).  This switch statement should compile away when
   // ASSERTs are disabled.
+  LOG(LS_INFO) << "Changing IceConnectionState " << ice_connection_state_
+               << " => " << state;
   switch (ice_connection_state_) {
     case PeerConnectionInterface::kIceConnectionNew:
       ASSERT(state == PeerConnectionInterface::kIceConnectionChecking);
@@ -1458,70 +1530,52 @@
   }
 }
 
-void WebRtcSession::OnTransportRequestSignaling(
-    cricket::Transport* transport) {
-  ASSERT(signaling_thread()->IsCurrent());
-  transport->OnSignalingReady();
-  if (ice_observer_) {
-    ice_observer_->OnIceGatheringChange(
-      PeerConnectionInterface::kIceGatheringGathering);
-  }
-}
-
-void WebRtcSession::OnTransportConnecting(cricket::Transport* transport) {
-  ASSERT(signaling_thread()->IsCurrent());
-  // start monitoring for the write state of the transport.
-  OnTransportWritable(transport);
-}
-
-void WebRtcSession::OnTransportWritable(cricket::Transport* transport) {
-  ASSERT(signaling_thread()->IsCurrent());
-  if (transport->all_channels_writable()) {
-    SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected);
-  } else if (transport->HasChannels()) {
-    // If the current state is Connected or Completed, then there were writable
-    // channels but now there are not, so the next state must be Disconnected.
-    if (ice_connection_state_ ==
-            PeerConnectionInterface::kIceConnectionConnected ||
-        ice_connection_state_ ==
-            PeerConnectionInterface::kIceConnectionCompleted) {
-      SetIceConnectionState(
-          PeerConnectionInterface::kIceConnectionDisconnected);
-    }
-  }
-}
-
-void WebRtcSession::OnTransportCompleted(cricket::Transport* transport) {
-  ASSERT(signaling_thread()->IsCurrent());
-  PeerConnectionInterface::IceConnectionState old_state = ice_connection_state_;
-  SetIceConnectionState(PeerConnectionInterface::kIceConnectionCompleted);
-  // Only report once when Ice connection is completed.
-  if (old_state != PeerConnectionInterface::kIceConnectionCompleted) {
-    cricket::TransportStats stats;
-    if (metrics_observer_ && transport->GetStats(&stats)) {
-      ReportBestConnectionState(stats);
-      ReportNegotiatedCiphers(stats);
-    }
-  }
-}
-
-void WebRtcSession::OnTransportFailed(cricket::Transport* transport) {
-  ASSERT(signaling_thread()->IsCurrent());
-  SetIceConnectionState(PeerConnectionInterface::kIceConnectionFailed);
-}
-
-void WebRtcSession::OnTransportReceiving(cricket::Transport* transport) {
-  ASSERT(signaling_thread()->IsCurrent());
-  // The ice connection is considered receiving if at least one transport is
-  // receiving on any channels.
-  bool receiving = false;
-  for (const auto& kv : transport_proxies()) {
-    cricket::Transport* transport = kv.second->impl();
-    if (transport && transport->any_channel_receiving()) {
-      receiving = true;
+void WebRtcSession::OnTransportControllerConnectionState(
+    cricket::IceConnectionState state) {
+  switch (state) {
+    case cricket::kIceConnectionConnecting:
+      // If the current state is Connected or Completed, then there were
+      // writable channels but now there are not, so the next state must
+      // be Disconnected.
+      // kIceConnectionConnecting is currently used as the default,
+      // un-connected state by the TransportController, so its only use is
+      // detecting disconnections.
+      if (ice_connection_state_ ==
+              PeerConnectionInterface::kIceConnectionConnected ||
+          ice_connection_state_ ==
+              PeerConnectionInterface::kIceConnectionCompleted) {
+        SetIceConnectionState(
+            PeerConnectionInterface::kIceConnectionDisconnected);
+      }
       break;
-    }
+    case cricket::kIceConnectionFailed:
+      SetIceConnectionState(PeerConnectionInterface::kIceConnectionFailed);
+      break;
+    case cricket::kIceConnectionConnected:
+      LOG(LS_INFO) << "Changing to ICE connected state because "
+                   << "all transports are writable.";
+      SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected);
+      break;
+    case cricket::kIceConnectionCompleted:
+      LOG(LS_INFO) << "Changing to ICE completed state because "
+                   << "all transports are complete.";
+      if (ice_connection_state_ !=
+          PeerConnectionInterface::kIceConnectionConnected) {
+        // If jumping directly from "checking" to "connected",
+        // signal "connected" first.
+        SetIceConnectionState(PeerConnectionInterface::kIceConnectionConnected);
+      }
+      SetIceConnectionState(PeerConnectionInterface::kIceConnectionCompleted);
+      if (metrics_observer_) {
+        ReportTransportStats();
+      }
+      break;
+    default:
+      ASSERT(false);
   }
+}
+
+void WebRtcSession::OnTransportControllerReceiving(bool receiving) {
   SetIceConnectionReceiving(receiving);
 }
 
@@ -1535,18 +1589,27 @@
   }
 }
 
-void WebRtcSession::OnTransportProxyCandidatesReady(
-    cricket::TransportProxy* proxy, const cricket::Candidates& candidates) {
+void WebRtcSession::OnTransportControllerCandidatesGathered(
+    const std::string& transport_name,
+    const cricket::Candidates& candidates) {
   ASSERT(signaling_thread()->IsCurrent());
-  ProcessNewLocalCandidate(proxy->content_name(), candidates);
-}
+  int sdp_mline_index;
+  if (!GetLocalCandidateMediaIndex(transport_name, &sdp_mline_index)) {
+    LOG(LS_ERROR) << "OnTransportControllerCandidatesGathered: content name "
+                  << transport_name << " not found";
+    return;
+  }
 
-void WebRtcSession::OnCandidatesAllocationDone() {
-  ASSERT(signaling_thread()->IsCurrent());
-  if (ice_observer_) {
-    ice_observer_->OnIceGatheringChange(
-      PeerConnectionInterface::kIceGatheringComplete);
-    ice_observer_->OnIceComplete();
+  for (cricket::Candidates::const_iterator citer = candidates.begin();
+       citer != candidates.end(); ++citer) {
+    // Use transport_name as the candidate media id.
+    JsepIceCandidate candidate(transport_name, sdp_mline_index, *citer);
+    if (ice_observer_) {
+      ice_observer_->OnIceCandidate(&candidate);
+    }
+    if (local_desc_) {
+      local_desc_->AddCandidate(&candidate);
+    }
   }
 }
 
@@ -1562,29 +1625,6 @@
     data_channel_->Enable(true);
 }
 
-void WebRtcSession::ProcessNewLocalCandidate(
-    const std::string& content_name,
-    const cricket::Candidates& candidates) {
-  int sdp_mline_index;
-  if (!GetLocalCandidateMediaIndex(content_name, &sdp_mline_index)) {
-    LOG(LS_ERROR) << "ProcessNewLocalCandidate: content name "
-                  << content_name << " not found";
-    return;
-  }
-
-  for (cricket::Candidates::const_iterator citer = candidates.begin();
-      citer != candidates.end(); ++citer) {
-    // Use content_name as the candidate media id.
-    JsepIceCandidate candidate(content_name, sdp_mline_index, *citer);
-    if (ice_observer_) {
-      ice_observer_->OnIceCandidate(&candidate);
-    }
-    if (local_desc_) {
-      local_desc_->AddCandidate(&candidate);
-    }
-  }
-}
-
 // Returns the media index for a local ice candidate given the content name.
 bool WebRtcSession::GetLocalCandidateMediaIndex(const std::string& content_name,
                                                 int* sdp_mline_index) {
@@ -1649,7 +1689,8 @@
   candidates.push_back(candidate->candidate());
   // Invoking BaseSession method to handle remote candidates.
   std::string error;
-  if (OnRemoteCandidates(content.name, candidates, &error)) {
+  if (transport_controller()->AddRemoteCandidates(content.name, candidates,
+                                                  &error)) {
     // Candidates successfully submitted for checking.
     if (ice_connection_state_ == PeerConnectionInterface::kIceConnectionNew ||
         ice_connection_state_ ==
@@ -1673,8 +1714,7 @@
   return true;
 }
 
-void WebRtcSession::RemoveUnusedChannelsAndTransports(
-    const SessionDescription* desc) {
+void WebRtcSession::RemoveUnusedChannels(const SessionDescription* desc) {
   // Destroy video_channel_ first since it may have a pointer to the
   // voice_channel_.
   const cricket::ContentInfo* video_info =
@@ -1684,7 +1724,6 @@
     SignalVideoChannelDestroyed();
     const std::string content_name = video_channel_->content_name();
     channel_manager_->DestroyVideoChannel(video_channel_.release());
-    DestroyTransportProxy(content_name);
   }
 
   const cricket::ContentInfo* voice_info =
@@ -1694,7 +1733,6 @@
     SignalVoiceChannelDestroyed();
     const std::string content_name = voice_channel_->content_name();
     channel_manager_->DestroyVoiceChannel(voice_channel_.release());
-    DestroyTransportProxy(content_name);
   }
 
   const cricket::ContentInfo* data_info =
@@ -1704,7 +1742,6 @@
     SignalDataChannelDestroyed();
     const std::string content_name = data_channel_->content_name();
     channel_manager_->DestroyDataChannel(data_channel_.release());
-    DestroyTransportProxy(content_name);
   }
 }
 
@@ -1749,7 +1786,7 @@
     }
   }
 
-  // Enable bundle before when kMaxBundle policy is in effect.
+  // Enable BUNDLE immediately when kBundlePolicyMaxBundle is in effect.
   if (bundle_policy_ == PeerConnectionInterface::kBundlePolicyMaxBundle) {
     const cricket::ContentGroup* bundle_group = desc->GetGroupByName(
         cricket::GROUP_TYPE_BUNDLE);
@@ -1757,7 +1794,7 @@
       LOG(LS_WARNING) << "max-bundle specified without BUNDLE specified";
       return false;
     }
-    if (!BaseSession::BundleContentGroup(bundle_group)) {
+    if (!EnableBundle(*bundle_group)) {
       LOG(LS_WARNING) << "max-bundle failed to enable bundling.";
       return false;
     }
@@ -1768,7 +1805,8 @@
 
 bool WebRtcSession::CreateVoiceChannel(const cricket::ContentInfo* content) {
   voice_channel_.reset(channel_manager_->CreateVoiceChannel(
-      media_controller_.get(), this, content->name, true, audio_options_));
+      media_controller_.get(), transport_controller(), content->name, true,
+      audio_options_));
   if (!voice_channel_) {
     return false;
   }
@@ -1780,7 +1818,8 @@
 
 bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content) {
   video_channel_.reset(channel_manager_->CreateVideoChannel(
-      media_controller_.get(), this, content->name, true, video_options_));
+      media_controller_.get(), transport_controller(), content->name, true,
+      video_options_));
   if (!video_channel_) {
     return false;
   }
@@ -1793,7 +1832,7 @@
 bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content) {
   bool sctp = (data_channel_type_ == cricket::DCT_SCTP);
   data_channel_.reset(channel_manager_->CreateDataChannel(
-      this, content->name, !sctp, data_channel_type_));
+      transport_controller(), content->name, !sctp, data_channel_type_));
   if (!data_channel_) {
     return false;
   }
@@ -1974,7 +2013,6 @@
     const SessionDescriptionInterface* remote_desc,
     bool* valid) {
   *valid = true;;
-  cricket::TransportProxy* transport_proxy = NULL;
 
   const SessionDescriptionInterface* current_remote_desc =
       remote_desc ? remote_desc : remote_description();
@@ -1996,12 +2034,53 @@
 
   cricket::ContentInfo content =
       current_remote_desc->description()->contents()[mediacontent_index];
-  transport_proxy = GetTransportProxy(content.name);
+  cricket::BaseChannel* channel = GetChannel(content.name);
+  if (!channel) {
+    return false;
+  }
 
-  return transport_proxy && transport_proxy->local_description_set() &&
-      transport_proxy->remote_description_set();
+  return transport_controller()->ReadyForRemoteCandidates(
+      channel->transport_name());
 }
 
+void WebRtcSession::OnTransportControllerGatheringState(
+    cricket::IceGatheringState state) {
+  ASSERT(signaling_thread()->IsCurrent());
+  if (state == cricket::kIceGatheringGathering) {
+    if (ice_observer_) {
+      ice_observer_->OnIceGatheringChange(
+          PeerConnectionInterface::kIceGatheringGathering);
+    }
+  } else if (state == cricket::kIceGatheringComplete) {
+    if (ice_observer_) {
+      ice_observer_->OnIceGatheringChange(
+          PeerConnectionInterface::kIceGatheringComplete);
+      ice_observer_->OnIceComplete();
+    }
+  }
+}
+
+void WebRtcSession::ReportTransportStats() {
+  // Use a set so we don't report the same stats twice if two channels share
+  // a transport.
+  std::set<std::string> transport_names;
+  if (voice_channel()) {
+    transport_names.insert(voice_channel()->transport_name());
+  }
+  if (video_channel()) {
+    transport_names.insert(video_channel()->transport_name());
+  }
+  if (data_channel()) {
+    transport_names.insert(data_channel()->transport_name());
+  }
+  for (const auto& name : transport_names) {
+    cricket::TransportStats stats;
+    if (transport_controller()->GetStats(name, &stats)) {
+      ReportBestConnectionState(stats);
+      ReportNegotiatedCiphers(stats);
+    }
+  }
+}
 // Walk through the ConnectionInfos to gather best connection usage
 // for IPv4 and IPv6.
 void WebRtcSession::ReportBestConnectionState(
@@ -2069,13 +2148,13 @@
 
   PeerConnectionMetricsName srtp_name;
   PeerConnectionMetricsName ssl_name;
-  if (stats.content_name == cricket::CN_AUDIO) {
+  if (stats.transport_name == cricket::CN_AUDIO) {
     srtp_name = kAudioSrtpCipher;
     ssl_name = kAudioSslCipher;
-  } else if (stats.content_name == cricket::CN_VIDEO) {
+  } else if (stats.transport_name == cricket::CN_VIDEO) {
     srtp_name = kVideoSrtpCipher;
     ssl_name = kVideoSslCipher;
-  } else if (stats.content_name == cricket::CN_DATA) {
+  } else if (stats.transport_name == cricket::CN_DATA) {
     srtp_name = kDataSrtpCipher;
     ssl_name = kDataSslCipher;
   } else {
diff --git a/talk/app/webrtc/webrtcsession.h b/talk/app/webrtc/webrtcsession.h
index 582d6d0..1ad9a69 100644
--- a/talk/app/webrtc/webrtcsession.h
+++ b/talk/app/webrtc/webrtcsession.h
@@ -29,6 +29,7 @@
 #define TALK_APP_WEBRTC_WEBRTCSESSION_H_
 
 #include <string>
+#include <vector>
 
 #include "talk/app/webrtc/datachannel.h"
 #include "talk/app/webrtc/dtmfsender.h"
@@ -49,7 +50,6 @@
 class ChannelManager;
 class DataChannel;
 class StatsReport;
-class Transport;
 class VideoCapturer;
 class VideoChannel;
 class VoiceChannel;
@@ -77,6 +77,8 @@
 extern const char kSessionErrorDesc[];
 extern const char kDtlsSetupFailureRtp[];
 extern const char kDtlsSetupFailureRtcp[];
+extern const char kEnableBundleFailed[];
+
 // Maximum number of received video streams that will be processed by webrtc
 // even if they are not signalled beforehand.
 extern const int kMaxUnsignalledRecvStreams;
@@ -235,6 +237,19 @@
   // This avoids exposing the internal structures used to track them.
   virtual bool GetTransportStats(cricket::SessionStats* stats);
 
+  // Get stats for a specific channel
+  bool GetChannelTransportStats(cricket::BaseChannel* ch,
+                                cricket::SessionStats* stats);
+
+  // virtual so it can be mocked in unit tests
+  virtual bool GetLocalCertificate(
+      const std::string& transport_name,
+      rtc::scoped_refptr<rtc::RTCCertificate>* certificate);
+
+  // Caller owns returned certificate
+  virtual bool GetRemoteSSLCertificate(const std::string& transport_name,
+                                       rtc::SSLCertificate** cert);
+
   // Implements DataChannelFactory.
   rtc::scoped_refptr<DataChannel> CreateDataChannel(
       const std::string& label,
@@ -254,6 +269,7 @@
 
   // For unit test.
   bool waiting_for_certificate_for_testing() const;
+  const rtc::scoped_refptr<rtc::RTCCertificate>& certificate_for_testing();
 
   void set_metrics_observer(
       webrtc::MetricsObserverInterface* metrics_observer) {
@@ -269,9 +285,6 @@
     kAnswer,
   };
 
-  // Invokes ConnectChannels() on transport proxies, which initiates ice
-  // candidates allocation.
-  bool StartCandidatesAllocation();
   bool UpdateSessionState(Action action, cricket::ContentSource source,
                           std::string* err_desc);
   static Action GetAction(const std::string& type);
@@ -281,25 +294,13 @@
                                 cricket::ContentSource source,
                                 std::string* error_desc);
 
-
-  // Transport related callbacks, override from cricket::BaseSession.
-  virtual void OnTransportRequestSignaling(cricket::Transport* transport);
-  virtual void OnTransportConnecting(cricket::Transport* transport);
-  virtual void OnTransportWritable(cricket::Transport* transport);
-  virtual void OnTransportCompleted(cricket::Transport* transport);
-  virtual void OnTransportFailed(cricket::Transport* transport);
-  virtual void OnTransportProxyCandidatesReady(
-      cricket::TransportProxy* proxy,
-      const cricket::Candidates& candidates);
-  virtual void OnCandidatesAllocationDone();
-  void OnTransportReceiving(cricket::Transport* transport) override;
+  cricket::BaseChannel* GetChannel(const std::string& content_name);
+  // Cause all the BaseChannels in the bundle group to have the same
+  // transport channel.
+  bool EnableBundle(const cricket::ContentGroup& bundle);
 
   // Enables media channels to allow sending of media.
   void EnableChannels();
-  // Creates a JsepIceCandidate and adds it to the local session description
-  // and notify observers. Called when a new local candidate have been found.
-  void ProcessNewLocalCandidate(const std::string& content_name,
-                                const cricket::Candidates& candidates);
   // Returns the media index for a local ice candidate given the content name.
   // Returns false if the local session description does not have a media
   // content called  |content_name|.
@@ -312,8 +313,7 @@
   bool UseCandidate(const IceCandidateInterface* candidate);
   // Deletes the corresponding channel of contents that don't exist in |desc|.
   // |desc| can be null. This means that all channels are deleted.
-  void RemoveUnusedChannelsAndTransports(
-      const cricket::SessionDescription* desc);
+  void RemoveUnusedChannels(const cricket::SessionDescription* desc);
 
   // Allocates media channels based on the |desc|. If |desc| doesn't have
   // the BUNDLE option, this method will disable BUNDLE in PortAllocator.
@@ -362,10 +362,20 @@
                                  const SessionDescriptionInterface* remote_desc,
                                  bool* valid);
 
+  void OnTransportControllerConnectionState(cricket::IceConnectionState state);
+  void OnTransportControllerReceiving(bool receiving);
+  void OnTransportControllerGatheringState(cricket::IceGatheringState state);
+  void OnTransportControllerCandidatesGathered(
+      const std::string& transport_name,
+      const cricket::Candidates& candidates);
+
   std::string GetSessionErrorMsg();
 
-  // Invoked when OnTransportCompleted is signaled to gather the usage
-  // of IPv4/IPv6 as best connection.
+  // Invoked when TransportController connection completion is signaled.
+  // Reports stats for all transports in use.
+  void ReportTransportStats();
+
+  // Gather the usage of IPv4/IPv6 as best connection.
   void ReportBestConnectionState(const cricket::TransportStats& stats);
 
   void ReportNegotiatedCiphers(const cricket::TransportStats& stats);
diff --git a/talk/app/webrtc/webrtcsession_unittest.cc b/talk/app/webrtc/webrtcsession_unittest.cc
index 8bd97e5..6dc0fc7 100644
--- a/talk/app/webrtc/webrtcsession_unittest.cc
+++ b/talk/app/webrtc/webrtcsession_unittest.cc
@@ -25,6 +25,8 @@
  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */
 
+#include <vector>
+
 #include "talk/app/webrtc/audiotrack.h"
 #include "talk/app/webrtc/fakemetricsobserver.h"
 #include "talk/app/webrtc/jsepicecandidate.h"
@@ -163,8 +165,8 @@
                         const std::string& newlines,
                         std::string* message) {
   const std::string tmp = line + newlines;
-  rtc::replace_substrs(line.c_str(), line.length(),
-                       tmp.c_str(), tmp.length(), message);
+  rtc::replace_substrs(line.c_str(), line.length(), tmp.c_str(), tmp.length(),
+                       message);
 }
 
 class MockIceObserver : public webrtc::IceObserver {
@@ -244,12 +246,52 @@
   }
   virtual ~WebRtcSessionForTest() {}
 
-  using cricket::BaseSession::GetTransportProxy;
+  // Note that these methods are only safe to use if the signaling thread
+  // is the same as the worker thread
+  cricket::TransportChannel* voice_rtp_transport_channel() {
+    return rtp_transport_channel(voice_channel());
+  }
+
+  cricket::TransportChannel* voice_rtcp_transport_channel() {
+    return rtcp_transport_channel(voice_channel());
+  }
+
+  cricket::TransportChannel* video_rtp_transport_channel() {
+    return rtp_transport_channel(video_channel());
+  }
+
+  cricket::TransportChannel* video_rtcp_transport_channel() {
+    return rtcp_transport_channel(video_channel());
+  }
+
+  cricket::TransportChannel* data_rtp_transport_channel() {
+    return rtp_transport_channel(data_channel());
+  }
+
+  cricket::TransportChannel* data_rtcp_transport_channel() {
+    return rtcp_transport_channel(data_channel());
+  }
+
   using webrtc::WebRtcSession::SetAudioPlayout;
   using webrtc::WebRtcSession::SetAudioSend;
   using webrtc::WebRtcSession::SetCaptureDevice;
   using webrtc::WebRtcSession::SetVideoPlayout;
   using webrtc::WebRtcSession::SetVideoSend;
+
+ private:
+  cricket::TransportChannel* rtp_transport_channel(cricket::BaseChannel* ch) {
+    if (!ch) {
+      return nullptr;
+    }
+    return ch->transport_channel();
+  }
+
+  cricket::TransportChannel* rtcp_transport_channel(cricket::BaseChannel* ch) {
+    if (!ch) {
+      return nullptr;
+    }
+    return ch->rtcp_transport_channel();
+  }
 };
 
 class WebRtcSessionCreateSDPObserverForTest
@@ -375,9 +417,9 @@
     EXPECT_EQ(PeerConnectionInterface::kIceGatheringNew,
         observer_.ice_gathering_state_);
 
-    EXPECT_TRUE(session_->Initialize(
-        options_, constraints_.get(), dtls_identity_store.Pass(),
-        rtc_configuration));
+    EXPECT_TRUE(session_->Initialize(options_, constraints_.get(),
+                                     dtls_identity_store.Pass(),
+                                     rtc_configuration));
     session_->set_metrics_observer(metrics_observer_);
   }
 
@@ -490,13 +532,6 @@
             session_->video_channel() != NULL);
   }
 
-  void CheckTransportChannels() const {
-    EXPECT_TRUE(session_->GetChannel(cricket::CN_AUDIO, 1) != NULL);
-    EXPECT_TRUE(session_->GetChannel(cricket::CN_AUDIO, 2) != NULL);
-    EXPECT_TRUE(session_->GetChannel(cricket::CN_VIDEO, 1) != NULL);
-    EXPECT_TRUE(session_->GetChannel(cricket::CN_VIDEO, 2) != NULL);
-  }
-
   void VerifyCryptoParams(const cricket::SessionDescription* sdp) {
     ASSERT_TRUE(session_.get() != NULL);
     const cricket::ContentInfo* content = cricket::GetFirstAudioContent(sdp);
@@ -722,6 +757,7 @@
   }
   void SetLocalDescriptionWithoutError(SessionDescriptionInterface* desc) {
     EXPECT_TRUE(session_->SetLocalDescription(desc, NULL));
+    session_->MaybeStartGathering();
   }
   void SetLocalDescriptionExpectState(SessionDescriptionInterface* desc,
                                       BaseSession::State expected_state) {
@@ -968,15 +1004,10 @@
     SetRemoteDescriptionWithoutError(new_answer);
     EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout);
     EXPECT_EQ(expected_candidate_num, observer_.mline_0_candidates_.size());
-    EXPECT_EQ(expected_candidate_num, observer_.mline_1_candidates_.size());
-    for (size_t i = 0; i < observer_.mline_0_candidates_.size(); ++i) {
-      cricket::Candidate c0 = observer_.mline_0_candidates_[i];
-      cricket::Candidate c1 = observer_.mline_1_candidates_[i];
-      if (bundle) {
-        EXPECT_TRUE(c0.IsEquivalent(c1));
-      } else {
-        EXPECT_FALSE(c0.IsEquivalent(c1));
-      }
+    if (bundle) {
+      EXPECT_EQ(0, observer_.mline_1_candidates_.size());
+    } else {
+      EXPECT_EQ(expected_candidate_num, observer_.mline_1_candidates_.size());
     }
   }
   // Tests that we can only send DTMF when the dtmf codec is supported.
@@ -1001,7 +1032,7 @@
   // initial ICE convergences.
 
   class LoopbackNetworkConfiguration {
-  public:
+   public:
     LoopbackNetworkConfiguration()
         : test_ipv6_network_(false),
           test_extra_ipv4_network_(false),
@@ -1150,11 +1181,8 @@
 
     // Clearing the rules, session should move back to completed state.
     loopback_network_manager.ClearRules(fss_.get());
-    // Session is automatically calling OnSignalingReady after creation of
-    // new portallocator session which will allocate new set of candidates.
 
     LOG(LS_INFO) << "Firewall Rules cleared";
-
     EXPECT_EQ_WAIT(PeerConnectionInterface::kIceConnectionCompleted,
                    observer_.ice_connection_state_,
                    kIceCandidatesTimeout);
@@ -1707,15 +1735,14 @@
 // a DTLS fingerprint when DTLS is required.
 TEST_P(WebRtcSessionTest, TestSetRemoteNonDtlsAnswerWhenDtlsOn) {
   MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp);
-  // Enable both SDES and DTLS, so that offer won't be outright rejected as a
-  // result of using the "UDP/TLS/RTP/SAVPF" profile.
   InitWithDtls(GetParam());
-  session_->SetSdesPolicy(cricket::SEC_ENABLED);
   SessionDescriptionInterface* offer = CreateOffer();
   cricket::MediaSessionOptions options;
   options.recv_video = true;
+  rtc::scoped_ptr<SessionDescriptionInterface> temp_offer(
+      CreateRemoteOffer(options, cricket::SEC_ENABLED));
   JsepSessionDescription* answer =
-      CreateRemoteAnswer(offer, options, cricket::SEC_ENABLED);
+      CreateRemoteAnswer(temp_offer.get(), options, cricket::SEC_ENABLED);
 
   // SetRemoteDescription and SetLocalDescription will take the ownership of
   // the offer and answer.
@@ -2017,7 +2044,7 @@
   EXPECT_LT(0u, candidates->count());
   candidates = local_desc->candidates(1);
   ASSERT_TRUE(candidates != NULL);
-  EXPECT_LT(0u, candidates->count());
+  EXPECT_EQ(0u, candidates->count());
 
   // Update the session descriptions.
   mediastream_signaling_.SendAudioVideoStream1();
@@ -2029,7 +2056,7 @@
   EXPECT_LT(0u, candidates->count());
   candidates = local_desc->candidates(1);
   ASSERT_TRUE(candidates != NULL);
-  EXPECT_LT(0u, candidates->count());
+  EXPECT_EQ(0u, candidates->count());
 }
 
 // Test that we can set a remote session description with remote candidates.
@@ -2073,23 +2100,17 @@
   // Wait until at least one local candidate has been collected.
   EXPECT_TRUE_WAIT(0u < observer_.mline_0_candidates_.size(),
                    kIceCandidatesTimeout);
-  EXPECT_TRUE_WAIT(0u < observer_.mline_1_candidates_.size(),
-                   kIceCandidatesTimeout);
 
   rtc::scoped_ptr<SessionDescriptionInterface> local_offer(CreateOffer());
 
   ASSERT_TRUE(local_offer->candidates(kMediaContentIndex0) != NULL);
   EXPECT_LT(0u, local_offer->candidates(kMediaContentIndex0)->count());
-  ASSERT_TRUE(local_offer->candidates(kMediaContentIndex1) != NULL);
-  EXPECT_LT(0u, local_offer->candidates(kMediaContentIndex1)->count());
 
   SessionDescriptionInterface* remote_offer(CreateRemoteOffer());
   SetRemoteDescriptionWithoutError(remote_offer);
   SessionDescriptionInterface* answer = CreateAnswer(NULL);
   ASSERT_TRUE(answer->candidates(kMediaContentIndex0) != NULL);
   EXPECT_LT(0u, answer->candidates(kMediaContentIndex0)->count());
-  ASSERT_TRUE(answer->candidates(kMediaContentIndex1) != NULL);
-  EXPECT_LT(0u, answer->candidates(kMediaContentIndex1)->count());
   SetLocalDescriptionWithoutError(answer);
 }
 
@@ -2131,8 +2152,14 @@
       CreateAnswer(NULL);
   SetLocalDescriptionWithoutError(answer);
 
-  EXPECT_TRUE(session_->GetTransportProxy("audio_content_name") != NULL);
-  EXPECT_TRUE(session_->GetTransportProxy("video_content_name") != NULL);
+  cricket::TransportChannel* voice_transport_channel =
+      session_->voice_rtp_transport_channel();
+  EXPECT_TRUE(voice_transport_channel != NULL);
+  EXPECT_EQ(voice_transport_channel->transport_name(), "audio_content_name");
+  cricket::TransportChannel* video_transport_channel =
+      session_->video_rtp_transport_channel();
+  EXPECT_TRUE(video_transport_channel != NULL);
+  EXPECT_EQ(video_transport_channel->transport_name(), "video_content_name");
   EXPECT_TRUE((video_channel_ = media_engine_->GetVideoChannel(0)) != NULL);
   EXPECT_TRUE((voice_channel_ = media_engine_->GetVoiceChannel(0)) != NULL);
 }
@@ -2692,20 +2719,23 @@
   SessionDescriptionInterface* answer = CreateAnswer(NULL);
   SetLocalDescriptionWithoutError(answer);
 
-  EXPECT_EQ(session_->GetTransportProxy("audio")->impl(),
-            session_->GetTransportProxy("video")->impl());
+  EXPECT_EQ(session_->voice_rtp_transport_channel(),
+            session_->video_rtp_transport_channel());
 
-  cricket::Transport* t = session_->GetTransport("audio");
+  cricket::BaseChannel* voice_channel = session_->voice_channel();
+  ASSERT(voice_channel != NULL);
 
   // Checks if one of the transport channels contains a connection using a given
   // port.
-  auto connection_with_remote_port = [t](int port) {
-    cricket::TransportStats stats;
-    t->GetStats(&stats);
-    for (auto& chan_stat : stats.channel_stats) {
-      for (auto& conn_info : chan_stat.connection_infos) {
-        if (conn_info.remote_candidate.address().port() == port) {
-          return true;
+  auto connection_with_remote_port = [this, voice_channel](int port) {
+    cricket::SessionStats stats;
+    session_->GetChannelTransportStats(voice_channel, &stats);
+    for (auto& kv : stats.transport_stats) {
+      for (auto& chan_stat : kv.second.channel_stats) {
+        for (auto& conn_info : chan_stat.connection_infos) {
+          if (conn_info.remote_candidate.address().port() == port) {
+            return true;
+          }
         }
       }
     }
@@ -2758,7 +2788,7 @@
   EXPECT_FALSE(connection_with_remote_port(6000));
 }
 
-// kBundlePolicyBalanced bundle policy and answer contains BUNDLE.
+// kBundlePolicyBalanced BUNDLE policy and answer contains BUNDLE.
 TEST_F(WebRtcSessionTest, TestBalancedBundleInAnswer) {
   InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyBalanced);
   mediastream_signaling_.SendAudioVideoStream1();
@@ -2769,19 +2799,19 @@
   SessionDescriptionInterface* offer = CreateOffer(options);
   SetLocalDescriptionWithoutError(offer);
 
-  EXPECT_NE(session_->GetTransportProxy("audio")->impl(),
-            session_->GetTransportProxy("video")->impl());
+  EXPECT_NE(session_->voice_rtp_transport_channel(),
+            session_->video_rtp_transport_channel());
 
   mediastream_signaling_.SendAudioVideoStream2();
   SessionDescriptionInterface* answer =
       CreateRemoteAnswer(session_->local_description());
   SetRemoteDescriptionWithoutError(answer);
 
-  EXPECT_EQ(session_->GetTransportProxy("audio")->impl(),
-            session_->GetTransportProxy("video")->impl());
+  EXPECT_EQ(session_->voice_rtp_transport_channel(),
+            session_->video_rtp_transport_channel());
 }
 
-// kBundlePolicyBalanced bundle policy but no BUNDLE in the answer.
+// kBundlePolicyBalanced BUNDLE policy but no BUNDLE in the answer.
 TEST_F(WebRtcSessionTest, TestBalancedNoBundleInAnswer) {
   InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyBalanced);
   mediastream_signaling_.SendAudioVideoStream1();
@@ -2792,8 +2822,8 @@
   SessionDescriptionInterface* offer = CreateOffer(options);
   SetLocalDescriptionWithoutError(offer);
 
-  EXPECT_NE(session_->GetTransportProxy("audio")->impl(),
-            session_->GetTransportProxy("video")->impl());
+  EXPECT_NE(session_->voice_rtp_transport_channel(),
+            session_->video_rtp_transport_channel());
 
   mediastream_signaling_.SendAudioVideoStream2();
 
@@ -2807,8 +2837,8 @@
   modified_answer->Initialize(answer_copy, "1", "1");
   SetRemoteDescriptionWithoutError(modified_answer);  //
 
-  EXPECT_NE(session_->GetTransportProxy("audio")->impl(),
-            session_->GetTransportProxy("video")->impl());
+  EXPECT_NE(session_->voice_rtp_transport_channel(),
+            session_->video_rtp_transport_channel());
 }
 
 // kBundlePolicyMaxBundle policy with BUNDLE in the answer.
@@ -2822,16 +2852,49 @@
   SessionDescriptionInterface* offer = CreateOffer(options);
   SetLocalDescriptionWithoutError(offer);
 
-  EXPECT_EQ(session_->GetTransportProxy("audio")->impl(),
-            session_->GetTransportProxy("video")->impl());
+  EXPECT_EQ(session_->voice_rtp_transport_channel(),
+            session_->video_rtp_transport_channel());
 
   mediastream_signaling_.SendAudioVideoStream2();
   SessionDescriptionInterface* answer =
       CreateRemoteAnswer(session_->local_description());
   SetRemoteDescriptionWithoutError(answer);
 
-  EXPECT_EQ(session_->GetTransportProxy("audio")->impl(),
-            session_->GetTransportProxy("video")->impl());
+  EXPECT_EQ(session_->voice_rtp_transport_channel(),
+            session_->video_rtp_transport_channel());
+}
+
+// kBundlePolicyMaxBundle policy with BUNDLE in the answer, but no
+// audio content in the answer.
+TEST_F(WebRtcSessionTest, TestMaxBundleRejectAudio) {
+  InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxBundle);
+  mediastream_signaling_.SendAudioVideoStream1();
+
+  PeerConnectionInterface::RTCOfferAnswerOptions options;
+  options.use_rtp_mux = true;
+
+  SessionDescriptionInterface* offer = CreateOffer(options);
+  SetLocalDescriptionWithoutError(offer);
+
+  EXPECT_EQ(session_->voice_rtp_transport_channel(),
+            session_->video_rtp_transport_channel());
+
+  mediastream_signaling_.SendAudioVideoStream2();
+  cricket::MediaSessionOptions recv_options;
+  recv_options.recv_audio = false;
+  recv_options.recv_video = true;
+  SessionDescriptionInterface* answer =
+      CreateRemoteAnswer(session_->local_description(), recv_options);
+  SetRemoteDescriptionWithoutError(answer);
+
+  EXPECT_TRUE(NULL == session_->voice_channel());
+  EXPECT_TRUE(NULL != session_->video_rtp_transport_channel());
+
+  session_->Terminate();
+  EXPECT_TRUE(NULL == session_->voice_rtp_transport_channel());
+  EXPECT_TRUE(NULL == session_->voice_rtcp_transport_channel());
+  EXPECT_TRUE(NULL == session_->video_rtp_transport_channel());
+  EXPECT_TRUE(NULL == session_->video_rtcp_transport_channel());
 }
 
 // kBundlePolicyMaxBundle policy but no BUNDLE in the answer.
@@ -2845,8 +2908,8 @@
   SessionDescriptionInterface* offer = CreateOffer(options);
   SetLocalDescriptionWithoutError(offer);
 
-  EXPECT_EQ(session_->GetTransportProxy("audio")->impl(),
-            session_->GetTransportProxy("video")->impl());
+  EXPECT_EQ(session_->voice_rtp_transport_channel(),
+            session_->video_rtp_transport_channel());
 
   mediastream_signaling_.SendAudioVideoStream2();
 
@@ -2860,8 +2923,45 @@
   modified_answer->Initialize(answer_copy, "1", "1");
   SetRemoteDescriptionWithoutError(modified_answer);
 
-  EXPECT_EQ(session_->GetTransportProxy("audio")->impl(),
-            session_->GetTransportProxy("video")->impl());
+  EXPECT_EQ(session_->voice_rtp_transport_channel(),
+            session_->video_rtp_transport_channel());
+}
+
+// kBundlePolicyMaxBundle policy with BUNDLE in the remote offer.
+TEST_F(WebRtcSessionTest, TestMaxBundleBundleInRemoteOffer) {
+  InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxBundle);
+  mediastream_signaling_.SendAudioVideoStream1();
+
+  SessionDescriptionInterface* offer = CreateRemoteOffer();
+  SetRemoteDescriptionWithoutError(offer);
+
+  EXPECT_EQ(session_->voice_rtp_transport_channel(),
+            session_->video_rtp_transport_channel());
+
+  mediastream_signaling_.SendAudioVideoStream2();
+  SessionDescriptionInterface* answer = CreateAnswer(nullptr);
+  SetLocalDescriptionWithoutError(answer);
+
+  EXPECT_EQ(session_->voice_rtp_transport_channel(),
+            session_->video_rtp_transport_channel());
+}
+
+// kBundlePolicyMaxBundle policy but no BUNDLE in the remote offer.
+TEST_F(WebRtcSessionTest, TestMaxBundleNoBundleInRemoteOffer) {
+  InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxBundle);
+  mediastream_signaling_.SendAudioVideoStream1();
+
+  // Remove BUNDLE from the offer.
+  rtc::scoped_ptr<SessionDescriptionInterface> offer(CreateRemoteOffer());
+  cricket::SessionDescription* offer_copy = offer->description()->Copy();
+  offer_copy->RemoveGroupByName(cricket::GROUP_TYPE_BUNDLE);
+  JsepSessionDescription* modified_offer =
+      new JsepSessionDescription(JsepSessionDescription::kOffer);
+  modified_offer->Initialize(offer_copy, "1", "1");
+
+  // Expect an error when applying the remote description
+  SetRemoteDescriptionExpectError(JsepSessionDescription::kOffer,
+                                  kCreateChannelFailed, modified_offer);
 }
 
 // kBundlePolicyMaxCompat bundle policy and answer contains BUNDLE.
@@ -2875,8 +2975,8 @@
   SessionDescriptionInterface* offer = CreateOffer(options);
   SetLocalDescriptionWithoutError(offer);
 
-  EXPECT_NE(session_->GetTransportProxy("audio")->impl(),
-            session_->GetTransportProxy("video")->impl());
+  EXPECT_NE(session_->voice_rtp_transport_channel(),
+            session_->video_rtp_transport_channel());
 
   mediastream_signaling_.SendAudioVideoStream2();
   SessionDescriptionInterface* answer =
@@ -2885,11 +2985,11 @@
 
   // This should lead to an audio-only call but isn't implemented
   // correctly yet.
-  EXPECT_EQ(session_->GetTransportProxy("audio")->impl(),
-            session_->GetTransportProxy("video")->impl());
+  EXPECT_EQ(session_->voice_rtp_transport_channel(),
+            session_->video_rtp_transport_channel());
 }
 
-// kBundlePolicyMaxCompat bundle policy but no BUNDLE in the answer.
+// kBundlePolicyMaxCompat BUNDLE policy but no BUNDLE in the answer.
 TEST_F(WebRtcSessionTest, TestMaxCompatNoBundleInAnswer) {
   InitWithBundlePolicy(PeerConnectionInterface::kBundlePolicyMaxCompat);
   mediastream_signaling_.SendAudioVideoStream1();
@@ -2899,8 +2999,8 @@
   SessionDescriptionInterface* offer = CreateOffer(options);
   SetLocalDescriptionWithoutError(offer);
 
-  EXPECT_NE(session_->GetTransportProxy("audio")->impl(),
-            session_->GetTransportProxy("video")->impl());
+  EXPECT_NE(session_->voice_rtp_transport_channel(),
+            session_->video_rtp_transport_channel());
 
   mediastream_signaling_.SendAudioVideoStream2();
 
@@ -2914,8 +3014,8 @@
   modified_answer->Initialize(answer_copy, "1", "1");
   SetRemoteDescriptionWithoutError(modified_answer);  //
 
-  EXPECT_NE(session_->GetTransportProxy("audio")->impl(),
-            session_->GetTransportProxy("video")->impl());
+  EXPECT_NE(session_->voice_rtp_transport_channel(),
+            session_->video_rtp_transport_channel());
 }
 
 // kBundlePolicyMaxbundle and then we call SetRemoteDescription first.
@@ -2929,8 +3029,8 @@
   SessionDescriptionInterface* offer = CreateOffer(options);
   SetRemoteDescriptionWithoutError(offer);
 
-  EXPECT_EQ(session_->GetTransportProxy("audio")->impl(),
-            session_->GetTransportProxy("video")->impl());
+  EXPECT_EQ(session_->voice_rtp_transport_channel(),
+            session_->video_rtp_transport_channel());
 }
 
 TEST_F(WebRtcSessionTest, TestRequireRtcpMux) {
@@ -2941,16 +3041,16 @@
   SessionDescriptionInterface* offer = CreateOffer(options);
   SetLocalDescriptionWithoutError(offer);
 
-  EXPECT_FALSE(session_->GetTransportProxy("audio")->impl()->HasChannel(2));
-  EXPECT_FALSE(session_->GetTransportProxy("video")->impl()->HasChannel(2));
+  EXPECT_TRUE(session_->voice_rtcp_transport_channel() == NULL);
+  EXPECT_TRUE(session_->video_rtcp_transport_channel() == NULL);
 
   mediastream_signaling_.SendAudioVideoStream2();
   SessionDescriptionInterface* answer =
       CreateRemoteAnswer(session_->local_description());
   SetRemoteDescriptionWithoutError(answer);
 
-  EXPECT_FALSE(session_->GetTransportProxy("audio")->impl()->HasChannel(2));
-  EXPECT_FALSE(session_->GetTransportProxy("video")->impl()->HasChannel(2));
+  EXPECT_TRUE(session_->voice_rtcp_transport_channel() == NULL);
+  EXPECT_TRUE(session_->video_rtcp_transport_channel() == NULL);
 }
 
 TEST_F(WebRtcSessionTest, TestNegotiateRtcpMux) {
@@ -2961,16 +3061,16 @@
   SessionDescriptionInterface* offer = CreateOffer(options);
   SetLocalDescriptionWithoutError(offer);
 
-  EXPECT_TRUE(session_->GetTransportProxy("audio")->impl()->HasChannel(2));
-  EXPECT_TRUE(session_->GetTransportProxy("video")->impl()->HasChannel(2));
+  EXPECT_TRUE(session_->voice_rtcp_transport_channel() != NULL);
+  EXPECT_TRUE(session_->video_rtcp_transport_channel() != NULL);
 
   mediastream_signaling_.SendAudioVideoStream2();
   SessionDescriptionInterface* answer =
       CreateRemoteAnswer(session_->local_description());
   SetRemoteDescriptionWithoutError(answer);
 
-  EXPECT_FALSE(session_->GetTransportProxy("audio")->impl()->HasChannel(2));
-  EXPECT_FALSE(session_->GetTransportProxy("video")->impl()->HasChannel(2));
+  EXPECT_TRUE(session_->voice_rtcp_transport_channel() == NULL);
+  EXPECT_TRUE(session_->video_rtcp_transport_channel() == NULL);
 }
 
 // This test verifies that SetLocalDescription and SetRemoteDescription fails
@@ -2991,11 +3091,11 @@
   rtc::replace_substrs(rtcp_mux.c_str(), rtcp_mux.length(),
                              xrtcp_mux.c_str(), xrtcp_mux.length(),
                              &offer_str);
-  JsepSessionDescription *local_offer =
+  JsepSessionDescription* local_offer =
       new JsepSessionDescription(JsepSessionDescription::kOffer);
   EXPECT_TRUE((local_offer)->Initialize(offer_str, NULL));
   SetLocalDescriptionOfferExpectError(kBundleWithoutRtcpMux, local_offer);
-  JsepSessionDescription *remote_offer =
+  JsepSessionDescription* remote_offer =
       new JsepSessionDescription(JsepSessionDescription::kOffer);
   EXPECT_TRUE((remote_offer)->Initialize(offer_str, NULL));
   SetRemoteDescriptionOfferExpectError(kBundleWithoutRtcpMux, remote_offer);
@@ -3258,8 +3358,8 @@
                                   candidate1);
   EXPECT_TRUE(offer->AddCandidate(&ice_candidate1));
   SetRemoteDescriptionWithoutError(offer);
-  ASSERT_TRUE(session_->GetTransportProxy("audio") != NULL);
-  ASSERT_TRUE(session_->GetTransportProxy("video") != NULL);
+  ASSERT_TRUE(session_->voice_rtp_transport_channel() != NULL);
+  ASSERT_TRUE(session_->video_rtp_transport_channel() != NULL);
 
   // Pump for 1 second and verify that no candidates are generated.
   rtc::Thread::Current()->ProcessMessages(1000);
@@ -3268,8 +3368,6 @@
 
   SessionDescriptionInterface* answer = CreateAnswer(NULL);
   SetLocalDescriptionWithoutError(answer);
-  EXPECT_TRUE(session_->GetTransportProxy("audio")->negotiated());
-  EXPECT_TRUE(session_->GetTransportProxy("video")->negotiated());
   EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout);
 }
 
@@ -3304,7 +3402,7 @@
   // will be set as per MediaSessionDescriptionFactory.
   std::string offer_str;
   offer->ToString(&offer_str);
-  SessionDescriptionInterface *jsep_offer_str =
+  SessionDescriptionInterface* jsep_offer_str =
       CreateSessionDescription(JsepSessionDescription::kOffer, offer_str, NULL);
   SetLocalDescriptionWithoutError(jsep_offer_str);
   EXPECT_FALSE(session_->voice_channel()->secure_required());
@@ -3657,8 +3755,8 @@
 TEST_P(WebRtcSessionTest,
        TestMultipleCreateOfferBeforeIdentityRequestReturnSuccess) {
   MAYBE_SKIP_TEST(rtc::SSLStreamAdapter::HaveDtlsSrtp);
-  VerifyMultipleAsyncCreateDescription(
-      GetParam(), CreateSessionDescriptionRequest::kOffer);
+  VerifyMultipleAsyncCreateDescription(GetParam(),
+                                       CreateSessionDescriptionRequest::kOffer);
 }
 
 // Verifies that CreateOffer fails when Multiple CreateOffer calls are made
@@ -3881,31 +3979,31 @@
                                        rtc::Socket::Option::OPT_RCVBUF, 8000);
 
   int option_val;
-  EXPECT_TRUE(session_->video_channel()->transport_channel()->GetOption(
+  EXPECT_TRUE(session_->video_rtp_transport_channel()->GetOption(
       rtc::Socket::Option::OPT_SNDBUF, &option_val));
   EXPECT_EQ(4000, option_val);
-  EXPECT_FALSE(session_->voice_channel()->transport_channel()->GetOption(
+  EXPECT_FALSE(session_->voice_rtp_transport_channel()->GetOption(
       rtc::Socket::Option::OPT_SNDBUF, &option_val));
 
-  EXPECT_TRUE(session_->voice_channel()->transport_channel()->GetOption(
+  EXPECT_TRUE(session_->voice_rtp_transport_channel()->GetOption(
       rtc::Socket::Option::OPT_RCVBUF, &option_val));
   EXPECT_EQ(8000, option_val);
-  EXPECT_FALSE(session_->video_channel()->transport_channel()->GetOption(
+  EXPECT_FALSE(session_->video_rtp_transport_channel()->GetOption(
       rtc::Socket::Option::OPT_RCVBUF, &option_val));
 
-  EXPECT_NE(session_->voice_channel()->transport_channel(),
-            session_->video_channel()->transport_channel());
+  EXPECT_NE(session_->voice_rtp_transport_channel(),
+            session_->video_rtp_transport_channel());
 
   mediastream_signaling_.SendAudioVideoStream2();
   SessionDescriptionInterface* answer =
       CreateRemoteAnswer(session_->local_description());
   SetRemoteDescriptionWithoutError(answer);
 
-  EXPECT_TRUE(session_->voice_channel()->transport_channel()->GetOption(
+  EXPECT_TRUE(session_->voice_rtp_transport_channel()->GetOption(
       rtc::Socket::Option::OPT_SNDBUF, &option_val));
   EXPECT_EQ(4000, option_val);
 
-  EXPECT_TRUE(session_->voice_channel()->transport_channel()->GetOption(
+  EXPECT_TRUE(session_->voice_rtp_transport_channel()->GetOption(
       rtc::Socket::Option::OPT_RCVBUF, &option_val));
   EXPECT_EQ(8000, option_val);
 }
@@ -3941,6 +4039,7 @@
 // currently fails because upon disconnection and reconnection OnIceComplete is
 // called more than once without returning to IceGatheringGathering.
 
-INSTANTIATE_TEST_CASE_P(
-    WebRtcSessionTests, WebRtcSessionTest,
-    testing::Values(ALREADY_GENERATED, DTLS_IDENTITY_STORE));
+INSTANTIATE_TEST_CASE_P(WebRtcSessionTests,
+                        WebRtcSessionTest,
+                        testing::Values(ALREADY_GENERATED,
+                                        DTLS_IDENTITY_STORE));
diff --git a/talk/app/webrtc/webrtcsessiondescriptionfactory.cc b/talk/app/webrtc/webrtcsessiondescriptionfactory.cc
index caa53df..f6414d3 100644
--- a/talk/app/webrtc/webrtcsessiondescriptionfactory.cc
+++ b/talk/app/webrtc/webrtcsessiondescriptionfactory.cc
@@ -165,9 +165,15 @@
     WebRtcSession* session,
     const std::string& session_id,
     cricket::DataChannelType dct)
-    : WebRtcSessionDescriptionFactory(
-        signaling_thread, channel_manager, mediastream_signaling, nullptr,
-        nullptr, session, session_id, dct, false) {
+    : WebRtcSessionDescriptionFactory(signaling_thread,
+                                      channel_manager,
+                                      mediastream_signaling,
+                                      nullptr,
+                                      nullptr,
+                                      session,
+                                      session_id,
+                                      dct,
+                                      false) {
   LOG(LS_VERBOSE) << "DTLS-SRTP disabled.";
 }
 
@@ -226,9 +232,9 @@
   // We already have a certificate but we wait to do SetIdentity; if we do
   // it in the constructor then the caller has not had a chance to connect to
   // SignalIdentityReady.
-  signaling_thread_->Post(this, MSG_USE_CONSTRUCTOR_CERTIFICATE,
-                          new rtc::ScopedRefMessageData<rtc::RTCCertificate>(
-                              certificate));
+  signaling_thread_->Post(
+      this, MSG_USE_CONSTRUCTOR_CERTIFICATE,
+      new rtc::ScopedRefMessageData<rtc::RTCCertificate>(certificate));
 }
 
 WebRtcSessionDescriptionFactory::~WebRtcSessionDescriptionFactory() {
@@ -254,8 +260,6 @@
       delete msg.pdata;
     }
   }
-
-  transport_desc_factory_.set_certificate(nullptr);
 }
 
 void WebRtcSessionDescriptionFactory::CreateOffer(
diff --git a/talk/app/webrtc/webrtcsessiondescriptionfactory.h b/talk/app/webrtc/webrtcsessiondescriptionfactory.h
index b42a551..52b8da5 100644
--- a/talk/app/webrtc/webrtcsessiondescriptionfactory.h
+++ b/talk/app/webrtc/webrtcsessiondescriptionfactory.h
@@ -90,13 +90,12 @@
                                         public sigslot::has_slots<> {
  public:
   // Construct with DTLS disabled.
-  WebRtcSessionDescriptionFactory(
-      rtc::Thread* signaling_thread,
-      cricket::ChannelManager* channel_manager,
-      MediaStreamSignaling* mediastream_signaling,
-      WebRtcSession* session,
-      const std::string& session_id,
-      cricket::DataChannelType dct);
+  WebRtcSessionDescriptionFactory(rtc::Thread* signaling_thread,
+                                  cricket::ChannelManager* channel_manager,
+                                  MediaStreamSignaling* mediastream_signaling,
+                                  WebRtcSession* session,
+                                  const std::string& session_id,
+                                  cricket::DataChannelType dct);
 
   // Construct with DTLS enabled using the specified |dtls_identity_store| to
   // generate a certificate.
diff --git a/talk/media/webrtc/webrtcvoiceengine_unittest.cc b/talk/media/webrtc/webrtcvoiceengine_unittest.cc
index af4239e..791e7cf 100644
--- a/talk/media/webrtc/webrtcvoiceengine_unittest.cc
+++ b/talk/media/webrtc/webrtcvoiceengine_unittest.cc
@@ -35,7 +35,7 @@
 #include "talk/media/webrtc/fakewebrtccall.h"
 #include "talk/media/webrtc/fakewebrtcvoiceengine.h"
 #include "talk/media/webrtc/webrtcvoiceengine.h"
-#include "webrtc/p2p/base/fakesession.h"
+#include "webrtc/p2p/base/faketransportcontroller.h"
 #include "talk/session/media/channel.h"
 
 // Tests for the WebRtcVoiceEngine/VoiceChannel code.
diff --git a/talk/session/media/channel.cc b/talk/session/media/channel.cc
index 3c5b9dd..1405c55 100644
--- a/talk/session/media/channel.cc
+++ b/talk/session/media/channel.cc
@@ -170,15 +170,17 @@
 }
 
 BaseChannel::BaseChannel(rtc::Thread* thread,
-                         MediaChannel* media_channel, BaseSession* session,
-                         const std::string& content_name, bool rtcp)
+                         MediaChannel* media_channel,
+                         TransportController* transport_controller,
+                         const std::string& content_name,
+                         bool rtcp)
     : worker_thread_(thread),
-      session_(session),
+      transport_controller_(transport_controller),
       media_channel_(media_channel),
       content_name_(content_name),
-      rtcp_(rtcp),
-      transport_channel_(NULL),
-      rtcp_transport_channel_(NULL),
+      rtcp_transport_enabled_(rtcp),
+      transport_channel_(nullptr),
+      rtcp_transport_channel_(nullptr),
       enabled_(false),
       writable_(false),
       rtp_ready_to_send_(false),
@@ -204,20 +206,31 @@
   // the media channel may try to send on the dead transport channel. NULLing
   // is not an effective strategy since the sends will come on another thread.
   delete media_channel_;
-  set_transport_channel(nullptr);
-  set_rtcp_transport_channel(nullptr);
+  // Note that we don't just call set_transport_channel(nullptr) because that
+  // would call a pure virtual method which we can't do from a destructor.
+  if (transport_channel_) {
+    DisconnectFromTransportChannel(transport_channel_);
+    transport_controller_->DestroyTransportChannel_w(
+        transport_name_, cricket::ICE_CANDIDATE_COMPONENT_RTP);
+  }
+  if (rtcp_transport_channel_) {
+    DisconnectFromTransportChannel(rtcp_transport_channel_);
+    transport_controller_->DestroyTransportChannel_w(
+        transport_name_, cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+  }
   LOG(LS_INFO) << "Destroyed channel";
 }
 
 bool BaseChannel::Init() {
-  if (!SetTransportChannels(session(), rtcp())) {
+  if (!SetTransport(content_name())) {
     return false;
   }
 
   if (!SetDtlsSrtpCiphers(transport_channel(), false)) {
     return false;
   }
-  if (rtcp() && !SetDtlsSrtpCiphers(rtcp_transport_channel(), true)) {
+  if (rtcp_transport_enabled() &&
+      !SetDtlsSrtpCiphers(rtcp_transport_channel(), true)) {
     return false;
   }
 
@@ -231,29 +244,35 @@
   media_channel_->SetInterface(NULL);
 }
 
-bool BaseChannel::SetTransportChannels(BaseSession* session, bool rtcp) {
-  return worker_thread_->Invoke<bool>(Bind(
-      &BaseChannel::SetTransportChannels_w, this, session, rtcp));
+bool BaseChannel::SetTransport(const std::string& transport_name) {
+  return worker_thread_->Invoke<bool>(
+      Bind(&BaseChannel::SetTransport_w, this, transport_name));
 }
 
-bool BaseChannel::SetTransportChannels_w(BaseSession* session, bool rtcp) {
+bool BaseChannel::SetTransport_w(const std::string& transport_name) {
   ASSERT(worker_thread_ == rtc::Thread::Current());
 
-  set_transport_channel(session->CreateChannel(
-      content_name(), cricket::ICE_CANDIDATE_COMPONENT_RTP));
+  if (transport_name == transport_name_) {
+    // Nothing to do if transport name isn't changing
+    return true;
+  }
+
+  set_transport_channel(transport_controller_->CreateTransportChannel_w(
+      transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTP));
   if (!transport_channel()) {
     return false;
   }
-  if (rtcp) {
-    set_rtcp_transport_channel(session->CreateChannel(
-        content_name(), cricket::ICE_CANDIDATE_COMPONENT_RTCP));
+  if (rtcp_transport_enabled()) {
+    LOG(LS_INFO) << "Create RTCP TransportChannel for " << content_name()
+                 << " on " << transport_name << " transport ";
+    set_rtcp_transport_channel(transport_controller_->CreateTransportChannel_w(
+        transport_name, cricket::ICE_CANDIDATE_COMPONENT_RTCP));
     if (!rtcp_transport_channel()) {
       return false;
     }
-  } else {
-    set_rtcp_transport_channel(nullptr);
   }
 
+  transport_name_ = transport_name;
   return true;
 }
 
@@ -261,42 +280,62 @@
   ASSERT(worker_thread_ == rtc::Thread::Current());
 
   TransportChannel* old_tc = transport_channel_;
-
-  if (old_tc == new_tc) {
+  if (!old_tc && !new_tc) {
+    // Nothing to do
     return;
   }
+  ASSERT(old_tc != new_tc);
+
   if (old_tc) {
     DisconnectFromTransportChannel(old_tc);
-    session()->DestroyChannel(
-        content_name(), cricket::ICE_CANDIDATE_COMPONENT_RTP);
+    transport_controller_->DestroyTransportChannel_w(
+        transport_name_, cricket::ICE_CANDIDATE_COMPONENT_RTP);
   }
 
   transport_channel_ = new_tc;
 
   if (new_tc) {
     ConnectToTransportChannel(new_tc);
+    for (const auto& pair : socket_options_) {
+      new_tc->SetOption(pair.first, pair.second);
+    }
   }
+
+  // Update aggregate writable/ready-to-send state between RTP and RTCP upon
+  // setting new channel
+  UpdateWritableState_w();
+  SetReadyToSend(false, new_tc && new_tc->writable());
 }
 
 void BaseChannel::set_rtcp_transport_channel(TransportChannel* new_tc) {
   ASSERT(worker_thread_ == rtc::Thread::Current());
 
   TransportChannel* old_tc = rtcp_transport_channel_;
-
-  if (old_tc == new_tc) {
+  if (!old_tc && !new_tc) {
+    // Nothing to do
     return;
   }
+  ASSERT(old_tc != new_tc);
+
   if (old_tc) {
     DisconnectFromTransportChannel(old_tc);
-    session()->DestroyChannel(
-        content_name(), cricket::ICE_CANDIDATE_COMPONENT_RTCP);
+    transport_controller_->DestroyTransportChannel_w(
+        transport_name_, cricket::ICE_CANDIDATE_COMPONENT_RTCP);
   }
 
   rtcp_transport_channel_ = new_tc;
 
   if (new_tc) {
     ConnectToTransportChannel(new_tc);
+    for (const auto& pair : rtcp_socket_options_) {
+      new_tc->SetOption(pair.first, pair.second);
+    }
   }
+
+  // Update aggregate writable/ready-to-send state between RTP and RTCP upon
+  // setting new channel
+  UpdateWritableState_w();
+  SetReadyToSend(true, new_tc && new_tc->writable());
 }
 
 void BaseChannel::ConnectToTransportChannel(TransportChannel* tc) {
@@ -407,9 +446,13 @@
   switch (type) {
     case ST_RTP:
       channel = transport_channel_;
+      socket_options_.push_back(
+          std::pair<rtc::Socket::Option, int>(opt, value));
       break;
     case ST_RTCP:
       channel = rtcp_transport_channel_;
+      rtcp_socket_options_.push_back(
+          std::pair<rtc::Socket::Option, int>(opt, value));
       break;
   }
   return channel ? channel->SetOption(opt, value) : -1;
@@ -417,12 +460,7 @@
 
 void BaseChannel::OnWritableState(TransportChannel* channel) {
   ASSERT(channel == transport_channel_ || channel == rtcp_transport_channel_);
-  if (transport_channel_->writable()
-      && (!rtcp_transport_channel_ || rtcp_transport_channel_->writable())) {
-    ChannelWritable_w();
-  } else {
-    ChannelNotWritable_w();
-  }
+  UpdateWritableState_w();
 }
 
 void BaseChannel::OnChannelRead(TransportChannel* channel,
@@ -440,26 +478,25 @@
 }
 
 void BaseChannel::OnReadyToSend(TransportChannel* channel) {
-  SetReadyToSend(channel, true);
+  ASSERT(channel == transport_channel_ || channel == rtcp_transport_channel_);
+  SetReadyToSend(channel == rtcp_transport_channel_, true);
 }
 
-void BaseChannel::SetReadyToSend(TransportChannel* channel, bool ready) {
-  ASSERT(channel == transport_channel_ || channel == rtcp_transport_channel_);
-  if (channel == transport_channel_) {
+void BaseChannel::SetReadyToSend(bool rtcp, bool ready) {
+  if (rtcp) {
+    rtcp_ready_to_send_ = ready;
+  } else {
     rtp_ready_to_send_ = ready;
   }
-  if (channel == rtcp_transport_channel_) {
-    rtcp_ready_to_send_ = ready;
-  }
 
-  if (!ready) {
-    // Notify the MediaChannel when either rtp or rtcp channel can't send.
-    media_channel_->OnReadyToSend(false);
-  } else if (rtp_ready_to_send_ &&
-             // In the case of rtcp mux |rtcp_transport_channel_| will be null.
-             (rtcp_ready_to_send_ || !rtcp_transport_channel_)) {
+  if (rtp_ready_to_send_ &&
+      // In the case of rtcp mux |rtcp_transport_channel_| will be null.
+      (rtcp_ready_to_send_ || !rtcp_transport_channel_)) {
     // Notify the MediaChannel when both rtp and rtcp channel can send.
     media_channel_->OnReadyToSend(true);
+  } else {
+    // Notify the MediaChannel when either rtp or rtcp channel can't send.
+    media_channel_->OnReadyToSend(false);
   }
 }
 
@@ -581,7 +618,7 @@
   if (ret != static_cast<int>(packet->size())) {
     if (channel->GetError() == EWOULDBLOCK) {
       LOG(LS_WARNING) << "Got EWOULDBLOCK from socket.";
-      SetReadyToSend(channel, false);
+      SetReadyToSend(rtcp, false);
     }
     return false;
   }
@@ -715,14 +752,21 @@
   ChangeState();
 }
 
+void BaseChannel::UpdateWritableState_w() {
+  if (transport_channel_ && transport_channel_->writable() &&
+      (!rtcp_transport_channel_ || rtcp_transport_channel_->writable())) {
+    ChannelWritable_w();
+  } else {
+    ChannelNotWritable_w();
+  }
+}
+
 void BaseChannel::ChannelWritable_w() {
   ASSERT(worker_thread_ == rtc::Thread::Current());
   if (writable_)
     return;
 
-  LOG(LS_INFO) << "Channel socket writable ("
-               << transport_channel_->content_name() << ", "
-               << transport_channel_->component() << ")"
+  LOG(LS_INFO) << "Channel writable (" << content_name_ << ")"
                << (was_ever_writable_ ? "" : " for the first time");
 
   std::vector<ConnectionInfo> infos;
@@ -739,13 +783,13 @@
   // If we're doing DTLS-SRTP, now is the time.
   if (!was_ever_writable_ && ShouldSetupDtlsSrtp()) {
     if (!SetupDtlsSrtp(false)) {
-      SignalDtlsSetupFailure(this, false);
+      SignalDtlsSetupFailure_w(false);
       return;
     }
 
     if (rtcp_transport_channel_) {
       if (!SetupDtlsSrtp(true)) {
-        SignalDtlsSetupFailure(this, true);
+        SignalDtlsSetupFailure_w(true);
         return;
       }
     }
@@ -788,8 +832,8 @@
 bool BaseChannel::SetupDtlsSrtp(bool rtcp_channel) {
   bool ret = false;
 
-  TransportChannel *channel = rtcp_channel ?
-      rtcp_transport_channel_ : transport_channel_;
+  TransportChannel* channel =
+      rtcp_channel ? rtcp_transport_channel_ : transport_channel_;
 
   // No DTLS
   if (!channel->IsDtlsActive())
@@ -884,9 +928,7 @@
   if (!writable_)
     return;
 
-  LOG(LS_INFO) << "Channel socket not writable ("
-               << transport_channel_->content_name() << ", "
-               << transport_channel_->component() << ")";
+  LOG(LS_INFO) << "Channel not writable (" << content_name_ << ")";
   writable_ = false;
   ChangeState();
 }
@@ -985,7 +1027,8 @@
 void BaseChannel::ActivateRtcpMux_w() {
   if (!rtcp_mux_filter_.IsActive()) {
     rtcp_mux_filter_.SetActive();
-    set_rtcp_transport_channel(NULL);
+    set_rtcp_transport_channel(nullptr);
+    rtcp_transport_enabled_ = false;
   }
 }
 
@@ -1004,7 +1047,11 @@
       ret = rtcp_mux_filter_.SetAnswer(enable, src);
       if (ret && rtcp_mux_filter_.IsActive()) {
         // We activated RTCP mux, close down the RTCP transport.
-        set_rtcp_transport_channel(NULL);
+        LOG(LS_INFO) << "Enabling rtcp-mux for " << content_name()
+                     << " by destroying RTCP transport channel for "
+                     << transport_name();
+        set_rtcp_transport_channel(nullptr);
+        rtcp_transport_enabled_ = false;
       }
       break;
     case CA_UPDATE:
@@ -1231,14 +1278,16 @@
 VoiceChannel::VoiceChannel(rtc::Thread* thread,
                            MediaEngineInterface* media_engine,
                            VoiceMediaChannel* media_channel,
-                           BaseSession* session,
+                           TransportController* transport_controller,
                            const std::string& content_name,
                            bool rtcp)
-    : BaseChannel(thread, media_channel, session, content_name,
+    : BaseChannel(thread,
+                  media_channel,
+                  transport_controller,
+                  content_name,
                   rtcp),
       media_engine_(media_engine),
-      received_media_(false) {
-}
+      received_media_(false) {}
 
 VoiceChannel::~VoiceChannel() {
   StopAudioMonitor();
@@ -1264,11 +1313,12 @@
                              media_channel(), ssrc, renderer));
 }
 
-bool VoiceChannel::SetAudioSend(uint32 ssrc, bool mute,
+bool VoiceChannel::SetAudioSend(uint32 ssrc,
+                                bool mute,
                                 const AudioOptions* options,
                                 AudioRenderer* renderer) {
-  return InvokeOnWorker(Bind(&VoiceMediaChannel::SetAudioSend,
-                             media_channel(), ssrc, mute, options, renderer));
+  return InvokeOnWorker(Bind(&VoiceMediaChannel::SetAudioSend, media_channel(),
+                             ssrc, mute, options, renderer));
 }
 
 // TODO(juberti): Handle early media the right way. We should get an explicit
@@ -1583,14 +1633,16 @@
 
 VideoChannel::VideoChannel(rtc::Thread* thread,
                            VideoMediaChannel* media_channel,
-                           BaseSession* session,
+                           TransportController* transport_controller,
                            const std::string& content_name,
                            bool rtcp)
-    : BaseChannel(thread, media_channel, session, content_name,
+    : BaseChannel(thread,
+                  media_channel,
+                  transport_controller,
+                  content_name,
                   rtcp),
       renderer_(NULL),
-      previous_we_(rtc::WE_CLOSE) {
-}
+      previous_we_(rtc::WE_CLOSE) {}
 
 bool VideoChannel::Init() {
   if (!BaseChannel::Init()) {
@@ -1683,10 +1735,11 @@
   return true;
 }
 
-bool VideoChannel::SetVideoSend(uint32 ssrc, bool mute,
+bool VideoChannel::SetVideoSend(uint32 ssrc,
+                                bool mute,
                                 const VideoOptions* options) {
-  return InvokeOnWorker(Bind(&VideoMediaChannel::SetVideoSend,
-                             media_channel(), ssrc, mute, options));
+  return InvokeOnWorker(Bind(&VideoMediaChannel::SetVideoSend, media_channel(),
+                             ssrc, mute, options));
 }
 
 void VideoChannel::ChangeState() {
@@ -2021,13 +2074,16 @@
 
 DataChannel::DataChannel(rtc::Thread* thread,
                          DataMediaChannel* media_channel,
-                         BaseSession* session,
+                         TransportController* transport_controller,
                          const std::string& content_name,
                          bool rtcp)
-    : BaseChannel(thread, media_channel, session, content_name, rtcp),
+    : BaseChannel(thread,
+                  media_channel,
+                  transport_controller,
+                  content_name,
+                  rtcp),
       data_channel_type_(cricket::DCT_NONE),
-      ready_to_send_data_(false) {
-}
+      ready_to_send_data_(false) {}
 
 DataChannel::~DataChannel() {
   StopMediaMonitor();
diff --git a/talk/session/media/channel.h b/talk/session/media/channel.h
index ec35f4c..0a7ce9d1 100644
--- a/talk/session/media/channel.h
+++ b/talk/session/media/channel.h
@@ -30,12 +30,15 @@
 
 #include <string>
 #include <vector>
+#include <map>
+#include <set>
+#include <utility>
 
 #include "talk/media/base/mediachannel.h"
 #include "talk/media/base/mediaengine.h"
 #include "talk/media/base/streamparams.h"
 #include "talk/media/base/videocapturer.h"
-#include "webrtc/p2p/base/session.h"
+#include "webrtc/p2p/base/transportcontroller.h"
 #include "webrtc/p2p/client/socketmonitor.h"
 #include "talk/session/media/audiomonitor.h"
 #include "talk/session/media/bundlefilter.h"
@@ -74,8 +77,11 @@
       public MediaChannel::NetworkInterface,
       public ConnectionStatsGetter {
  public:
-  BaseChannel(rtc::Thread* thread, MediaChannel* channel, BaseSession* session,
-              const std::string& content_name, bool rtcp);
+  BaseChannel(rtc::Thread* thread,
+              MediaChannel* channel,
+              TransportController* transport_controller,
+              const std::string& content_name,
+              bool rtcp);
   virtual ~BaseChannel();
   bool Init();
   // Deinit may be called multiple times and is simply ignored if it's alreay
@@ -83,8 +89,8 @@
   void Deinit();
 
   rtc::Thread* worker_thread() const { return worker_thread_; }
-  BaseSession* session() const { return session_; }
-  const std::string& content_name() { return content_name_; }
+  const std::string& content_name() const { return content_name_; }
+  const std::string& transport_name() const { return transport_name_; }
   TransportChannel* transport_channel() const {
     return transport_channel_;
   }
@@ -109,6 +115,7 @@
   // description doesn't support RTCP mux, setting the remote
   // description will fail.
   void ActivateRtcpMux();
+  bool SetTransport(const std::string& transport_name);
   bool PushdownLocalDescription(const SessionDescription* local_desc,
                                 ContentAction action,
                                 std::string* error_desc);
@@ -135,7 +142,7 @@
   void StartConnectionMonitor(int cms);
   void StopConnectionMonitor();
   // For ConnectionStatsGetter, used by ConnectionMonitor
-  virtual bool GetConnectionStats(ConnectionInfos* infos) override;
+  bool GetConnectionStats(ConnectionInfos* infos) override;
 
   void set_srtp_signal_silent_time(uint32 silent_time) {
     srtp_filter_.set_signal_silent_time(silent_time);
@@ -158,19 +165,16 @@
   sigslot::signal1<BaseChannel*> SignalFirstPacketReceived;
 
   // Made public for easier testing.
-  void SetReadyToSend(TransportChannel* channel, bool ready);
+  void SetReadyToSend(bool rtcp, bool ready);
 
   // Only public for unit tests.  Otherwise, consider protected.
   virtual int SetOption(SocketType type, rtc::Socket::Option o, int val);
 
  protected:
   virtual MediaChannel* media_channel() const { return media_channel_; }
-  // Sets the transport_channel_ and rtcp_transport_channel_.  If
-  // |rtcp| is false, set rtcp_transport_channel_ is set to NULL.  Get
-  // the transport channels from |session|.
-  // TODO(pthatcher): Pass in a Transport instead of a BaseSession.
-  bool SetTransportChannels(BaseSession* session, bool rtcp);
-  bool SetTransportChannels_w(BaseSession* session, bool rtcp);
+  // Sets the |transport_channel_| (and |rtcp_transport_channel_|, if |rtcp_| is
+  // true). Gets the transport channels from |transport_controller_|.
+  bool SetTransport_w(const std::string& transport_name);
   void set_transport_channel(TransportChannel* transport);
   void set_rtcp_transport_channel(TransportChannel* transport);
   bool was_ever_writable() const { return was_ever_writable_; }
@@ -185,9 +189,11 @@
   }
   bool IsReadyToReceive() const;
   bool IsReadyToSend() const;
-  rtc::Thread* signaling_thread() { return session_->signaling_thread(); }
+  rtc::Thread* signaling_thread() {
+    return transport_controller_->signaling_thread();
+  }
   SrtpFilter* srtp_filter() { return &srtp_filter_; }
-  bool rtcp() const { return rtcp_; }
+  bool rtcp_transport_enabled() const { return rtcp_transport_enabled_; }
 
   void ConnectToTransportChannel(TransportChannel* tc);
   void DisconnectFromTransportChannel(TransportChannel* tc);
@@ -217,12 +223,9 @@
   void HandlePacket(bool rtcp, rtc::Buffer* packet,
                     const rtc::PacketTime& packet_time);
 
-  // Apply the new local/remote session description.
-  void OnNewLocalDescription(BaseSession* session, ContentAction action);
-  void OnNewRemoteDescription(BaseSession* session, ContentAction action);
-
   void EnableMedia_w();
   void DisableMedia_w();
+  void UpdateWritableState_w();
   void ChannelWritable_w();
   void ChannelNotWritable_w();
   bool AddRecvStream_w(const StreamParams& sp);
@@ -293,15 +296,18 @@
 
  private:
   rtc::Thread* worker_thread_;
-  BaseSession* session_;
+  TransportController* transport_controller_;
   MediaChannel* media_channel_;
   std::vector<StreamParams> local_streams_;
   std::vector<StreamParams> remote_streams_;
 
   const std::string content_name_;
-  bool rtcp_;
+  std::string transport_name_;
+  bool rtcp_transport_enabled_;
   TransportChannel* transport_channel_;
+  std::vector<std::pair<rtc::Socket::Option, int> > socket_options_;
   TransportChannel* rtcp_transport_channel_;
+  std::vector<std::pair<rtc::Socket::Option, int> > rtcp_socket_options_;
   SrtpFilter srtp_filter_;
   RtcpMuxFilter rtcp_mux_filter_;
   BundleFilter bundle_filter_;
@@ -323,16 +329,21 @@
 // and input/output level monitoring.
 class VoiceChannel : public BaseChannel {
  public:
-  VoiceChannel(rtc::Thread* thread, MediaEngineInterface* media_engine,
-               VoiceMediaChannel* channel, BaseSession* session,
-               const std::string& content_name, bool rtcp);
+  VoiceChannel(rtc::Thread* thread,
+               MediaEngineInterface* media_engine,
+               VoiceMediaChannel* channel,
+               TransportController* transport_controller,
+               const std::string& content_name,
+               bool rtcp);
   ~VoiceChannel();
   bool Init();
   bool SetRemoteRenderer(uint32 ssrc, AudioRenderer* renderer);
 
   // Configure sending media on the stream with SSRC |ssrc|
   // If there is only one sending stream SSRC 0 can be used.
-  bool SetAudioSend(uint32 ssrc, bool mute, const AudioOptions* options,
+  bool SetAudioSend(uint32 ssrc,
+                    bool mute,
+                    const AudioOptions* options,
                     AudioRenderer* renderer);
 
   // downcasts a MediaChannel
@@ -429,8 +440,10 @@
 // VideoChannel is a specialization for video.
 class VideoChannel : public BaseChannel {
  public:
-  VideoChannel(rtc::Thread* thread, VideoMediaChannel* channel,
-               BaseSession* session, const std::string& content_name,
+  VideoChannel(rtc::Thread* thread,
+               VideoMediaChannel* channel,
+               TransportController* transport_controller,
+               const std::string& content_name,
                bool rtcp);
   ~VideoChannel();
   bool Init();
@@ -529,7 +542,7 @@
  public:
   DataChannel(rtc::Thread* thread,
               DataMediaChannel* media_channel,
-              BaseSession* session,
+              TransportController* transport_controller,
               const std::string& content_name,
               bool rtcp);
   ~DataChannel();
diff --git a/talk/session/media/channel_unittest.cc b/talk/session/media/channel_unittest.cc
index 22b1aef..d6e259d 100644
--- a/talk/session/media/channel_unittest.cc
+++ b/talk/session/media/channel_unittest.cc
@@ -33,7 +33,7 @@
 #include "talk/media/base/rtpdump.h"
 #include "talk/media/base/screencastid.h"
 #include "talk/media/base/testutils.h"
-#include "webrtc/p2p/base/fakesession.h"
+#include "webrtc/p2p/base/faketransportcontroller.h"
 #include "talk/session/media/channel.h"
 #include "webrtc/base/fileutils.h"
 #include "webrtc/base/gunit.h"
@@ -73,12 +73,12 @@
 static const int kAudioPts[] = {0, 8};
 static const int kVideoPts[] = {97, 99};
 
-template<class ChannelT,
-         class MediaChannelT,
-         class ContentT,
-         class CodecT,
-         class MediaInfoT,
-         class OptionsT>
+template <class ChannelT,
+          class MediaChannelT,
+          class ContentT,
+          class CodecT,
+          class MediaInfoT,
+          class OptionsT>
 class Traits {
  public:
   typedef ChannelT Channel;
@@ -98,25 +98,21 @@
                                   cricket::AudioContentDescription,
                                   cricket::AudioCodec,
                                   cricket::VoiceMediaInfo,
-                                  cricket::AudioOptions> {
-};
+                                  cricket::AudioOptions> {};
 
 class VideoTraits : public Traits<cricket::VideoChannel,
                                   cricket::FakeVideoMediaChannel,
                                   cricket::VideoContentDescription,
                                   cricket::VideoCodec,
                                   cricket::VideoMediaInfo,
-                                  cricket::VideoOptions> {
-};
+                                  cricket::VideoOptions> {};
 
 class DataTraits : public Traits<cricket::DataChannel,
                                  cricket::FakeDataMediaChannel,
                                  cricket::DataContentDescription,
                                  cricket::DataCodec,
                                  cricket::DataMediaInfo,
-                                 cricket::DataOptions> {
-};
-
+                                 cricket::DataOptions> {};
 
 rtc::StreamInterface* Open(const std::string& path) {
   return rtc::Filesystem::OpenFile(
@@ -130,10 +126,12 @@
   enum Flags { RTCP = 0x1, RTCP_MUX = 0x2, SECURE = 0x4, SSRC_MUX = 0x8,
                DTLS = 0x10 };
 
-  ChannelTest(const uint8* rtp_data, int rtp_len,
-              const uint8* rtcp_data, int rtcp_len)
-      : session1_(true),
-        session2_(false),
+  ChannelTest(const uint8* rtp_data,
+              int rtp_len,
+              const uint8* rtcp_data,
+              int rtcp_len)
+      : transport_controller1_(cricket::ICEROLE_CONTROLLING),
+        transport_controller2_(cricket::ICEROLE_CONTROLLED),
         media_channel1_(NULL),
         media_channel2_(NULL),
         rtp_packet_(reinterpret_cast<const char*>(rtp_data), rtp_len),
@@ -141,8 +139,7 @@
         media_info_callbacks1_(),
         media_info_callbacks2_(),
         ssrc_(0),
-        error_(T::MediaChannel::ERROR_NONE) {
-  }
+        error_(T::MediaChannel::ERROR_NONE) {}
 
   void CreateChannels(int flags1, int flags2) {
     CreateChannels(new typename T::MediaChannel(NULL, typename T::Options()),
@@ -154,9 +151,11 @@
       int flags1, int flags2, rtc::Thread* thread) {
     media_channel1_ = ch1;
     media_channel2_ = ch2;
-    channel1_.reset(CreateChannel(thread, &media_engine_, ch1, &session1_,
+    channel1_.reset(CreateChannel(thread, &media_engine_, ch1,
+                                  &transport_controller1_,
                                   (flags1 & RTCP) != 0));
-    channel2_.reset(CreateChannel(thread, &media_engine_, ch2, &session2_,
+    channel2_.reset(CreateChannel(thread, &media_engine_, ch2,
+                                  &transport_controller2_,
                                   (flags2 & RTCP) != 0));
     channel1_->SignalMediaMonitor.connect(
         this, &ChannelTest<T>::OnMediaMonitor);
@@ -179,15 +178,17 @@
 
     if (flags1 & DTLS) {
       // Confirmed to work with KT_RSA and KT_ECDSA.
-      session1_.set_ssl_rtccertificate(rtc::RTCCertificate::Create(
-          rtc::scoped_ptr<rtc::SSLIdentity>(rtc::SSLIdentity::Generate(
-              "session1", rtc::KT_DEFAULT)).Pass()));
+      transport_controller1_.SetLocalCertificate(rtc::RTCCertificate::Create(
+          rtc::scoped_ptr<rtc::SSLIdentity>(
+              rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT))
+              .Pass()));
     }
     if (flags2 & DTLS) {
       // Confirmed to work with KT_RSA and KT_ECDSA.
-      session2_.set_ssl_rtccertificate(rtc::RTCCertificate::Create(
-          rtc::scoped_ptr<rtc::SSLIdentity>(rtc::SSLIdentity::Generate(
-              "session2", rtc::KT_DEFAULT)).Pass()));
+      transport_controller2_.SetLocalCertificate(rtc::RTCCertificate::Create(
+          rtc::scoped_ptr<rtc::SSLIdentity>(
+              rtc::SSLIdentity::Generate("session2", rtc::KT_DEFAULT))
+              .Pass()));
     }
 
     // Add stream information (SSRC) to the local content but not to the remote
@@ -204,13 +205,14 @@
       AddLegacyStreamInContent(kSsrc2, flags2, &remote_media_content2_);
     }
   }
-  typename T::Channel* CreateChannel(rtc::Thread* thread,
-                                     cricket::MediaEngineInterface* engine,
-                                     typename T::MediaChannel* ch,
-                                     cricket::BaseSession* session,
-                                     bool rtcp) {
+  typename T::Channel* CreateChannel(
+      rtc::Thread* thread,
+      cricket::MediaEngineInterface* engine,
+      typename T::MediaChannel* ch,
+      cricket::TransportController* transport_controller,
+      bool rtcp) {
     typename T::Channel* channel = new typename T::Channel(
-        thread, engine, ch, session, cricket::CN_AUDIO, rtcp);
+        thread, engine, ch, transport_controller, cricket::CN_AUDIO, rtcp);
     if (!channel->Init()) {
       delete channel;
       channel = NULL;
@@ -226,7 +228,7 @@
       result = channel2_->SetRemoteContent(&remote_media_content1_,
                                            CA_OFFER, NULL);
       if (result) {
-        session1_.Connect(&session2_);
+        transport_controller1_.Connect(&transport_controller2_);
 
         result = channel2_->SetLocalContent(&local_media_content2_,
                                             CA_ANSWER, NULL);
@@ -259,7 +261,7 @@
       channel2_->Enable(true);
       result = channel1_->SetRemoteContent(&remote_media_content2_,
                                            CA_PRANSWER, NULL);
-      session1_.Connect(&session2_);
+      transport_controller1_.Connect(&transport_controller2_);
     }
     return result;
   }
@@ -286,11 +288,12 @@
     return channel1_->RemoveRecvStream(id);
   }
 
+  // Calling "_w" method here is ok since we only use one thread for this test
   cricket::FakeTransport* GetTransport1() {
-    return session1_.GetTransport(channel1_->content_name());
+    return transport_controller1_.GetTransport_w(channel1_->content_name());
   }
   cricket::FakeTransport* GetTransport2() {
-    return session2_.GetTransport(channel2_->content_name());
+    return transport_controller2_.GetTransport_w(channel2_->content_name());
   }
 
   bool SendRtp1() {
@@ -769,7 +772,7 @@
 
     EXPECT_TRUE(channel2_->SetRemoteContent(&content1, CA_OFFER, NULL));
     EXPECT_EQ(1u, media_channel2_->recv_streams().size());
-    session1_.Connect(&session2_);
+    transport_controller1_.Connect(&transport_controller2_);
 
     // Channel 2 do not send anything.
     typename T::Content content2;
@@ -832,7 +835,7 @@
                                            CA_ANSWER, NULL));
     EXPECT_FALSE(media_channel2_->playout());
     EXPECT_FALSE(media_channel2_->sending());
-    session1_.Connect(&session2_);
+    transport_controller1_.Connect(&transport_controller2_);
     EXPECT_TRUE(media_channel1_->playout());
     EXPECT_FALSE(media_channel1_->sending());
     EXPECT_FALSE(media_channel2_->playout());
@@ -868,7 +871,7 @@
     EXPECT_TRUE(channel2_->SetRemoteContent(&content1, CA_OFFER, NULL));
     EXPECT_TRUE(channel2_->SetLocalContent(&content2, CA_PRANSWER, NULL));
     EXPECT_TRUE(channel1_->SetRemoteContent(&content2, CA_PRANSWER, NULL));
-    session1_.Connect(&session2_);
+    transport_controller1_.Connect(&transport_controller2_);
 
     EXPECT_TRUE(media_channel1_->playout());
     EXPECT_FALSE(media_channel1_->sending());  // remote InActive
@@ -938,6 +941,8 @@
     CreateChannels(0, 0);
     EXPECT_TRUE(SendInitiate());
     EXPECT_TRUE(SendAccept());
+    ASSERT_TRUE(GetTransport1());
+    ASSERT_TRUE(GetTransport2());
     EXPECT_EQ(1U, GetTransport1()->channels().size());
     EXPECT_EQ(1U, GetTransport2()->channels().size());
     EXPECT_TRUE(SendRtp1());
@@ -953,6 +958,8 @@
     CreateChannels(0, 0);
     EXPECT_TRUE(SendInitiate());
     EXPECT_TRUE(SendAccept());
+    ASSERT_TRUE(GetTransport1());
+    ASSERT_TRUE(GetTransport2());
     EXPECT_EQ(1U, GetTransport1()->channels().size());
     EXPECT_EQ(1U, GetTransport2()->channels().size());
     EXPECT_FALSE(SendRtcp1());
@@ -966,6 +973,8 @@
     CreateChannels(0, RTCP);
     EXPECT_TRUE(SendInitiate());
     EXPECT_TRUE(SendAccept());
+    ASSERT_TRUE(GetTransport1());
+    ASSERT_TRUE(GetTransport2());
     EXPECT_EQ(1U, GetTransport1()->channels().size());
     EXPECT_EQ(2U, GetTransport2()->channels().size());
     EXPECT_FALSE(SendRtcp1());
@@ -979,6 +988,8 @@
     CreateChannels(RTCP, 0);
     EXPECT_TRUE(SendInitiate());
     EXPECT_TRUE(SendAccept());
+    ASSERT_TRUE(GetTransport1());
+    ASSERT_TRUE(GetTransport2());
     EXPECT_EQ(2U, GetTransport1()->channels().size());
     EXPECT_EQ(1U, GetTransport2()->channels().size());
     EXPECT_FALSE(SendRtcp1());
@@ -992,6 +1003,8 @@
     CreateChannels(RTCP, RTCP);
     EXPECT_TRUE(SendInitiate());
     EXPECT_TRUE(SendAccept());
+    ASSERT_TRUE(GetTransport1());
+    ASSERT_TRUE(GetTransport2());
     EXPECT_EQ(2U, GetTransport1()->channels().size());
     EXPECT_EQ(2U, GetTransport2()->channels().size());
     EXPECT_TRUE(SendRtcp1());
@@ -1007,6 +1020,8 @@
     CreateChannels(RTCP | RTCP_MUX, RTCP);
     EXPECT_TRUE(SendInitiate());
     EXPECT_TRUE(SendAccept());
+    ASSERT_TRUE(GetTransport1());
+    ASSERT_TRUE(GetTransport2());
     EXPECT_EQ(2U, GetTransport1()->channels().size());
     EXPECT_EQ(2U, GetTransport2()->channels().size());
     EXPECT_TRUE(SendRtcp1());
@@ -1021,6 +1036,8 @@
   void SendRtcpMuxToRtcpMux() {
     CreateChannels(RTCP | RTCP_MUX, RTCP | RTCP_MUX);
     EXPECT_TRUE(SendInitiate());
+    ASSERT_TRUE(GetTransport1());
+    ASSERT_TRUE(GetTransport2());
     EXPECT_EQ(2U, GetTransport1()->channels().size());
     EXPECT_EQ(1U, GetTransport2()->channels().size());
     EXPECT_TRUE(SendAccept());
@@ -1045,6 +1062,8 @@
     CreateChannels(RTCP | RTCP_MUX, RTCP | RTCP_MUX);
     channel1_->ActivateRtcpMux();
     EXPECT_TRUE(SendInitiate());
+    ASSERT_TRUE(GetTransport1());
+    ASSERT_TRUE(GetTransport2());
     EXPECT_EQ(1U, GetTransport1()->channels().size());
     EXPECT_EQ(1U, GetTransport2()->channels().size());
     EXPECT_TRUE(SendAccept());
@@ -1068,6 +1087,8 @@
     CreateChannels(RTCP | RTCP_MUX, RTCP | RTCP_MUX);
     channel2_->ActivateRtcpMux();
     EXPECT_TRUE(SendInitiate());
+    ASSERT_TRUE(GetTransport1());
+    ASSERT_TRUE(GetTransport2());
     EXPECT_EQ(2U, GetTransport1()->channels().size());
     EXPECT_EQ(1U, GetTransport2()->channels().size());
     EXPECT_TRUE(SendAccept());
@@ -1093,6 +1114,8 @@
     channel1_->ActivateRtcpMux();
     channel2_->ActivateRtcpMux();
     EXPECT_TRUE(SendInitiate());
+    ASSERT_TRUE(GetTransport1());
+    ASSERT_TRUE(GetTransport2());
     EXPECT_EQ(1U, GetTransport1()->channels().size());
     EXPECT_EQ(1U, GetTransport2()->channels().size());
     EXPECT_TRUE(SendAccept());
@@ -1117,6 +1140,8 @@
     CreateChannels(RTCP | RTCP_MUX, RTCP);
     channel1_->ActivateRtcpMux();
     EXPECT_TRUE(SendInitiate());
+    ASSERT_TRUE(GetTransport1());
+    ASSERT_TRUE(GetTransport2());
     EXPECT_EQ(1U, GetTransport1()->channels().size());
     EXPECT_EQ(2U, GetTransport2()->channels().size());
     EXPECT_FALSE(SendAccept());
@@ -1126,6 +1151,8 @@
   void SendEarlyRtcpMuxToRtcp() {
     CreateChannels(RTCP | RTCP_MUX, RTCP);
     EXPECT_TRUE(SendInitiate());
+    ASSERT_TRUE(GetTransport1());
+    ASSERT_TRUE(GetTransport2());
     EXPECT_EQ(2U, GetTransport1()->channels().size());
     EXPECT_EQ(2U, GetTransport2()->channels().size());
 
@@ -1156,6 +1183,8 @@
   void SendEarlyRtcpMuxToRtcpMux() {
     CreateChannels(RTCP | RTCP_MUX, RTCP | RTCP_MUX);
     EXPECT_TRUE(SendInitiate());
+    ASSERT_TRUE(GetTransport1());
+    ASSERT_TRUE(GetTransport2());
     EXPECT_EQ(2U, GetTransport1()->channels().size());
     EXPECT_EQ(1U, GetTransport2()->channels().size());
 
@@ -1246,6 +1275,8 @@
       EXPECT_TRUE(SendProvisionalAnswer());
       EXPECT_TRUE(channel1_->secure());
       EXPECT_TRUE(channel2_->secure());
+      ASSERT_TRUE(GetTransport1());
+      ASSERT_TRUE(GetTransport2());
       EXPECT_EQ(2U, GetTransport1()->channels().size());
       EXPECT_EQ(2U, GetTransport2()->channels().size());
       EXPECT_TRUE(SendCustomRtcp1(kSsrc1));
@@ -1329,6 +1360,8 @@
     CreateChannels(0, 0);
     EXPECT_TRUE(SendInitiate());
     EXPECT_TRUE(SendAccept());
+    ASSERT_TRUE(GetTransport1());
+    ASSERT_TRUE(GetTransport2());
     EXPECT_EQ(1U, GetTransport1()->channels().size());
     EXPECT_EQ(1U, GetTransport2()->channels().size());
     EXPECT_TRUE(SendRtp1());
@@ -1393,6 +1426,8 @@
     }
     CreateChannels(flags, flags);
     EXPECT_TRUE(SendInitiate());
+    ASSERT_TRUE(GetTransport1());
+    ASSERT_TRUE(GetTransport2());
     EXPECT_EQ(2U, GetTransport1()->channels().size());
     EXPECT_EQ(expected_channels, GetTransport2()->channels().size());
     EXPECT_TRUE(SendAccept());
@@ -1581,6 +1616,8 @@
     CreateChannels(RTCP, RTCP);
     EXPECT_TRUE(SendInitiate());
     EXPECT_TRUE(SendAccept());
+    ASSERT_TRUE(GetTransport1());
+    ASSERT_TRUE(GetTransport2());
     EXPECT_EQ(2U, GetTransport1()->channels().size());
     EXPECT_EQ(2U, GetTransport2()->channels().size());
 
@@ -1669,15 +1706,15 @@
     EXPECT_TRUE(media_channel1_->ready_to_send());
 
     // rtp channel becomes not ready to send will be propagated to mediachannel
-    channel1_->SetReadyToSend(rtp, false);
+    channel1_->SetReadyToSend(false, false);
     EXPECT_FALSE(media_channel1_->ready_to_send());
-    channel1_->SetReadyToSend(rtp, true);
+    channel1_->SetReadyToSend(false, true);
     EXPECT_TRUE(media_channel1_->ready_to_send());
 
     // rtcp channel becomes not ready to send will be propagated to mediachannel
-    channel1_->SetReadyToSend(rtcp, false);
+    channel1_->SetReadyToSend(true, false);
     EXPECT_FALSE(media_channel1_->ready_to_send());
-    channel1_->SetReadyToSend(rtcp, true);
+    channel1_->SetReadyToSend(true, true);
     EXPECT_TRUE(media_channel1_->ready_to_send());
   }
 
@@ -1696,13 +1733,13 @@
     // should trigger the MediaChannel's OnReadyToSend.
     rtp->SignalReadyToSend(rtp);
     EXPECT_TRUE(media_channel1_->ready_to_send());
-    channel1_->SetReadyToSend(rtp, false);
+    channel1_->SetReadyToSend(false, false);
     EXPECT_FALSE(media_channel1_->ready_to_send());
   }
 
  protected:
-  cricket::FakeSession session1_;
-  cricket::FakeSession session2_;
+  cricket::FakeTransportController transport_controller1_;
+  cricket::FakeTransportController transport_controller2_;
   cricket::FakeMediaEngine media_engine_;
   // The media channels are owned by the voice channel objects below.
   typename T::MediaChannel* media_channel1_;
@@ -1763,18 +1800,21 @@
     : public ChannelTest<VoiceTraits> {
  public:
   typedef ChannelTest<VoiceTraits> Base;
-  VoiceChannelTest() : Base(kPcmuFrame, sizeof(kPcmuFrame),
-                            kRtcpReport, sizeof(kRtcpReport)) {}
+  VoiceChannelTest()
+      : Base(kPcmuFrame, sizeof(kPcmuFrame), kRtcpReport, sizeof(kRtcpReport)) {
+  }
 };
 
 // override to add NULL parameter
-template<>
+template <>
 cricket::VideoChannel* ChannelTest<VideoTraits>::CreateChannel(
-    rtc::Thread* thread, cricket::MediaEngineInterface* engine,
-    cricket::FakeVideoMediaChannel* ch, cricket::BaseSession* session,
+    rtc::Thread* thread,
+    cricket::MediaEngineInterface* engine,
+    cricket::FakeVideoMediaChannel* ch,
+    cricket::TransportController* transport_controller,
     bool rtcp) {
   cricket::VideoChannel* channel = new cricket::VideoChannel(
-      thread, ch, session, cricket::CN_VIDEO, rtcp);
+      thread, ch, transport_controller, cricket::CN_VIDEO, rtcp);
   if (!channel->Init()) {
     delete channel;
     channel = NULL;
@@ -1827,8 +1867,11 @@
     : public ChannelTest<VideoTraits> {
  public:
   typedef ChannelTest<VideoTraits> Base;
-  VideoChannelTest() : Base(kH264Packet, sizeof(kH264Packet),
-                            kRtcpReport, sizeof(kRtcpReport)) {}
+  VideoChannelTest()
+      : Base(kH264Packet,
+             sizeof(kH264Packet),
+             kRtcpReport,
+             sizeof(kRtcpReport)) {}
 };
 
 
@@ -2519,13 +2562,15 @@
 };
 
 // Override to avoid engine channel parameter.
-template<>
+template <>
 cricket::DataChannel* ChannelTest<DataTraits>::CreateChannel(
-    rtc::Thread* thread, cricket::MediaEngineInterface* engine,
-    cricket::FakeDataMediaChannel* ch, cricket::BaseSession* session,
+    rtc::Thread* thread,
+    cricket::MediaEngineInterface* engine,
+    cricket::FakeDataMediaChannel* ch,
+    cricket::TransportController* transport_controller,
     bool rtcp) {
   cricket::DataChannel* channel = new cricket::DataChannel(
-      thread, ch, session, cricket::CN_DATA, rtcp);
+      thread, ch, transport_controller, cricket::CN_DATA, rtcp);
   if (!channel->Init()) {
     delete channel;
     channel = NULL;
diff --git a/talk/session/media/channelmanager.cc b/talk/session/media/channelmanager.cc
index 9285543..201a031 100644
--- a/talk/session/media/channelmanager.cc
+++ b/talk/session/media/channelmanager.cc
@@ -318,23 +318,18 @@
 
 VoiceChannel* ChannelManager::CreateVoiceChannel(
     webrtc::MediaControllerInterface* media_controller,
-    BaseSession* session,
+    TransportController* transport_controller,
     const std::string& content_name,
     bool rtcp,
     const AudioOptions& options) {
   return worker_thread_->Invoke<VoiceChannel*>(
-      Bind(&ChannelManager::CreateVoiceChannel_w,
-          this,
-          media_controller,
-          session,
-          content_name,
-          rtcp,
-          options));
+      Bind(&ChannelManager::CreateVoiceChannel_w, this, media_controller,
+           transport_controller, content_name, rtcp, options));
 }
 
 VoiceChannel* ChannelManager::CreateVoiceChannel_w(
     webrtc::MediaControllerInterface* media_controller,
-    BaseSession* session,
+    TransportController* transport_controller,
     const std::string& content_name,
     bool rtcp,
     const AudioOptions& options) {
@@ -346,9 +341,9 @@
   if (!media_channel)
     return nullptr;
 
-  VoiceChannel* voice_channel = new VoiceChannel(
-      worker_thread_, media_engine_.get(), media_channel,
-      session, content_name, rtcp);
+  VoiceChannel* voice_channel =
+      new VoiceChannel(worker_thread_, media_engine_.get(), media_channel,
+                       transport_controller, content_name, rtcp);
   if (!voice_channel->Init()) {
     delete voice_channel;
     return nullptr;
@@ -379,23 +374,18 @@
 
 VideoChannel* ChannelManager::CreateVideoChannel(
     webrtc::MediaControllerInterface* media_controller,
-    BaseSession* session,
+    TransportController* transport_controller,
     const std::string& content_name,
     bool rtcp,
     const VideoOptions& options) {
   return worker_thread_->Invoke<VideoChannel*>(
-      Bind(&ChannelManager::CreateVideoChannel_w,
-           this,
-           media_controller,
-           session,
-           content_name,
-           rtcp,
-           options));
+      Bind(&ChannelManager::CreateVideoChannel_w, this, media_controller,
+           transport_controller, content_name, rtcp, options));
 }
 
 VideoChannel* ChannelManager::CreateVideoChannel_w(
     webrtc::MediaControllerInterface* media_controller,
-    BaseSession* session,
+    TransportController* transport_controller,
     const std::string& content_name,
     bool rtcp,
     const VideoOptions& options) {
@@ -404,12 +394,12 @@
   ASSERT(nullptr != media_controller);
   VideoMediaChannel* media_channel =
       media_engine_->CreateVideoChannel(media_controller->call_w(), options);
-  if (media_channel == NULL)
+  if (media_channel == NULL) {
     return NULL;
+  }
 
   VideoChannel* video_channel = new VideoChannel(
-      worker_thread_, media_channel,
-      session, content_name, rtcp);
+      worker_thread_, media_channel, transport_controller, content_name, rtcp);
   if (!video_channel->Init()) {
     delete video_channel;
     return NULL;
@@ -440,16 +430,20 @@
 }
 
 DataChannel* ChannelManager::CreateDataChannel(
-    BaseSession* session, const std::string& content_name,
-    bool rtcp, DataChannelType channel_type) {
+    TransportController* transport_controller,
+    const std::string& content_name,
+    bool rtcp,
+    DataChannelType channel_type) {
   return worker_thread_->Invoke<DataChannel*>(
-      Bind(&ChannelManager::CreateDataChannel_w, this, session, content_name,
-           rtcp, channel_type));
+      Bind(&ChannelManager::CreateDataChannel_w, this, transport_controller,
+           content_name, rtcp, channel_type));
 }
 
 DataChannel* ChannelManager::CreateDataChannel_w(
-    BaseSession* session, const std::string& content_name,
-    bool rtcp, DataChannelType data_channel_type) {
+    TransportController* transport_controller,
+    const std::string& content_name,
+    bool rtcp,
+    DataChannelType data_channel_type) {
   // This is ok to alloc from a thread other than the worker thread.
   ASSERT(initialized_);
   DataMediaChannel* media_channel = data_media_engine_->CreateChannel(
@@ -461,8 +455,7 @@
   }
 
   DataChannel* data_channel = new DataChannel(
-      worker_thread_, media_channel,
-      session, content_name, rtcp);
+      worker_thread_, media_channel, transport_controller, content_name, rtcp);
   if (!data_channel->Init()) {
     LOG(LS_WARNING) << "Failed to init data channel.";
     delete data_channel;
diff --git a/talk/session/media/channelmanager.h b/talk/session/media/channelmanager.h
index 86bb972..5f76519 100644
--- a/talk/session/media/channelmanager.h
+++ b/talk/session/media/channelmanager.h
@@ -104,11 +104,10 @@
   void Terminate();
 
   // The operations below all occur on the worker thread.
-
   // Creates a voice channel, to be associated with the specified session.
   VoiceChannel* CreateVoiceChannel(
       webrtc::MediaControllerInterface* media_controller,
-      BaseSession* session,
+      TransportController* transport_controller,
       const std::string& content_name,
       bool rtcp,
       const AudioOptions& options);
@@ -118,15 +117,16 @@
   // associated with the specified session.
   VideoChannel* CreateVideoChannel(
       webrtc::MediaControllerInterface* media_controller,
-      BaseSession* session,
+      TransportController* transport_controller,
       const std::string& content_name,
       bool rtcp,
       const VideoOptions& options);
   // Destroys a video channel created with the Create API.
   void DestroyVideoChannel(VideoChannel* video_channel);
-  DataChannel* CreateDataChannel(
-      BaseSession* session, const std::string& content_name,
-      bool rtcp, DataChannelType data_channel_type);
+  DataChannel* CreateDataChannel(TransportController* transport_controller,
+                                 const std::string& content_name,
+                                 bool rtcp,
+                                 DataChannelType data_channel_type);
   // Destroys a data channel created with the Create API.
   void DestroyDataChannel(DataChannel* data_channel);
 
@@ -241,21 +241,22 @@
   void Terminate_w();
   VoiceChannel* CreateVoiceChannel_w(
       webrtc::MediaControllerInterface* media_controller,
-      BaseSession* session,
+      TransportController* transport_controller,
       const std::string& content_name,
       bool rtcp,
       const AudioOptions& options);
   void DestroyVoiceChannel_w(VoiceChannel* voice_channel);
   VideoChannel* CreateVideoChannel_w(
       webrtc::MediaControllerInterface* media_controller,
-      BaseSession* session,
+      TransportController* transport_controller,
       const std::string& content_name,
       bool rtcp,
       const VideoOptions& options);
   void DestroyVideoChannel_w(VideoChannel* video_channel);
-  DataChannel* CreateDataChannel_w(
-      BaseSession* session, const std::string& content_name,
-      bool rtcp, DataChannelType data_channel_type);
+  DataChannel* CreateDataChannel_w(TransportController* transport_controller,
+                                   const std::string& content_name,
+                                   bool rtcp,
+                                   DataChannelType data_channel_type);
   void DestroyDataChannel_w(DataChannel* data_channel);
   bool SetAudioOptions_w(const AudioOptions& options, int delay_offset,
                          const Device* in_dev, const Device* out_dev);
diff --git a/talk/session/media/channelmanager_unittest.cc b/talk/session/media/channelmanager_unittest.cc
index 98c79c3..bbd5602 100644
--- a/talk/session/media/channelmanager_unittest.cc
+++ b/talk/session/media/channelmanager_unittest.cc
@@ -31,11 +31,11 @@
 #include "talk/media/base/testutils.h"
 #include "talk/media/devices/fakedevicemanager.h"
 #include "talk/media/webrtc/fakewebrtccall.h"
-#include "webrtc/p2p/base/fakesession.h"
 #include "talk/session/media/channelmanager.h"
 #include "webrtc/base/gunit.h"
 #include "webrtc/base/logging.h"
 #include "webrtc/base/thread.h"
+#include "webrtc/p2p/base/faketransportcontroller.h"
 
 namespace cricket {
 
@@ -57,14 +57,20 @@
   }
   ~FakeMediaController() override {}
   webrtc::Call* call_w() override { return call_; }
+
  private:
   webrtc::Call* call_;
 };
 
 class ChannelManagerTest : public testing::Test {
  protected:
-  ChannelManagerTest() : fake_call_(webrtc::Call::Config()),
-      fake_mc_(&fake_call_), fme_(NULL), fdm_(NULL), fcm_(NULL), cm_(NULL) {}
+  ChannelManagerTest()
+      : fake_call_(webrtc::Call::Config()),
+        fake_mc_(&fake_call_),
+        fme_(NULL),
+        fdm_(NULL),
+        fcm_(NULL),
+        cm_(NULL) {}
 
   virtual void SetUp() {
     fme_ = new cricket::FakeMediaEngine();
@@ -75,7 +81,8 @@
     fcm_ = new cricket::FakeCaptureManager();
     cm_ = new cricket::ChannelManager(
         fme_, fdme_, fdm_, fcm_, rtc::Thread::Current());
-    session_ = new cricket::FakeSession(true);
+    transport_controller_ =
+        new cricket::FakeTransportController(ICEROLE_CONTROLLING);
 
     std::vector<std::string> in_device_list, out_device_list, vid_device_list;
     in_device_list.push_back("audio-in1");
@@ -90,7 +97,7 @@
   }
 
   virtual void TearDown() {
-    delete session_;
+    delete transport_controller_;
     delete cm_;
     cm_ = NULL;
     fdm_ = NULL;
@@ -107,7 +114,7 @@
   cricket::FakeDeviceManager* fdm_;
   cricket::FakeCaptureManager* fcm_;
   cricket::ChannelManager* cm_;
-  cricket::FakeSession* session_;
+  cricket::FakeTransportController* transport_controller_;
 };
 
 // Test that we startup/shutdown properly.
@@ -138,15 +145,16 @@
 // Test that we can create and destroy a voice and video channel.
 TEST_F(ChannelManagerTest, CreateDestroyChannels) {
   EXPECT_TRUE(cm_->Init());
-  cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel(
-      &fake_mc_, session_, cricket::CN_AUDIO, false, AudioOptions());
+  cricket::VoiceChannel* voice_channel =
+      cm_->CreateVoiceChannel(&fake_mc_, transport_controller_,
+                              cricket::CN_AUDIO, false, AudioOptions());
   EXPECT_TRUE(voice_channel != nullptr);
-  cricket::VideoChannel* video_channel = cm_->CreateVideoChannel(
-      &fake_mc_, session_, cricket::CN_VIDEO, false, VideoOptions());
+  cricket::VideoChannel* video_channel =
+      cm_->CreateVideoChannel(&fake_mc_, transport_controller_,
+                              cricket::CN_VIDEO, false, VideoOptions());
   EXPECT_TRUE(video_channel != nullptr);
-  cricket::DataChannel* data_channel =
-      cm_->CreateDataChannel(session_, cricket::CN_DATA,
-                             false, cricket::DCT_RTP);
+  cricket::DataChannel* data_channel = cm_->CreateDataChannel(
+      transport_controller_, cricket::CN_DATA, false, cricket::DCT_RTP);
   EXPECT_TRUE(data_channel != nullptr);
   cm_->DestroyVideoChannel(video_channel);
   cm_->DestroyVoiceChannel(voice_channel);
@@ -159,17 +167,19 @@
   worker_.Start();
   EXPECT_TRUE(cm_->set_worker_thread(&worker_));
   EXPECT_TRUE(cm_->Init());
-  delete session_;
-  session_ = new cricket::FakeSession(&worker_, true);
-  cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel(
-      &fake_mc_, session_, cricket::CN_AUDIO, false, AudioOptions());
+  delete transport_controller_;
+  transport_controller_ =
+      new cricket::FakeTransportController(&worker_, ICEROLE_CONTROLLING);
+  cricket::VoiceChannel* voice_channel =
+      cm_->CreateVoiceChannel(&fake_mc_, transport_controller_,
+                              cricket::CN_AUDIO, false, AudioOptions());
   EXPECT_TRUE(voice_channel != nullptr);
-  cricket::VideoChannel* video_channel = cm_->CreateVideoChannel(
-      &fake_mc_, session_, cricket::CN_VIDEO, false, VideoOptions());
+  cricket::VideoChannel* video_channel =
+      cm_->CreateVideoChannel(&fake_mc_, transport_controller_,
+                              cricket::CN_VIDEO, false, VideoOptions());
   EXPECT_TRUE(video_channel != nullptr);
-  cricket::DataChannel* data_channel =
-      cm_->CreateDataChannel(session_, cricket::CN_DATA,
-                             false, cricket::DCT_RTP);
+  cricket::DataChannel* data_channel = cm_->CreateDataChannel(
+      transport_controller_, cricket::CN_DATA, false, cricket::DCT_RTP);
   EXPECT_TRUE(data_channel != nullptr);
   cm_->DestroyVideoChannel(video_channel);
   cm_->DestroyVoiceChannel(voice_channel);
@@ -181,21 +191,22 @@
 // to create a cricket::TransportChannel
 TEST_F(ChannelManagerTest, NoTransportChannelTest) {
   EXPECT_TRUE(cm_->Init());
-  session_->set_fail_channel_creation(true);
+  transport_controller_->set_fail_channel_creation(true);
   // The test is useless unless the session does not fail creating
   // cricket::TransportChannel.
-  ASSERT_TRUE(session_->CreateChannel(
+  ASSERT_TRUE(transport_controller_->CreateTransportChannel_w(
                   "audio", cricket::ICE_CANDIDATE_COMPONENT_RTP) == nullptr);
 
-  cricket::VoiceChannel* voice_channel = cm_->CreateVoiceChannel(
-      &fake_mc_, session_, cricket::CN_AUDIO, false, AudioOptions());
+  cricket::VoiceChannel* voice_channel =
+      cm_->CreateVoiceChannel(&fake_mc_, transport_controller_,
+                              cricket::CN_AUDIO, false, AudioOptions());
   EXPECT_TRUE(voice_channel == nullptr);
-  cricket::VideoChannel* video_channel = cm_->CreateVideoChannel(
-      &fake_mc_, session_, cricket::CN_VIDEO, false, VideoOptions());
+  cricket::VideoChannel* video_channel =
+      cm_->CreateVideoChannel(&fake_mc_, transport_controller_,
+                              cricket::CN_VIDEO, false, VideoOptions());
   EXPECT_TRUE(video_channel == nullptr);
-  cricket::DataChannel* data_channel =
-      cm_->CreateDataChannel(session_, cricket::CN_DATA,
-                             false, cricket::DCT_RTP);
+  cricket::DataChannel* data_channel = cm_->CreateDataChannel(
+      transport_controller_, cricket::CN_DATA, false, cricket::DCT_RTP);
   EXPECT_TRUE(data_channel == nullptr);
   cm_->Terminate();
 }
diff --git a/webrtc/base/fakenetwork.h b/webrtc/base/fakenetwork.h
index 4b6bb68..065d08d 100644
--- a/webrtc/base/fakenetwork.h
+++ b/webrtc/base/fakenetwork.h
@@ -29,12 +29,7 @@
 class FakeNetworkManager : public NetworkManagerBase,
                            public MessageHandler {
  public:
-  FakeNetworkManager()
-      : thread_(Thread::Current()),
-        next_index_(0),
-        started_(false),
-        sent_first_update_(false) {
-  }
+  FakeNetworkManager() : thread_(Thread::Current()) {}
 
   typedef std::vector<SocketAddress> IfaceList;
 
@@ -58,20 +53,18 @@
   }
 
   virtual void StartUpdating() {
-    if (started_) {
-      if (sent_first_update_)
+    ++start_count_;
+    if (start_count_ == 1) {
+      sent_first_update_ = false;
+      thread_->Post(this);
+    } else {
+      if (sent_first_update_) {
         SignalNetworksChanged();
-      return;
+      }
     }
-
-    started_ = true;
-    sent_first_update_ = false;
-    thread_->Post(this);
   }
 
-  virtual void StopUpdating() {
-    started_ = false;
-  }
+  virtual void StopUpdating() { --start_count_; }
 
   // MessageHandler interface.
   virtual void OnMessage(Message* msg) {
@@ -82,7 +75,7 @@
 
  private:
   void DoUpdateNetworks() {
-    if (!started_)
+    if (start_count_ == 0)
       return;
     std::vector<Network*> networks;
     for (IfaceList::iterator it = ifaces_.begin();
@@ -111,9 +104,9 @@
 
   Thread* thread_;
   IfaceList ifaces_;
-  int next_index_;
-  bool started_;
-  bool sent_first_update_;
+  int next_index_ = 0;
+  int start_count_ = 0;
+  bool sent_first_update_ = false;
 };
 
 }  // namespace rtc
diff --git a/webrtc/p2p/base/dtlstransport.h b/webrtc/p2p/base/dtlstransport.h
index 9559c1e..c448eb1 100644
--- a/webrtc/p2p/base/dtlstransport.h
+++ b/webrtc/p2p/base/dtlstransport.h
@@ -11,7 +11,6 @@
 #ifndef WEBRTC_P2P_BASE_DTLSTRANSPORT_H_
 #define WEBRTC_P2P_BASE_DTLSTRANSPORT_H_
 
-#include "webrtc/base/checks.h"
 #include "webrtc/p2p/base/dtlstransportchannel.h"
 #include "webrtc/p2p/base/transport.h"
 
@@ -23,33 +22,31 @@
 
 class PortAllocator;
 
-// Base should be a descendant of cricket::Transport
-// TODO(hbos): Add appropriate RTC_DCHECK thread checks to all methods.
+// Base should be a descendant of cricket::Transport and have a constructor
+// that takes a transport name and PortAllocator.
+//
+// Everything in this class should be called on the worker thread.
 template<class Base>
 class DtlsTransport : public Base {
  public:
-  DtlsTransport(rtc::Thread* signaling_thread,
-                rtc::Thread* worker_thread,
-                const std::string& content_name,
+  DtlsTransport(const std::string& name,
                 PortAllocator* allocator,
                 const rtc::scoped_refptr<rtc::RTCCertificate>& certificate)
-      : Base(signaling_thread, worker_thread, content_name, allocator),
+      : Base(name, allocator),
         certificate_(certificate),
         secure_role_(rtc::SSL_CLIENT),
-        ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_10) {
-  }
+        ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_10) {}
 
   ~DtlsTransport() {
     Base::DestroyAllChannels();
   }
-  void SetCertificate_w(
+
+  void SetLocalCertificate(
       const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override {
-    RTC_DCHECK(Base::worker_thread()->IsCurrent());
     certificate_ = certificate;
   }
-  bool GetCertificate_w(
+  bool GetLocalCertificate(
       rtc::scoped_refptr<rtc::RTCCertificate>* certificate) override {
-    RTC_DCHECK(Base::worker_thread()->IsCurrent());
     if (!certificate_)
       return false;
 
@@ -57,15 +54,13 @@
     return true;
   }
 
-  bool SetSslMaxProtocolVersion_w(rtc::SSLProtocolVersion version) override {
-    RTC_DCHECK(Base::worker_thread()->IsCurrent());
+  bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) override {
     ssl_max_version_ = version;
     return true;
   }
 
-  bool ApplyLocalTransportDescription_w(TransportChannelImpl* channel,
-                                        std::string* error_desc) override {
-    RTC_DCHECK(Base::worker_thread()->IsCurrent());
+  bool ApplyLocalTransportDescription(TransportChannelImpl* channel,
+                                      std::string* error_desc) override {
     rtc::SSLFingerprint* local_fp =
         Base::local_description()->identity_fingerprint.get();
 
@@ -98,12 +93,11 @@
     }
 
     // Apply the description in the base class.
-    return Base::ApplyLocalTransportDescription_w(channel, error_desc);
+    return Base::ApplyLocalTransportDescription(channel, error_desc);
   }
 
-  bool NegotiateTransportDescription_w(ContentAction local_role,
-                                       std::string* error_desc) override {
-    RTC_DCHECK(Base::worker_thread()->IsCurrent());
+  bool NegotiateTransportDescription(ContentAction local_role,
+                                     std::string* error_desc) override {
     if (!Base::local_description() || !Base::remote_description()) {
       const std::string msg = "Local and Remote description must be set before "
                               "transport descriptions are negotiated";
@@ -200,7 +194,7 @@
     }
 
     // Now run the negotiation for the base class.
-    return Base::NegotiateTransportDescription_w(local_role, error_desc);
+    return Base::NegotiateTransportDescription(local_role, error_desc);
   }
 
   DtlsTransportChannelWrapper* CreateTransportChannel(int component) override {
@@ -219,18 +213,15 @@
     Base::DestroyTransportChannel(base_channel);
   }
 
-  bool GetSslRole_w(rtc::SSLRole* ssl_role) const override {
-    RTC_DCHECK(Base::worker_thread()->IsCurrent());
+  bool GetSslRole(rtc::SSLRole* ssl_role) const override {
     ASSERT(ssl_role != NULL);
     *ssl_role = secure_role_;
     return true;
   }
 
  private:
-  bool ApplyNegotiatedTransportDescription_w(
-      TransportChannelImpl* channel,
-      std::string* error_desc) override {
-    RTC_DCHECK(Base::worker_thread()->IsCurrent());
+  bool ApplyNegotiatedTransportDescription(TransportChannelImpl* channel,
+                                           std::string* error_desc) override {
     // Set ssl role. Role must be set before fingerprint is applied, which
     // initiates DTLS setup.
     if (!channel->SetSslRole(secure_role_)) {
@@ -245,7 +236,7 @@
       return BadTransportDescription("Failed to apply remote fingerprint.",
                                      error_desc);
     }
-    return Base::ApplyNegotiatedTransportDescription_w(channel, error_desc);
+    return Base::ApplyNegotiatedTransportDescription(channel, error_desc);
   }
 
   rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
diff --git a/webrtc/p2p/base/dtlstransportchannel.cc b/webrtc/p2p/base/dtlstransportchannel.cc
index dcebdee..b434d08 100644
--- a/webrtc/p2p/base/dtlstransportchannel.cc
+++ b/webrtc/p2p/base/dtlstransportchannel.cc
@@ -87,9 +87,9 @@
 }
 
 DtlsTransportChannelWrapper::DtlsTransportChannelWrapper(
-                                           Transport* transport,
-                                           TransportChannelImpl* channel)
-    : TransportChannelImpl(channel->content_name(), channel->component()),
+    Transport* transport,
+    TransportChannelImpl* channel)
+    : TransportChannelImpl(channel->transport_name(), channel->component()),
       transport_(transport),
       worker_thread_(rtc::Thread::Current()),
       channel_(channel),
@@ -103,12 +103,10 @@
       &DtlsTransportChannelWrapper::OnReadPacket);
   channel_->SignalReadyToSend.connect(this,
       &DtlsTransportChannelWrapper::OnReadyToSend);
-  channel_->SignalRequestSignaling.connect(this,
-      &DtlsTransportChannelWrapper::OnRequestSignaling);
-  channel_->SignalCandidateReady.connect(this,
-      &DtlsTransportChannelWrapper::OnCandidateReady);
-  channel_->SignalCandidatesAllocationDone.connect(this,
-      &DtlsTransportChannelWrapper::OnCandidatesAllocationDone);
+  channel_->SignalGatheringState.connect(
+      this, &DtlsTransportChannelWrapper::OnGatheringState);
+  channel_->SignalCandidateGathered.connect(
+      this, &DtlsTransportChannelWrapper::OnCandidateGathered);
   channel_->SignalRoleConflict.connect(this,
       &DtlsTransportChannelWrapper::OnRoleConflict);
   channel_->SignalRouteChange.connect(this,
@@ -211,7 +209,7 @@
     return true;
   }
 
-  // Allow SetRemoteFingerprint with a NULL digest even if SetLocalIdentity
+  // Allow SetRemoteFingerprint with a NULL digest even if SetLocalCertificate
   // hasn't been called.
   if (dtls_state_ > STATE_OFFERED ||
       (dtls_state_ == STATE_NONE && !digest_alg.empty())) {
@@ -591,22 +589,17 @@
   return downward_->OnPacketReceived(data, size);
 }
 
-void DtlsTransportChannelWrapper::OnRequestSignaling(
+void DtlsTransportChannelWrapper::OnGatheringState(
     TransportChannelImpl* channel) {
   ASSERT(channel == channel_);
-  SignalRequestSignaling(this);
+  SignalGatheringState(this);
 }
 
-void DtlsTransportChannelWrapper::OnCandidateReady(
-    TransportChannelImpl* channel, const Candidate& c) {
+void DtlsTransportChannelWrapper::OnCandidateGathered(
+    TransportChannelImpl* channel,
+    const Candidate& c) {
   ASSERT(channel == channel_);
-  SignalCandidateReady(this, c);
-}
-
-void DtlsTransportChannelWrapper::OnCandidatesAllocationDone(
-    TransportChannelImpl* channel) {
-  ASSERT(channel == channel_);
-  SignalCandidatesAllocationDone(this);
+  SignalCandidateGathered(this, c);
 }
 
 void DtlsTransportChannelWrapper::OnRoleConflict(
diff --git a/webrtc/p2p/base/dtlstransportchannel.h b/webrtc/p2p/base/dtlstransportchannel.h
index ddedcbc..ea6a579 100644
--- a/webrtc/p2p/base/dtlstransportchannel.h
+++ b/webrtc/p2p/base/dtlstransportchannel.h
@@ -27,7 +27,7 @@
 // the bottom and a StreamInterface on the top.
 class StreamInterfaceChannel : public rtc::StreamInterface {
  public:
-  StreamInterfaceChannel(TransportChannel* channel);
+  explicit StreamInterfaceChannel(TransportChannel* channel);
 
   // Push in a packet; this gets pulled out from Read().
   bool OnPacketReceived(const char* data, size_t size);
@@ -35,10 +35,14 @@
   // Implementations of StreamInterface
   rtc::StreamState GetState() const override { return state_; }
   void Close() override { state_ = rtc::SS_CLOSED; }
-  rtc::StreamResult Read(void* buffer, size_t buffer_len,
-                         size_t* read, int* error) override;
-  rtc::StreamResult Write(const void* data, size_t data_len,
-                          size_t* written, int* error) override;
+  rtc::StreamResult Read(void* buffer,
+                         size_t buffer_len,
+                         size_t* read,
+                         int* error) override;
+  rtc::StreamResult Write(const void* data,
+                          size_t data_len,
+                          size_t* written,
+                          int* error) override;
 
  private:
   TransportChannel* channel_;  // owned by DtlsTransportChannelWrapper
@@ -93,12 +97,8 @@
                               TransportChannelImpl* channel);
   ~DtlsTransportChannelWrapper() override;
 
-  void SetIceRole(IceRole role) override {
-    channel_->SetIceRole(role);
-  }
-  IceRole GetIceRole() const override {
-    return channel_->GetIceRole();
-  }
+  void SetIceRole(IceRole role) override { channel_->SetIceRole(role); }
+  IceRole GetIceRole() const override { return channel_->GetIceRole(); }
   bool SetLocalCertificate(
       const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override;
   rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const override;
@@ -109,7 +109,8 @@
   bool IsDtlsActive() const override { return dtls_state_ != STATE_NONE; }
 
   // Called to send a packet (via DTLS, if turned on).
-  int SendPacket(const char* data, size_t size,
+  int SendPacket(const char* data,
+                 size_t size,
                  const rtc::PacketOptions& options,
                  int flags) override;
 
@@ -120,15 +121,11 @@
   bool GetOption(rtc::Socket::Option opt, int* value) override {
     return channel_->GetOption(opt, value);
   }
-  int GetError() override {
-    return channel_->GetError();
-  }
+  int GetError() override { return channel_->GetError(); }
   bool GetStats(ConnectionInfos* infos) override {
     return channel_->GetStats(infos);
   }
-  const std::string SessionId() const override {
-    return channel_->SessionId();
-  }
+  const std::string SessionId() const override { return channel_->SessionId(); }
 
   virtual bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version);
 
@@ -168,9 +165,7 @@
   }
 
   // TransportChannelImpl calls.
-  Transport* GetTransport() override {
-    return transport_;
-  }
+  Transport* GetTransport() override { return transport_; }
 
   TransportChannelState GetState() const override {
     return channel_->GetState();
@@ -192,11 +187,14 @@
 
   void Connect() override;
 
-  void OnSignalingReady() override {
-    channel_->OnSignalingReady();
+  void MaybeStartGathering() override { channel_->MaybeStartGathering(); }
+
+  IceGatheringState gathering_state() const override {
+    return channel_->gathering_state();
   }
-  void OnCandidate(const Candidate& candidate) override {
-    channel_->OnCandidate(candidate);
+
+  void AddRemoteCandidate(const Candidate& candidate) override {
+    channel_->AddRemoteCandidate(candidate);
   }
 
   void SetReceivingTimeout(int receiving_timeout_ms) override {
@@ -217,9 +215,8 @@
   bool SetupDtls();
   bool MaybeStartDtls();
   bool HandleDtlsPacket(const char* data, size_t size);
-  void OnRequestSignaling(TransportChannelImpl* channel);
-  void OnCandidateReady(TransportChannelImpl* channel, const Candidate& c);
-  void OnCandidatesAllocationDone(TransportChannelImpl* channel);
+  void OnGatheringState(TransportChannelImpl* channel);
+  void OnCandidateGathered(TransportChannelImpl* channel, const Candidate& c);
   void OnRoleConflict(TransportChannelImpl* channel);
   void OnRouteChange(TransportChannel* channel, const Candidate& candidate);
   void OnConnectionRemoved(TransportChannelImpl* channel);
diff --git a/webrtc/p2p/base/dtlstransportchannel_unittest.cc b/webrtc/p2p/base/dtlstransportchannel_unittest.cc
index 10640f9..95696e2 100644
--- a/webrtc/p2p/base/dtlstransportchannel_unittest.cc
+++ b/webrtc/p2p/base/dtlstransportchannel_unittest.cc
@@ -11,7 +11,7 @@
 #include <set>
 
 #include "webrtc/p2p/base/dtlstransport.h"
-#include "webrtc/p2p/base/fakesession.h"
+#include "webrtc/p2p/base/faketransportcontroller.h"
 #include "webrtc/base/common.h"
 #include "webrtc/base/dscp.h"
 #include "webrtc/base/gunit.h"
@@ -21,7 +21,6 @@
 #include "webrtc/base/sslidentity.h"
 #include "webrtc/base/sslstreamadapter.h"
 #include "webrtc/base/stringutils.h"
-#include "webrtc/base/thread.h"
 
 #define MAYBE_SKIP_TEST(feature)                    \
   if (!(rtc::SSLStreamAdapter::feature())) {  \
@@ -45,19 +44,14 @@
 
 class DtlsTestClient : public sigslot::has_slots<> {
  public:
-  DtlsTestClient(const std::string& name,
-                 rtc::Thread* signaling_thread,
-                 rtc::Thread* worker_thread) :
-      name_(name),
-      signaling_thread_(signaling_thread),
-      worker_thread_(worker_thread),
-      packet_size_(0),
-      use_dtls_srtp_(false),
-      ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_10),
-      negotiated_dtls_(false),
-      received_dtls_client_hello_(false),
-      received_dtls_server_hello_(false) {
-  }
+  DtlsTestClient(const std::string& name)
+      : name_(name),
+        packet_size_(0),
+        use_dtls_srtp_(false),
+        ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_10),
+        negotiated_dtls_(false),
+        received_dtls_client_hello_(false),
+        received_dtls_server_hello_(false) {}
   void CreateCertificate(rtc::KeyType key_type) {
     certificate_ = rtc::RTCCertificate::Create(
         rtc::scoped_ptr<rtc::SSLIdentity>(
@@ -71,13 +65,12 @@
     use_dtls_srtp_ = true;
   }
   void SetupMaxProtocolVersion(rtc::SSLProtocolVersion version) {
-    ASSERT(transport_.get() == NULL);
+    ASSERT(!transport_);
     ssl_max_version_ = version;
   }
   void SetupChannels(int count, cricket::IceRole role) {
     transport_.reset(new cricket::DtlsTransport<cricket::FakeTransport>(
-        signaling_thread_, worker_thread_, "dtls content name", nullptr,
-        certificate_));
+        "dtls content name", nullptr, certificate_));
     transport_->SetAsync(true);
     transport_->SetIceRole(role);
     transport_->SetIceTiebreaker(
@@ -118,8 +111,8 @@
   void Negotiate(DtlsTestClient* peer, cricket::ContentAction action,
                  ConnectionRole local_role, ConnectionRole remote_role,
                  int flags) {
-    Negotiate(certificate_, certificate_ ? peer->certificate_ : nullptr,
-              action, local_role, remote_role, flags);
+    Negotiate(certificate_, certificate_ ? peer->certificate_ : nullptr, action,
+              local_role, remote_role, flags);
   }
 
   // Allow any DTLS configuration to be specified (including invalid ones).
@@ -163,18 +156,18 @@
     }
 
     cricket::TransportDescription local_desc(
-        std::vector<std::string>(), kIceUfrag1, kIcePwd1,
-        cricket::ICEMODE_FULL, local_role,
-         // If remote if the offerer and has no DTLS support, answer will be
+        std::vector<std::string>(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL,
+        local_role,
+        // If remote if the offerer and has no DTLS support, answer will be
         // without any fingerprint.
-        (action == cricket::CA_ANSWER && !remote_cert) ?
-            NULL : local_fingerprint.get(),
+        (action == cricket::CA_ANSWER && !remote_cert)
+            ? nullptr
+            : local_fingerprint.get(),
         cricket::Candidates());
 
     cricket::TransportDescription remote_desc(
-        std::vector<std::string>(), kIceUfrag1, kIcePwd1,
-        cricket::ICEMODE_FULL, remote_role, remote_fingerprint.get(),
-        cricket::Candidates());
+        std::vector<std::string>(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL,
+        remote_role, remote_fingerprint.get(), cricket::Candidates());
 
     bool expect_success = (flags & NF_EXPECT_FAILURE) ? false : true;
     // If |expect_success| is false, expect SRTD or SLTD to fail when
@@ -199,7 +192,9 @@
     return true;
   }
 
-  bool writable() const { return transport_->writable(); }
+  bool all_channels_writable() const {
+    return transport_->all_channels_writable();
+  }
 
   void CheckRole(rtc::SSLRole role) {
     if (role == rtc::SSL_CLIENT) {
@@ -337,8 +332,8 @@
     ASSERT_TRUE(VerifyPacket(data, size, &packet_num));
     received_.insert(packet_num);
     // Only DTLS-SRTP packets should have the bypass flag set.
-    int expected_flags = (certificate_ && IsRtpLeadByte(data[0])) ?
-        cricket::PF_SRTP_BYPASS : 0;
+    int expected_flags =
+        (certificate_ && IsRtpLeadByte(data[0])) ? cricket::PF_SRTP_BYPASS : 0;
     ASSERT_EQ(expected_flags, flags);
   }
 
@@ -372,8 +367,6 @@
 
  private:
   std::string name_;
-  rtc::Thread* signaling_thread_;
-  rtc::Thread* worker_thread_;
   rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
   rtc::scoped_ptr<cricket::FakeTransport> transport_;
   std::vector<cricket::DtlsTransportChannelWrapper*> channels_;
@@ -389,16 +382,13 @@
 
 class DtlsTransportChannelTest : public testing::Test {
  public:
-  DtlsTransportChannelTest() :
-      client1_("P1", rtc::Thread::Current(),
-               rtc::Thread::Current()),
-      client2_("P2", rtc::Thread::Current(),
-               rtc::Thread::Current()),
-      channel_ct_(1),
-      use_dtls_(false),
-      use_dtls_srtp_(false),
-      ssl_expected_version_(rtc::SSL_PROTOCOL_DTLS_10) {
-  }
+  DtlsTransportChannelTest()
+      : client1_("P1"),
+        client2_("P2"),
+        channel_ct_(1),
+        use_dtls_(false),
+        use_dtls_srtp_(false),
+        ssl_expected_version_(rtc::SSL_PROTOCOL_DTLS_10) {}
 
   void SetChannelCount(size_t channel_ct) {
     channel_ct_ = static_cast<int>(channel_ct);
@@ -440,8 +430,10 @@
     if (!rv)
       return false;
 
-    EXPECT_TRUE_WAIT(client1_.writable() && client2_.writable(), 10000);
-    if (!client1_.writable() || !client2_.writable())
+    EXPECT_TRUE_WAIT(
+        client1_.all_channels_writable() && client2_.all_channels_writable(),
+        10000);
+    if (!client1_.all_channels_writable() || !client2_.all_channels_writable())
       return false;
 
     // Check that we used the right roles.
@@ -818,7 +810,9 @@
               cricket::CONNECTIONROLE_ACTIVE, NF_REOFFER);
   bool rv = client1_.Connect(&client2_);
   EXPECT_TRUE(rv);
-  EXPECT_TRUE_WAIT(client1_.writable() && client2_.writable(), 10000);
+  EXPECT_TRUE_WAIT(
+      client1_.all_channels_writable() && client2_.all_channels_writable(),
+      10000);
 
   TestTransfer(0, 1000, 100, true);
   TestTransfer(1, 1000, 100, true);
@@ -837,8 +831,8 @@
 
   // After negotiation, each side has a distinct local certificate, but still no
   // remote certificate, because connection has not yet occurred.
-  ASSERT_TRUE(client1_.transport()->GetCertificate(&certificate1));
-  ASSERT_TRUE(client2_.transport()->GetCertificate(&certificate2));
+  ASSERT_TRUE(client1_.transport()->GetLocalCertificate(&certificate1));
+  ASSERT_TRUE(client2_.transport()->GetLocalCertificate(&certificate2));
   ASSERT_NE(certificate1->ssl_certificate().ToPEMString(),
             certificate2->ssl_certificate().ToPEMString());
   ASSERT_FALSE(
@@ -861,8 +855,8 @@
   rtc::scoped_ptr<rtc::SSLCertificate> remote_cert2;
 
   // After connection, each side has a distinct local certificate.
-  ASSERT_TRUE(client1_.transport()->GetCertificate(&certificate1));
-  ASSERT_TRUE(client2_.transport()->GetCertificate(&certificate2));
+  ASSERT_TRUE(client1_.transport()->GetLocalCertificate(&certificate1));
+  ASSERT_TRUE(client2_.transport()->GetLocalCertificate(&certificate2));
   ASSERT_NE(certificate1->ssl_certificate().ToPEMString(),
             certificate2->ssl_certificate().ToPEMString());
 
diff --git a/webrtc/p2p/base/fakesession.h b/webrtc/p2p/base/fakesession.h
deleted file mode 100644
index bd3c089..0000000
--- a/webrtc/p2p/base/fakesession.h
+++ /dev/null
@@ -1,506 +0,0 @@
-/*
- *  Copyright 2009 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_BASE_FAKESESSION_H_
-#define WEBRTC_P2P_BASE_FAKESESSION_H_
-
-#include <map>
-#include <string>
-#include <vector>
-
-#include "webrtc/p2p/base/session.h"
-#include "webrtc/p2p/base/transport.h"
-#include "webrtc/p2p/base/transportchannel.h"
-#include "webrtc/p2p/base/transportchannelimpl.h"
-#include "webrtc/base/buffer.h"
-#include "webrtc/base/fakesslidentity.h"
-#include "webrtc/base/messagequeue.h"
-#include "webrtc/base/sigslot.h"
-#include "webrtc/base/sslfingerprint.h"
-
-namespace cricket {
-
-class FakeTransport;
-
-struct PacketMessageData : public rtc::MessageData {
-  PacketMessageData(const char* data, size_t len) : packet(data, len) {
-  }
-  rtc::Buffer packet;
-};
-
-// Fake transport channel class, which can be passed to anything that needs a
-// transport channel. Can be informed of another FakeTransportChannel via
-// SetDestination.
-// TODO(hbos): Move implementation to .cc file, this and other classes in file.
-class FakeTransportChannel : public TransportChannelImpl,
-                             public rtc::MessageHandler {
- public:
-  explicit FakeTransportChannel(Transport* transport,
-                                const std::string& content_name,
-                                int component)
-      : TransportChannelImpl(content_name, component),
-        transport_(transport),
-        dest_(nullptr),
-        state_(STATE_INIT),
-        async_(false),
-        do_dtls_(false),
-        role_(ICEROLE_UNKNOWN),
-        tiebreaker_(0),
-        remote_ice_mode_(ICEMODE_FULL),
-        dtls_fingerprint_("", nullptr, 0),
-        ssl_role_(rtc::SSL_CLIENT),
-        connection_count_(0) {
-  }
-  ~FakeTransportChannel() {
-    Reset();
-  }
-
-  uint64 IceTiebreaker() const { return tiebreaker_; }
-  IceMode remote_ice_mode() const { return remote_ice_mode_; }
-  const std::string& ice_ufrag() const { return ice_ufrag_; }
-  const std::string& ice_pwd() const { return ice_pwd_; }
-  const std::string& remote_ice_ufrag() const { return remote_ice_ufrag_; }
-  const std::string& remote_ice_pwd() const { return remote_ice_pwd_; }
-  const rtc::SSLFingerprint& dtls_fingerprint() const {
-    return dtls_fingerprint_;
-  }
-
-  void SetAsync(bool async) {
-    async_ = async;
-  }
-
-  Transport* GetTransport() override {
-    return transport_;
-  }
-
-  TransportChannelState GetState() const override {
-    if (connection_count_ == 0) {
-      return TransportChannelState::STATE_FAILED;
-    }
-
-    if (connection_count_ == 1) {
-      return TransportChannelState::STATE_COMPLETED;
-    }
-
-    return TransportChannelState::STATE_FAILED;
-  }
-
-  void SetIceRole(IceRole role) override { role_ = role; }
-  IceRole GetIceRole() const override { return role_; }
-  void SetIceTiebreaker(uint64 tiebreaker) override {
-    tiebreaker_ = tiebreaker;
-  }
-  void SetIceCredentials(const std::string& ice_ufrag,
-                         const std::string& ice_pwd) override {
-    ice_ufrag_ = ice_ufrag;
-    ice_pwd_ = ice_pwd;
-  }
-  void SetRemoteIceCredentials(const std::string& ice_ufrag,
-                               const std::string& ice_pwd) override {
-    remote_ice_ufrag_ = ice_ufrag;
-    remote_ice_pwd_ = ice_pwd;
-  }
-
-  void SetRemoteIceMode(IceMode mode) override { remote_ice_mode_ = mode; }
-  bool SetRemoteFingerprint(const std::string& alg, const uint8* digest,
-                            size_t digest_len) override {
-    dtls_fingerprint_ = rtc::SSLFingerprint(alg, digest, digest_len);
-    return true;
-  }
-  bool SetSslRole(rtc::SSLRole role) override {
-    ssl_role_ = role;
-    return true;
-  }
-  bool GetSslRole(rtc::SSLRole* role) const override {
-    *role = ssl_role_;
-    return true;
-  }
-
-  void Connect() override {
-    if (state_ == STATE_INIT) {
-      state_ = STATE_CONNECTING;
-    }
-  }
-  virtual void Reset() {
-    if (state_ != STATE_INIT) {
-      state_ = STATE_INIT;
-      if (dest_) {
-        dest_->state_ = STATE_INIT;
-        dest_->dest_ = NULL;
-        dest_ = NULL;
-      }
-    }
-  }
-
-  void SetWritable(bool writable) {
-    set_writable(writable);
-  }
-
-  void SetDestination(FakeTransportChannel* dest) {
-    if (state_ == STATE_CONNECTING && dest) {
-      // This simulates the delivery of candidates.
-      dest_ = dest;
-      dest_->dest_ = this;
-      if (certificate_ && dest_->certificate_) {
-        do_dtls_ = true;
-        dest_->do_dtls_ = true;
-        NegotiateSrtpCiphers();
-      }
-      state_ = STATE_CONNECTED;
-      dest_->state_ = STATE_CONNECTED;
-      set_writable(true);
-      dest_->set_writable(true);
-    } else if (state_ == STATE_CONNECTED && !dest) {
-      // Simulates loss of connectivity, by asymmetrically forgetting dest_.
-      dest_ = NULL;
-      state_ = STATE_CONNECTING;
-      set_writable(false);
-    }
-  }
-
-  void SetConnectionCount(size_t connection_count) {
-    size_t old_connection_count = connection_count_;
-    connection_count_ = connection_count;
-    if (connection_count_ < old_connection_count)
-      SignalConnectionRemoved(this);
-  }
-
-  void SetReceiving(bool receiving) {
-    set_receiving(receiving);
-  }
-
-  void SetReceivingTimeout(int timeout) override {}
-
-  int SendPacket(const char* data, size_t len,
-                 const rtc::PacketOptions& options, int flags) override {
-    if (state_ != STATE_CONNECTED) {
-      return -1;
-    }
-
-    if (flags != PF_SRTP_BYPASS && flags != 0) {
-      return -1;
-    }
-
-    PacketMessageData* packet = new PacketMessageData(data, len);
-    if (async_) {
-      rtc::Thread::Current()->Post(this, 0, packet);
-    } else {
-      rtc::Thread::Current()->Send(this, 0, packet);
-    }
-    return static_cast<int>(len);
-  }
-  int SetOption(rtc::Socket::Option opt, int value) override {
-    return true;
-  }
-  bool GetOption(rtc::Socket::Option opt, int* value) override {
-    return true;
-  }
-  int GetError() override {
-    return 0;
-  }
-
-  void OnSignalingReady() override {
-  }
-  void OnCandidate(const Candidate& candidate) override {
-  }
-
-  void OnMessage(rtc::Message* msg) override {
-    PacketMessageData* data = static_cast<PacketMessageData*>(
-        msg->pdata);
-    dest_->SignalReadPacket(dest_, data->packet.data<char>(),
-                            data->packet.size(), rtc::CreatePacketTime(0), 0);
-    delete data;
-  }
-
-  bool SetLocalCertificate(
-      const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override {
-    certificate_ = certificate;
-    return true;
-  }
-
-  void SetRemoteSSLCertificate(rtc::FakeSSLCertificate* cert) {
-    remote_cert_ = cert;
-  }
-
-  bool IsDtlsActive() const override {
-    return do_dtls_;
-  }
-
-  bool SetSrtpCiphers(const std::vector<std::string>& ciphers) override {
-    srtp_ciphers_ = ciphers;
-    return true;
-  }
-
-  bool GetSrtpCipher(std::string* cipher) override {
-    if (!chosen_srtp_cipher_.empty()) {
-      *cipher = chosen_srtp_cipher_;
-      return true;
-    }
-    return false;
-  }
-
-  bool GetSslCipher(std::string* cipher) override {
-    return false;
-  }
-
-  rtc::scoped_refptr<rtc::RTCCertificate>
-  GetLocalCertificate() const override {
-    return certificate_;
-  }
-
-  bool GetRemoteSSLCertificate(rtc::SSLCertificate** cert) const override {
-    if (!remote_cert_)
-      return false;
-
-    *cert = remote_cert_->GetReference();
-    return true;
-  }
-
-  bool ExportKeyingMaterial(const std::string& label,
-                            const uint8* context,
-                            size_t context_len,
-                            bool use_context,
-                            uint8* result,
-                            size_t result_len) override {
-    if (!chosen_srtp_cipher_.empty()) {
-      memset(result, 0xff, result_len);
-      return true;
-    }
-
-    return false;
-  }
-
-  virtual void NegotiateSrtpCiphers() {
-    for (std::vector<std::string>::const_iterator it1 = srtp_ciphers_.begin();
-        it1 != srtp_ciphers_.end(); ++it1) {
-      for (std::vector<std::string>::const_iterator it2 =
-              dest_->srtp_ciphers_.begin();
-          it2 != dest_->srtp_ciphers_.end(); ++it2) {
-        if (*it1 == *it2) {
-          chosen_srtp_cipher_ = *it1;
-          dest_->chosen_srtp_cipher_ = *it2;
-          return;
-        }
-      }
-    }
-  }
-
-  bool GetStats(ConnectionInfos* infos) override {
-    ConnectionInfo info;
-    infos->clear();
-    infos->push_back(info);
-    return true;
-  }
-
- private:
-  enum State { STATE_INIT, STATE_CONNECTING, STATE_CONNECTED };
-  Transport* transport_;
-  FakeTransportChannel* dest_;
-  State state_;
-  bool async_;
-  rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
-  rtc::FakeSSLCertificate* remote_cert_;
-  bool do_dtls_;
-  std::vector<std::string> srtp_ciphers_;
-  std::string chosen_srtp_cipher_;
-  IceRole role_;
-  uint64 tiebreaker_;
-  std::string ice_ufrag_;
-  std::string ice_pwd_;
-  std::string remote_ice_ufrag_;
-  std::string remote_ice_pwd_;
-  IceMode remote_ice_mode_;
-  rtc::SSLFingerprint dtls_fingerprint_;
-  rtc::SSLRole ssl_role_;
-  size_t connection_count_;
-};
-
-// Fake transport class, which can be passed to anything that needs a Transport.
-// Can be informed of another FakeTransport via SetDestination (low-tech way
-// of doing candidates)
-class FakeTransport : public Transport {
- public:
-  typedef std::map<int, FakeTransportChannel*> ChannelMap;
-  FakeTransport(rtc::Thread* signaling_thread,
-                rtc::Thread* worker_thread,
-                const std::string& content_name,
-                PortAllocator* alllocator = nullptr)
-      : Transport(signaling_thread, worker_thread,
-                  content_name, nullptr),
-        dest_(nullptr),
-        async_(false) {
-  }
-  ~FakeTransport() {
-    DestroyAllChannels();
-  }
-
-  const ChannelMap& channels() const { return channels_; }
-
-  void SetAsync(bool async) { async_ = async; }
-  void SetDestination(FakeTransport* dest) {
-    dest_ = dest;
-    for (ChannelMap::iterator it = channels_.begin(); it != channels_.end();
-         ++it) {
-      it->second->SetLocalCertificate(certificate_);
-      SetChannelDestination(it->first, it->second);
-    }
-  }
-
-  void SetWritable(bool writable) {
-    for (ChannelMap::iterator it = channels_.begin(); it != channels_.end();
-         ++it) {
-      it->second->SetWritable(writable);
-    }
-  }
-
-  void set_certificate(
-      const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
-    certificate_ = certificate;
-  }
-
-  using Transport::local_description;
-  using Transport::remote_description;
-
- protected:
-  TransportChannelImpl* CreateTransportChannel(int component) override {
-    if (channels_.find(component) != channels_.end()) {
-      return NULL;
-    }
-    FakeTransportChannel* channel =
-        new FakeTransportChannel(this, content_name(), component);
-    channel->SetAsync(async_);
-    SetChannelDestination(component, channel);
-    channels_[component] = channel;
-    return channel;
-  }
-  void DestroyTransportChannel(TransportChannelImpl* channel) override {
-    channels_.erase(channel->component());
-    delete channel;
-  }
-  void SetCertificate_w(
-      const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override {
-    certificate_ = certificate;
-  }
-  bool GetCertificate_w(
-      rtc::scoped_refptr<rtc::RTCCertificate>* certificate) override {
-    if (!certificate_)
-      return false;
-
-    *certificate = certificate_;
-    return true;
-  }
-
- private:
-  FakeTransportChannel* GetFakeChannel(int component) {
-    ChannelMap::iterator it = channels_.find(component);
-    return (it != channels_.end()) ? it->second : NULL;
-  }
-  void SetChannelDestination(int component,
-                             FakeTransportChannel* channel) {
-    FakeTransportChannel* dest_channel = NULL;
-    if (dest_) {
-      dest_channel = dest_->GetFakeChannel(component);
-      if (dest_channel)
-        dest_channel->SetLocalCertificate(dest_->certificate_);
-    }
-    channel->SetDestination(dest_channel);
-  }
-
-  // Note, this is distinct from the Channel map owned by Transport.
-  // This map just tracks the FakeTransportChannels created by this class.
-  ChannelMap channels_;
-  FakeTransport* dest_;
-  bool async_;
-  rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
-};
-
-// Fake session class, which can be passed into a BaseChannel object for
-// test purposes. Can be connected to other FakeSessions via Connect().
-class FakeSession : public BaseSession {
- public:
-  explicit FakeSession()
-      : BaseSession(rtc::Thread::Current(),
-                    rtc::Thread::Current(),
-                    NULL, "", "", true),
-      fail_create_channel_(false) {
-  }
-  explicit FakeSession(bool initiator)
-      : BaseSession(rtc::Thread::Current(),
-                    rtc::Thread::Current(),
-                    NULL, "", "", initiator),
-      fail_create_channel_(false) {
-  }
-  FakeSession(rtc::Thread* worker_thread, bool initiator)
-      : BaseSession(rtc::Thread::Current(),
-                    worker_thread,
-                    NULL, "", "", initiator),
-      fail_create_channel_(false) {
-  }
-
-  FakeTransport* GetTransport(const std::string& content_name) {
-    return static_cast<FakeTransport*>(
-        BaseSession::GetTransport(content_name));
-  }
-
-  void Connect(FakeSession* dest) {
-    // Simulate the exchange of candidates.
-    CompleteNegotiation();
-    dest->CompleteNegotiation();
-    for (TransportMap::const_iterator it = transport_proxies().begin();
-        it != transport_proxies().end(); ++it) {
-      static_cast<FakeTransport*>(it->second->impl())->SetDestination(
-          dest->GetTransport(it->first));
-    }
-  }
-
-  TransportChannel* CreateChannel(const std::string& content_name,
-                                  int component) override {
-    if (fail_create_channel_) {
-      return NULL;
-    }
-    return BaseSession::CreateChannel(content_name, component);
-  }
-
-  void set_fail_channel_creation(bool fail_channel_creation) {
-    fail_create_channel_ = fail_channel_creation;
-  }
-
-  // TODO: Hoist this into Session when we re-work the Session code.
-  void set_ssl_rtccertificate(
-      const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
-    for (TransportMap::const_iterator it = transport_proxies().begin();
-        it != transport_proxies().end(); ++it) {
-      // We know that we have a FakeTransport*
-
-      static_cast<FakeTransport*>(it->second->impl())->set_certificate
-          (certificate);
-    }
-  }
-
- protected:
-  Transport* CreateTransport(const std::string& content_name) override {
-    return new FakeTransport(signaling_thread(), worker_thread(), content_name);
-  }
-
-  void CompleteNegotiation() {
-    for (TransportMap::const_iterator it = transport_proxies().begin();
-        it != transport_proxies().end(); ++it) {
-      it->second->CompleteNegotiation();
-      it->second->ConnectChannels();
-    }
-  }
-
- private:
-  bool fail_create_channel_;
-};
-
-}  // namespace cricket
-
-#endif  // WEBRTC_P2P_BASE_FAKESESSION_H_
diff --git a/webrtc/p2p/base/faketransportcontroller.h b/webrtc/p2p/base/faketransportcontroller.h
new file mode 100644
index 0000000..e356e81
--- /dev/null
+++ b/webrtc/p2p/base/faketransportcontroller.h
@@ -0,0 +1,537 @@
+/*
+ *  Copyright 2009 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_BASE_FAKETRANSPORTCONTROLLER_H_
+#define WEBRTC_P2P_BASE_FAKETRANSPORTCONTROLLER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "webrtc/p2p/base/transport.h"
+#include "webrtc/p2p/base/transportchannel.h"
+#include "webrtc/p2p/base/transportcontroller.h"
+#include "webrtc/p2p/base/transportchannelimpl.h"
+#include "webrtc/base/bind.h"
+#include "webrtc/base/buffer.h"
+#include "webrtc/base/fakesslidentity.h"
+#include "webrtc/base/messagequeue.h"
+#include "webrtc/base/sigslot.h"
+#include "webrtc/base/sslfingerprint.h"
+#include "webrtc/base/thread.h"
+
+namespace cricket {
+
+class FakeTransport;
+
+struct PacketMessageData : public rtc::MessageData {
+  PacketMessageData(const char* data, size_t len) : packet(data, len) {}
+  rtc::Buffer packet;
+};
+
+// Fake transport channel class, which can be passed to anything that needs a
+// transport channel. Can be informed of another FakeTransportChannel via
+// SetDestination.
+// TODO(hbos): Move implementation to .cc file, this and other classes in file.
+class FakeTransportChannel : public TransportChannelImpl,
+                             public rtc::MessageHandler {
+ public:
+  explicit FakeTransportChannel(Transport* transport,
+                                const std::string& name,
+                                int component)
+      : TransportChannelImpl(name, component),
+        transport_(transport),
+        dtls_fingerprint_("", nullptr, 0) {}
+  ~FakeTransportChannel() { Reset(); }
+
+  uint64 IceTiebreaker() const { return tiebreaker_; }
+  IceMode remote_ice_mode() const { return remote_ice_mode_; }
+  const std::string& ice_ufrag() const { return ice_ufrag_; }
+  const std::string& ice_pwd() const { return ice_pwd_; }
+  const std::string& remote_ice_ufrag() const { return remote_ice_ufrag_; }
+  const std::string& remote_ice_pwd() const { return remote_ice_pwd_; }
+  const rtc::SSLFingerprint& dtls_fingerprint() const {
+    return dtls_fingerprint_;
+  }
+
+  // If async, will send packets by "Post"-ing to message queue instead of
+  // synchronously "Send"-ing.
+  void SetAsync(bool async) { async_ = async; }
+
+  Transport* GetTransport() override { return transport_; }
+
+  TransportChannelState GetState() const override {
+    if (connection_count_ == 0) {
+      return had_connection_ ? TransportChannelState::STATE_FAILED
+                             : TransportChannelState::STATE_INIT;
+    }
+
+    if (connection_count_ == 1) {
+      return TransportChannelState::STATE_COMPLETED;
+    }
+
+    return TransportChannelState::STATE_CONNECTING;
+  }
+
+  void SetIceRole(IceRole role) override { role_ = role; }
+  IceRole GetIceRole() const override { return role_; }
+  void SetIceTiebreaker(uint64 tiebreaker) override {
+    tiebreaker_ = tiebreaker;
+  }
+  void SetIceCredentials(const std::string& ice_ufrag,
+                         const std::string& ice_pwd) override {
+    ice_ufrag_ = ice_ufrag;
+    ice_pwd_ = ice_pwd;
+  }
+  void SetRemoteIceCredentials(const std::string& ice_ufrag,
+                               const std::string& ice_pwd) override {
+    remote_ice_ufrag_ = ice_ufrag;
+    remote_ice_pwd_ = ice_pwd;
+  }
+
+  void SetRemoteIceMode(IceMode mode) override { remote_ice_mode_ = mode; }
+  bool SetRemoteFingerprint(const std::string& alg,
+                            const uint8* digest,
+                            size_t digest_len) override {
+    dtls_fingerprint_ = rtc::SSLFingerprint(alg, digest, digest_len);
+    return true;
+  }
+  bool SetSslRole(rtc::SSLRole role) override {
+    ssl_role_ = role;
+    return true;
+  }
+  bool GetSslRole(rtc::SSLRole* role) const override {
+    *role = ssl_role_;
+    return true;
+  }
+
+  void Connect() override {
+    if (state_ == STATE_INIT) {
+      state_ = STATE_CONNECTING;
+    }
+  }
+
+  void MaybeStartGathering() override {
+    if (gathering_state_ == kIceGatheringNew) {
+      gathering_state_ = kIceGatheringGathering;
+      SignalGatheringState(this);
+    }
+  }
+
+  IceGatheringState gathering_state() const override {
+    return gathering_state_;
+  }
+
+  void Reset() {
+    if (state_ != STATE_INIT) {
+      state_ = STATE_INIT;
+      if (dest_) {
+        dest_->state_ = STATE_INIT;
+        dest_->dest_ = nullptr;
+        dest_ = nullptr;
+      }
+    }
+  }
+
+  void SetWritable(bool writable) { set_writable(writable); }
+
+  void SetDestination(FakeTransportChannel* dest) {
+    if (state_ == STATE_CONNECTING && dest) {
+      // This simulates the delivery of candidates.
+      dest_ = dest;
+      dest_->dest_ = this;
+      if (local_cert_ && dest_->local_cert_) {
+        do_dtls_ = true;
+        dest_->do_dtls_ = true;
+        NegotiateSrtpCiphers();
+      }
+      state_ = STATE_CONNECTED;
+      dest_->state_ = STATE_CONNECTED;
+      set_writable(true);
+      dest_->set_writable(true);
+    } else if (state_ == STATE_CONNECTED && !dest) {
+      // Simulates loss of connectivity, by asymmetrically forgetting dest_.
+      dest_ = nullptr;
+      state_ = STATE_CONNECTING;
+      set_writable(false);
+    }
+  }
+
+  void SetConnectionCount(size_t connection_count) {
+    size_t old_connection_count = connection_count_;
+    connection_count_ = connection_count;
+    if (connection_count)
+      had_connection_ = true;
+    if (connection_count_ < old_connection_count)
+      SignalConnectionRemoved(this);
+  }
+
+  void SetCandidatesGatheringComplete() {
+    if (gathering_state_ != kIceGatheringComplete) {
+      gathering_state_ = kIceGatheringComplete;
+      SignalGatheringState(this);
+    }
+  }
+
+  void SetReceiving(bool receiving) { set_receiving(receiving); }
+
+  void SetReceivingTimeout(int timeout) override {
+    receiving_timeout_ = timeout;
+  }
+
+  int receiving_timeout() const { return receiving_timeout_; }
+
+  int SendPacket(const char* data,
+                 size_t len,
+                 const rtc::PacketOptions& options,
+                 int flags) override {
+    if (state_ != STATE_CONNECTED) {
+      return -1;
+    }
+
+    if (flags != PF_SRTP_BYPASS && flags != 0) {
+      return -1;
+    }
+
+    PacketMessageData* packet = new PacketMessageData(data, len);
+    if (async_) {
+      rtc::Thread::Current()->Post(this, 0, packet);
+    } else {
+      rtc::Thread::Current()->Send(this, 0, packet);
+    }
+    return static_cast<int>(len);
+  }
+  int SetOption(rtc::Socket::Option opt, int value) override { return true; }
+  bool GetOption(rtc::Socket::Option opt, int* value) override { return true; }
+  int GetError() override { return 0; }
+
+  void AddRemoteCandidate(const Candidate& candidate) override {
+    remote_candidates_.push_back(candidate);
+  }
+  const Candidates& remote_candidates() const { return remote_candidates_; }
+
+  void OnMessage(rtc::Message* msg) override {
+    PacketMessageData* data = static_cast<PacketMessageData*>(msg->pdata);
+    dest_->SignalReadPacket(dest_, data->packet.data<char>(),
+                            data->packet.size(), rtc::CreatePacketTime(0), 0);
+    delete data;
+  }
+
+  bool SetLocalCertificate(
+      const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
+    local_cert_ = certificate;
+    return true;
+  }
+
+  void SetRemoteSSLCertificate(rtc::FakeSSLCertificate* cert) {
+    remote_cert_ = cert;
+  }
+
+  bool IsDtlsActive() const override { return do_dtls_; }
+
+  bool SetSrtpCiphers(const std::vector<std::string>& ciphers) override {
+    srtp_ciphers_ = ciphers;
+    return true;
+  }
+
+  bool GetSrtpCipher(std::string* cipher) override {
+    if (!chosen_srtp_cipher_.empty()) {
+      *cipher = chosen_srtp_cipher_;
+      return true;
+    }
+    return false;
+  }
+
+  bool GetSslCipher(std::string* cipher) override { return false; }
+
+  rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const {
+    return local_cert_;
+  }
+
+  bool GetRemoteSSLCertificate(rtc::SSLCertificate** cert) const override {
+    if (!remote_cert_)
+      return false;
+
+    *cert = remote_cert_->GetReference();
+    return true;
+  }
+
+  bool ExportKeyingMaterial(const std::string& label,
+                            const uint8* context,
+                            size_t context_len,
+                            bool use_context,
+                            uint8* result,
+                            size_t result_len) override {
+    if (!chosen_srtp_cipher_.empty()) {
+      memset(result, 0xff, result_len);
+      return true;
+    }
+
+    return false;
+  }
+
+  void NegotiateSrtpCiphers() {
+    for (std::vector<std::string>::const_iterator it1 = srtp_ciphers_.begin();
+         it1 != srtp_ciphers_.end(); ++it1) {
+      for (std::vector<std::string>::const_iterator it2 =
+               dest_->srtp_ciphers_.begin();
+           it2 != dest_->srtp_ciphers_.end(); ++it2) {
+        if (*it1 == *it2) {
+          chosen_srtp_cipher_ = *it1;
+          dest_->chosen_srtp_cipher_ = *it2;
+          return;
+        }
+      }
+    }
+  }
+
+  bool GetStats(ConnectionInfos* infos) override {
+    ConnectionInfo info;
+    infos->clear();
+    infos->push_back(info);
+    return true;
+  }
+
+  void set_ssl_max_protocol_version(rtc::SSLProtocolVersion version) {
+    ssl_max_version_ = version;
+  }
+  rtc::SSLProtocolVersion ssl_max_protocol_version() const {
+    return ssl_max_version_;
+  }
+
+ private:
+  enum State { STATE_INIT, STATE_CONNECTING, STATE_CONNECTED };
+  Transport* transport_;
+  FakeTransportChannel* dest_ = nullptr;
+  State state_ = STATE_INIT;
+  bool async_ = false;
+  Candidates remote_candidates_;
+  rtc::scoped_refptr<rtc::RTCCertificate> local_cert_;
+  rtc::FakeSSLCertificate* remote_cert_ = nullptr;
+  bool do_dtls_ = false;
+  std::vector<std::string> srtp_ciphers_;
+  std::string chosen_srtp_cipher_;
+  int receiving_timeout_ = -1;
+  IceRole role_ = ICEROLE_UNKNOWN;
+  uint64 tiebreaker_ = 0;
+  std::string ice_ufrag_;
+  std::string ice_pwd_;
+  std::string remote_ice_ufrag_;
+  std::string remote_ice_pwd_;
+  IceMode remote_ice_mode_ = ICEMODE_FULL;
+  rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_10;
+  rtc::SSLFingerprint dtls_fingerprint_;
+  rtc::SSLRole ssl_role_ = rtc::SSL_CLIENT;
+  size_t connection_count_ = 0;
+  IceGatheringState gathering_state_ = kIceGatheringNew;
+  bool had_connection_ = false;
+};
+
+// Fake transport class, which can be passed to anything that needs a Transport.
+// Can be informed of another FakeTransport via SetDestination (low-tech way
+// of doing candidates)
+class FakeTransport : public Transport {
+ public:
+  typedef std::map<int, FakeTransportChannel*> ChannelMap;
+
+  explicit FakeTransport(const std::string& name) : Transport(name, nullptr) {}
+
+  // Note that we only have a constructor with the allocator parameter so it can
+  // be wrapped by a DtlsTransport.
+  FakeTransport(const std::string& name, PortAllocator* allocator)
+      : Transport(name, nullptr) {}
+
+  ~FakeTransport() { DestroyAllChannels(); }
+
+  const ChannelMap& channels() const { return channels_; }
+
+  // If async, will send packets by "Post"-ing to message queue instead of
+  // synchronously "Send"-ing.
+  void SetAsync(bool async) { async_ = async; }
+  void SetDestination(FakeTransport* dest) {
+    dest_ = dest;
+    for (const auto& kv : channels_) {
+      kv.second->SetLocalCertificate(certificate_);
+      SetChannelDestination(kv.first, kv.second);
+    }
+  }
+
+  void SetWritable(bool writable) {
+    for (const auto& kv : channels_) {
+      kv.second->SetWritable(writable);
+    }
+  }
+
+  void SetLocalCertificate(
+      const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) override {
+    certificate_ = certificate;
+  }
+  bool GetLocalCertificate(
+      rtc::scoped_refptr<rtc::RTCCertificate>* certificate) override {
+    if (!certificate_)
+      return false;
+
+    *certificate = certificate_;
+    return true;
+  }
+
+  bool GetSslRole(rtc::SSLRole* role) const override {
+    if (channels_.empty()) {
+      return false;
+    }
+    return channels_.begin()->second->GetSslRole(role);
+  }
+
+  bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) override {
+    ssl_max_version_ = version;
+    for (const auto& kv : channels_) {
+      kv.second->set_ssl_max_protocol_version(ssl_max_version_);
+    }
+    return true;
+  }
+  rtc::SSLProtocolVersion ssl_max_protocol_version() const {
+    return ssl_max_version_;
+  }
+
+  using Transport::local_description;
+  using Transport::remote_description;
+
+ protected:
+  TransportChannelImpl* CreateTransportChannel(int component) override {
+    if (channels_.find(component) != channels_.end()) {
+      return nullptr;
+    }
+    FakeTransportChannel* channel =
+        new FakeTransportChannel(this, name(), component);
+    channel->set_ssl_max_protocol_version(ssl_max_version_);
+    channel->SetAsync(async_);
+    SetChannelDestination(component, channel);
+    channels_[component] = channel;
+    return channel;
+  }
+
+  void DestroyTransportChannel(TransportChannelImpl* channel) override {
+    channels_.erase(channel->component());
+    delete channel;
+  }
+
+ private:
+  FakeTransportChannel* GetFakeChannel(int component) {
+    auto it = channels_.find(component);
+    return (it != channels_.end()) ? it->second : nullptr;
+  }
+
+  void SetChannelDestination(int component, FakeTransportChannel* channel) {
+    FakeTransportChannel* dest_channel = nullptr;
+    if (dest_) {
+      dest_channel = dest_->GetFakeChannel(component);
+      if (dest_channel) {
+        dest_channel->SetLocalCertificate(dest_->certificate_);
+      }
+    }
+    channel->SetDestination(dest_channel);
+  }
+
+  // Note, this is distinct from the Channel map owned by Transport.
+  // This map just tracks the FakeTransportChannels created by this class.
+  // It's mainly needed so that we can access a FakeTransportChannel directly,
+  // even if wrapped by a DtlsTransportChannelWrapper.
+  ChannelMap channels_;
+  FakeTransport* dest_ = nullptr;
+  bool async_ = false;
+  rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
+  rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_10;
+};
+
+// Fake TransportController class, which can be passed into a BaseChannel object
+// for test purposes. Can be connected to other FakeTransportControllers via
+// Connect().
+//
+// This fake is unusual in that for the most part, it's implemented with the
+// real TransportController code, but with fake TransportChannels underneath.
+class FakeTransportController : public TransportController {
+ public:
+  FakeTransportController()
+      : TransportController(rtc::Thread::Current(),
+                            rtc::Thread::Current(),
+                            nullptr),
+        fail_create_channel_(false) {}
+
+  explicit FakeTransportController(IceRole role)
+      : TransportController(rtc::Thread::Current(),
+                            rtc::Thread::Current(),
+                            nullptr),
+        fail_create_channel_(false) {
+    SetIceRole(role);
+  }
+
+  explicit FakeTransportController(rtc::Thread* worker_thread)
+      : TransportController(rtc::Thread::Current(), worker_thread, nullptr),
+        fail_create_channel_(false) {}
+
+  FakeTransportController(rtc::Thread* worker_thread, IceRole role)
+      : TransportController(rtc::Thread::Current(), worker_thread, nullptr),
+        fail_create_channel_(false) {
+    SetIceRole(role);
+  }
+
+  FakeTransport* GetTransport_w(const std::string& transport_name) {
+    return static_cast<FakeTransport*>(
+        TransportController::GetTransport_w(transport_name));
+  }
+
+  void Connect(FakeTransportController* dest) {
+    worker_thread()->Invoke<void>(
+        rtc::Bind(&FakeTransportController::Connect_w, this, dest));
+  }
+
+  TransportChannel* CreateTransportChannel_w(const std::string& transport_name,
+                                             int component) override {
+    if (fail_create_channel_) {
+      return nullptr;
+    }
+    return TransportController::CreateTransportChannel_w(transport_name,
+                                                         component);
+  }
+
+  void set_fail_channel_creation(bool fail_channel_creation) {
+    fail_create_channel_ = fail_channel_creation;
+  }
+
+ protected:
+  Transport* CreateTransport_w(const std::string& transport_name) override {
+    return new FakeTransport(transport_name);
+  }
+
+  void Connect_w(FakeTransportController* dest) {
+    // Simulate the exchange of candidates.
+    ConnectChannels_w();
+    dest->ConnectChannels_w();
+    for (auto& kv : transports()) {
+      FakeTransport* transport = static_cast<FakeTransport*>(kv.second);
+      transport->SetDestination(dest->GetTransport_w(kv.first));
+    }
+  }
+
+  void ConnectChannels_w() {
+    for (auto& kv : transports()) {
+      FakeTransport* transport = static_cast<FakeTransport*>(kv.second);
+      transport->ConnectChannels();
+      transport->MaybeStartGathering();
+    }
+  }
+
+ private:
+  bool fail_create_channel_;
+};
+
+}  // namespace cricket
+
+#endif  // WEBRTC_P2P_BASE_FAKETRANSPORTCONTROLLER_H_
diff --git a/webrtc/p2p/base/p2ptransport.cc b/webrtc/p2p/base/p2ptransport.cc
index b919fde..abc4c14 100644
--- a/webrtc/p2p/base/p2ptransport.cc
+++ b/webrtc/p2p/base/p2ptransport.cc
@@ -20,21 +20,15 @@
 
 namespace cricket {
 
-P2PTransport::P2PTransport(rtc::Thread* signaling_thread,
-                           rtc::Thread* worker_thread,
-                           const std::string& content_name,
-                           PortAllocator* allocator)
-    : Transport(signaling_thread, worker_thread,
-                content_name, allocator) {
-}
+P2PTransport::P2PTransport(const std::string& name, PortAllocator* allocator)
+    : Transport(name, allocator) {}
 
 P2PTransport::~P2PTransport() {
   DestroyAllChannels();
 }
 
 TransportChannelImpl* P2PTransport::CreateTransportChannel(int component) {
-  return new P2PTransportChannel(content_name(), component, this,
-                                 port_allocator());
+  return new P2PTransportChannel(name(), component, this, port_allocator());
 }
 
 void P2PTransport::DestroyTransportChannel(TransportChannelImpl* channel) {
diff --git a/webrtc/p2p/base/p2ptransport.h b/webrtc/p2p/base/p2ptransport.h
index 2e27bd8..0f965b4 100644
--- a/webrtc/p2p/base/p2ptransport.h
+++ b/webrtc/p2p/base/p2ptransport.h
@@ -16,12 +16,10 @@
 
 namespace cricket {
 
+// Everything in this class should be called on the worker thread.
 class P2PTransport : public Transport {
  public:
-  P2PTransport(rtc::Thread* signaling_thread,
-               rtc::Thread* worker_thread,
-               const std::string& content_name,
-               PortAllocator* allocator);
+  P2PTransport(const std::string& name, PortAllocator* allocator);
   virtual ~P2PTransport();
 
  protected:
diff --git a/webrtc/p2p/base/p2ptransportchannel.cc b/webrtc/p2p/base/p2ptransportchannel.cc
index ee99060..e7f5c94 100644
--- a/webrtc/p2p/base/p2ptransportchannel.cc
+++ b/webrtc/p2p/base/p2ptransportchannel.cc
@@ -11,6 +11,7 @@
 #include "webrtc/p2p/base/p2ptransportchannel.h"
 
 #include <set>
+#include <algorithm>
 #include "webrtc/p2p/base/common.h"
 #include "webrtc/p2p/base/relayport.h"  // For RELAY_PORT_TYPE.
 #include "webrtc/p2p/base/stunport.h"  // For STUN_PORT_TYPE.
@@ -169,28 +170,27 @@
 
 namespace cricket {
 
-P2PTransportChannel::P2PTransportChannel(const std::string& content_name,
+P2PTransportChannel::P2PTransportChannel(const std::string& transport_name,
                                          int component,
                                          P2PTransport* transport,
-                                         PortAllocator *allocator) :
-    TransportChannelImpl(content_name, component),
-    transport_(transport),
-    allocator_(allocator),
-    worker_thread_(rtc::Thread::Current()),
-    incoming_only_(false),
-    waiting_for_signaling_(false),
-    error_(0),
-    best_connection_(NULL),
-    pending_best_connection_(NULL),
-    sort_dirty_(false),
-    was_writable_(false),
-    remote_ice_mode_(ICEMODE_FULL),
-    ice_role_(ICEROLE_UNKNOWN),
-    tiebreaker_(0),
-    remote_candidate_generation_(0),
-    check_receiving_delay_(MIN_CHECK_RECEIVING_DELAY * 5),
-    receiving_timeout_(MIN_CHECK_RECEIVING_DELAY * 50) {
-}
+                                         PortAllocator* allocator)
+    : TransportChannelImpl(transport_name, component),
+      transport_(transport),
+      allocator_(allocator),
+      worker_thread_(rtc::Thread::Current()),
+      incoming_only_(false),
+      error_(0),
+      best_connection_(NULL),
+      pending_best_connection_(NULL),
+      sort_dirty_(false),
+      was_writable_(false),
+      remote_ice_mode_(ICEMODE_FULL),
+      ice_role_(ICEROLE_UNKNOWN),
+      tiebreaker_(0),
+      remote_candidate_generation_(0),
+      gathering_state_(kIceGatheringNew),
+      check_receiving_delay_(MIN_CHECK_RECEIVING_DELAY * 5),
+      receiving_timeout_(MIN_CHECK_RECEIVING_DELAY * 50) {}
 
 P2PTransportChannel::~P2PTransportChannel() {
   ASSERT(worker_thread_ == rtc::Thread::Current());
@@ -231,6 +231,7 @@
   connection->SignalDestroyed.connect(
       this, &P2PTransportChannel::OnConnectionDestroyed);
   connection->SignalNominated.connect(this, &P2PTransportChannel::OnNominated);
+  had_connection_ = true;
 }
 
 void P2PTransportChannel::SetIceRole(IceRole ice_role) {
@@ -265,8 +266,9 @@
 TransportChannelState P2PTransportChannel::GetState() const {
   std::set<rtc::Network*> networks;
 
-  if (connections_.size() == 0) {
-    return TransportChannelState::STATE_FAILED;
+  if (connections_.empty()) {
+    return had_connection_ ? TransportChannelState::STATE_FAILED
+                           : TransportChannelState::STATE_INIT;
   }
 
   for (uint32 i = 0; i < connections_.size(); ++i) {
@@ -288,21 +290,10 @@
 void P2PTransportChannel::SetIceCredentials(const std::string& ice_ufrag,
                                             const std::string& ice_pwd) {
   ASSERT(worker_thread_ == rtc::Thread::Current());
-  bool ice_restart = false;
-  if (!ice_ufrag_.empty() && !ice_pwd_.empty()) {
-    // Restart candidate allocation if there is any change in either
-    // ice ufrag or password.
-    ice_restart =
-        IceCredentialsChanged(ice_ufrag_, ice_pwd_, ice_ufrag, ice_pwd);
-  }
-
   ice_ufrag_ = ice_ufrag;
   ice_pwd_ = ice_pwd;
-
-  if (ice_restart) {
-    // Restart candidate gathering.
-    Allocate();
-  }
+  // Note: Candidate gathering will restart when MaybeStartGathering is next
+  // called.
 }
 
 void P2PTransportChannel::SetRemoteIceCredentials(const std::string& ice_ufrag,
@@ -359,9 +350,6 @@
     return;
   }
 
-  // Kick off an allocator session
-  Allocate();
-
   // Start pinging as the ports come in.
   thread()->Post(this, MSG_PING);
 
@@ -369,6 +357,22 @@
       check_receiving_delay_, this, MSG_CHECK_RECEIVING);
 }
 
+void P2PTransportChannel::MaybeStartGathering() {
+  // Start gathering if we never started before, or if an ICE restart occurred.
+  if (allocator_sessions_.empty() ||
+      IceCredentialsChanged(allocator_sessions_.back()->ice_ufrag(),
+                            allocator_sessions_.back()->ice_pwd(), ice_ufrag_,
+                            ice_pwd_)) {
+    if (gathering_state_ != kIceGatheringGathering) {
+      gathering_state_ = kIceGatheringGathering;
+      SignalGatheringState(this);
+    }
+    // Time for a new allocator
+    AddAllocatorSession(allocator_->CreateSession(
+        SessionId(), transport_name(), component(), ice_ufrag_, ice_pwd_));
+  }
+}
+
 // A new port is available, attempt to make connections for it
 void P2PTransportChannel::OnPortReady(PortAllocatorSession *session,
                                       PortInterface* port) {
@@ -413,17 +417,21 @@
 
 // A new candidate is available, let listeners know
 void P2PTransportChannel::OnCandidatesReady(
-    PortAllocatorSession *session, const std::vector<Candidate>& candidates) {
+    PortAllocatorSession* session,
+    const std::vector<Candidate>& candidates) {
   ASSERT(worker_thread_ == rtc::Thread::Current());
   for (size_t i = 0; i < candidates.size(); ++i) {
-    SignalCandidateReady(this, candidates[i]);
+    SignalCandidateGathered(this, candidates[i]);
   }
 }
 
 void P2PTransportChannel::OnCandidatesAllocationDone(
     PortAllocatorSession* session) {
   ASSERT(worker_thread_ == rtc::Thread::Current());
-  SignalCandidatesAllocationDone(this);
+  gathering_state_ = kIceGatheringComplete;
+  LOG(LS_INFO) << "P2PTransportChannel: " << transport_name() << ", component "
+               << component() << " gathering complete";
+  SignalGatheringState(this);
 }
 
 // Handle stun packets
@@ -494,8 +502,7 @@
       LOG(LS_WARNING) << "P2PTransportChannel::OnUnknownAddress - "
                       << "No STUN_ATTR_PRIORITY found in the "
                       << "stun request message";
-      port->SendBindingErrorResponse(stun_msg, address,
-                                     STUN_ERROR_BAD_REQUEST,
+      port->SendBindingErrorResponse(stun_msg, address, STUN_ERROR_BAD_REQUEST,
                                      STUN_ERROR_REASON_BAD_REQUEST);
       return;
     }
@@ -545,8 +552,7 @@
       remote_candidate, cricket::PortInterface::ORIGIN_THIS_PORT);
   if (!connection) {
     ASSERT(false);
-    port->SendBindingErrorResponse(stun_msg, address,
-                                   STUN_ERROR_SERVER_ERROR,
+    port->SendBindingErrorResponse(stun_msg, address, STUN_ERROR_SERVER_ERROR,
                                    STUN_ERROR_REASON_SERVER_ERROR);
     return;
   }
@@ -575,16 +581,6 @@
                              // from Transport.
 }
 
-// When the signalling channel is ready, we can really kick off the allocator
-void P2PTransportChannel::OnSignalingReady() {
-  ASSERT(worker_thread_ == rtc::Thread::Current());
-  if (waiting_for_signaling_) {
-    waiting_for_signaling_ = false;
-    AddAllocatorSession(allocator_->CreateSession(
-        SessionId(), content_name(), component(), ice_ufrag_, ice_pwd_));
-  }
-}
-
 void P2PTransportChannel::OnNominated(Connection* conn) {
   ASSERT(worker_thread_ == rtc::Thread::Current());
   ASSERT(ice_role_ == ICEROLE_CONTROLLED);
@@ -606,7 +602,7 @@
   }
 }
 
-void P2PTransportChannel::OnCandidate(const Candidate& candidate) {
+void P2PTransportChannel::AddRemoteCandidate(const Candidate& candidate) {
   ASSERT(worker_thread_ == rtc::Thread::Current());
 
   uint32 generation = candidate.generation();
@@ -847,7 +843,7 @@
 
   std::vector<Connection *>::const_iterator it;
   for (it = connections_.begin(); it != connections_.end(); ++it) {
-    Connection *connection = *it;
+    Connection* connection = *it;
     ConnectionInfo info;
     info.best_connection = (best_connection_ == connection);
     info.receiving = connection->receiving();
@@ -881,14 +877,6 @@
   return static_cast<rtc::DiffServCodePoint> (it->second);
 }
 
-// Begin allocate (or immediately re-allocate, if MSG_ALLOCATE pending)
-void P2PTransportChannel::Allocate() {
-  // Time for a new allocator, lets make sure we have a signalling channel
-  // to communicate candidates through first.
-  waiting_for_signaling_ = true;
-  SignalRequestSignaling(this);
-}
-
 // Monitor connection states.
 void P2PTransportChannel::UpdateConnectionStates() {
   uint32 now = rtc::Time();
@@ -1228,8 +1216,7 @@
 void P2PTransportChannel::PingConnection(Connection* conn) {
   bool use_candidate = false;
   if (remote_ice_mode_ == ICEMODE_FULL && ice_role_ == ICEROLE_CONTROLLING) {
-    use_candidate = (conn == best_connection_) ||
-                    (best_connection_ == NULL) ||
+    use_candidate = (conn == best_connection_) || (best_connection_ == NULL) ||
                     (!best_connection_->writable()) ||
                     (conn->priority() > best_connection_->priority());
   } else if (remote_ice_mode_ == ICEMODE_LITE && conn == best_connection_) {
@@ -1311,9 +1298,10 @@
 }
 
 // We data is available, let listeners know
-void P2PTransportChannel::OnReadPacket(
-    Connection *connection, const char *data, size_t len,
-    const rtc::PacketTime& packet_time) {
+void P2PTransportChannel::OnReadPacket(Connection* connection,
+                                       const char* data,
+                                       size_t len,
+                                       const rtc::PacketTime& packet_time) {
   ASSERT(worker_thread_ == rtc::Thread::Current());
 
   // Do not deliver, if packet doesn't belong to the correct transport channel.
diff --git a/webrtc/p2p/base/p2ptransportchannel.h b/webrtc/p2p/base/p2ptransportchannel.h
index a8f1666..92969c8 100644
--- a/webrtc/p2p/base/p2ptransportchannel.h
+++ b/webrtc/p2p/base/p2ptransportchannel.h
@@ -51,11 +51,11 @@
 class P2PTransportChannel : public TransportChannelImpl,
                             public rtc::MessageHandler {
  public:
-  P2PTransportChannel(const std::string& content_name,
+  P2PTransportChannel(const std::string& transport_name,
                       int component,
                       P2PTransport* transport,
-                      PortAllocator *allocator);
-  ~P2PTransportChannel() override;
+                      PortAllocator* allocator);
+  virtual ~P2PTransportChannel();
 
   // From TransportChannelImpl:
   Transport* GetTransport() override { return transport_; }
@@ -69,15 +69,20 @@
                                const std::string& ice_pwd) override;
   void SetRemoteIceMode(IceMode mode) override;
   void Connect() override;
-  void OnSignalingReady() override;
-  void OnCandidate(const Candidate& candidate) override;
+  void MaybeStartGathering() override;
+  IceGatheringState gathering_state() const override {
+    return gathering_state_;
+  }
+  void AddRemoteCandidate(const Candidate& candidate) override;
   // Sets the receiving timeout in milliseconds.
   // This also sets the check_receiving_delay proportionally.
   void SetReceivingTimeout(int receiving_timeout_ms) override;
 
   // From TransportChannel:
-  int SendPacket(const char *data, size_t len,
-                 const rtc::PacketOptions& options, int flags) override;
+  int SendPacket(const char* data,
+                 size_t len,
+                 const rtc::PacketOptions& options,
+                 int flags) override;
   int SetOption(rtc::Socket::Option opt, int value) override;
   bool GetOption(rtc::Socket::Option opt, int* value) override;
   int GetError() override { return error_; }
@@ -96,13 +101,9 @@
   bool IsDtlsActive() const override { return false; }
 
   // Default implementation.
-  bool GetSslRole(rtc::SSLRole* role) const override {
-    return false;
-  }
+  bool GetSslRole(rtc::SSLRole* role) const override { return false; }
 
-  bool SetSslRole(rtc::SSLRole role) override {
-    return false;
-  }
+  bool SetSslRole(rtc::SSLRole role) override { return false; }
 
   // Set up the ciphers to use for DTLS-SRTP.
   bool SetSrtpCiphers(const std::vector<std::string>& ciphers) override {
@@ -110,14 +111,10 @@
   }
 
   // Find out which DTLS-SRTP cipher was negotiated.
-  bool GetSrtpCipher(std::string* cipher) override {
-    return false;
-  }
+  bool GetSrtpCipher(std::string* cipher) override { return false; }
 
   // Find out which DTLS cipher was negotiated.
-  bool GetSslCipher(std::string* cipher) override {
-    return false;
-  }
+  bool GetSslCipher(std::string* cipher) override { return false; }
 
   // Returns null because the channel is not encrypted by default.
   rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const override {
@@ -165,7 +162,6 @@
     return allocator_sessions_.back();
   }
 
-  void Allocate();
   void UpdateConnectionStates();
   void RequestSort();
   void SortConnections();
@@ -213,7 +209,7 @@
 
   void OnNominated(Connection* conn);
 
-  void OnMessage(rtc::Message *pmsg) override;
+  void OnMessage(rtc::Message* pmsg) override;
   void OnSort();
   void OnPing();
 
@@ -223,10 +219,9 @@
   Connection* best_nominated_connection() const;
 
   P2PTransport* transport_;
-  PortAllocator *allocator_;
-  rtc::Thread *worker_thread_;
+  PortAllocator* allocator_;
+  rtc::Thread* worker_thread_;
   bool incoming_only_;
-  bool waiting_for_signaling_;
   int error_;
   std::vector<PortAllocatorSession*> allocator_sessions_;
   std::vector<PortInterface *> ports_;
@@ -238,6 +233,7 @@
   std::vector<RemoteCandidate> remote_candidates_;
   bool sort_dirty_;  // indicates whether another sort is needed right now
   bool was_writable_;
+  bool had_connection_ = false;  // if connections_ has ever been nonempty
   typedef std::map<rtc::Socket::Option, int> OptionMap;
   OptionMap options_;
   std::string ice_ufrag_;
@@ -248,6 +244,7 @@
   IceRole ice_role_;
   uint64 tiebreaker_;
   uint32 remote_candidate_generation_;
+  IceGatheringState gathering_state_;
 
   int check_receiving_delay_;
   int receiving_timeout_;
diff --git a/webrtc/p2p/base/p2ptransportchannel_unittest.cc b/webrtc/p2p/base/p2ptransportchannel_unittest.cc
index d0277f4..485449f 100644
--- a/webrtc/p2p/base/p2ptransportchannel_unittest.cc
+++ b/webrtc/p2p/base/p2ptransportchannel_unittest.cc
@@ -297,10 +297,8 @@
       const std::string& remote_ice_pwd) {
     cricket::P2PTransportChannel* channel = new cricket::P2PTransportChannel(
         "test content name", component, NULL, GetAllocator(endpoint));
-    channel->SignalRequestSignaling.connect(
-        this, &P2PTransportChannelTestBase::OnChannelRequestSignaling);
-    channel->SignalCandidateReady.connect(this,
-        &P2PTransportChannelTestBase::OnCandidate);
+    channel->SignalCandidateGathered.connect(
+        this, &P2PTransportChannelTestBase::OnCandidate);
     channel->SignalReadPacket.connect(
         this, &P2PTransportChannelTestBase::OnReadPacket);
     channel->SignalRoleConflict.connect(
@@ -314,6 +312,7 @@
     channel->SetIceRole(GetEndpoint(endpoint)->ice_role());
     channel->SetIceTiebreaker(GetEndpoint(endpoint)->GetIceTiebreaker());
     channel->Connect();
+    channel->MaybeStartGathering();
     return channel;
   }
   void DestroyChannels() {
@@ -389,10 +388,9 @@
   }
 
   bool IsLocalToPrflxOrTheReverse(const Result& expected) {
-    return ((expected.local_type == "local" &&
-             expected.remote_type == "prflx") ||
-            (expected.local_type == "prflx" &&
-             expected.remote_type == "local"));
+    return (
+        (expected.local_type == "local" && expected.remote_type == "prflx") ||
+        (expected.local_type == "prflx" && expected.remote_type == "local"));
   }
 
   // Return true if the approprite parts of the expected Result, based
@@ -512,8 +510,8 @@
         ep2_ch1()->best_connection()) {
       int32 converge_start = rtc::Time(), converge_time;
       int converge_wait = 2000;
-      EXPECT_TRUE_WAIT_MARGIN(CheckCandidate1(expected),
-                              converge_wait, converge_wait);
+      EXPECT_TRUE_WAIT_MARGIN(CheckCandidate1(expected), converge_wait,
+                              converge_wait);
       // Also do EXPECT_EQ on each part so that failures are more verbose.
       ExpectCandidate1(expected);
 
@@ -562,7 +560,7 @@
   }
 
   // This test waits for the transport to become receiving and writable on both
-  // end points. Once they are, the end points set new local ice credentials to
+  // end points. Once they are, the end points set new local ice credentials and
   // restart the ice gathering. Finally it waits for the transport to select a
   // new connection using the newly generated ice candidates.
   // Before calling this function the end points must be configured.
@@ -582,8 +580,10 @@
 
     ep1_ch1()->SetIceCredentials(kIceUfrag[2], kIcePwd[2]);
     ep1_ch1()->SetRemoteIceCredentials(kIceUfrag[3], kIcePwd[3]);
+    ep1_ch1()->MaybeStartGathering();
     ep2_ch1()->SetIceCredentials(kIceUfrag[3], kIcePwd[3]);
     ep2_ch1()->SetRemoteIceCredentials(kIceUfrag[2], kIcePwd[2]);
+    ep2_ch1()->MaybeStartGathering();
 
     EXPECT_TRUE_WAIT_MARGIN(LocalCandidate(ep1_ch1())->generation() !=
                             old_local_candidate1->generation(),
@@ -626,9 +626,6 @@
     TestSendRecv(1);
   }
 
-  void OnChannelRequestSignaling(cricket::TransportChannelImpl* channel) {
-    channel->OnSignalingReady();
-  }
   // We pass the candidates directly to the other side.
   void OnCandidate(cricket::TransportChannelImpl* ch,
                    const cricket::Candidate& c) {
@@ -669,7 +666,7 @@
         }
         LOG(LS_INFO) << "Candidate(" << data->channel->component() << "->"
                      << rch->component() << "): " << c.ToString();
-        rch->OnCandidate(c);
+        rch->AddRemoteCandidate(c);
         break;
       }
     }
@@ -804,8 +801,10 @@
   static const Result* kMatrixSharedUfrag[NUM_CONFIGS][NUM_CONFIGS];
   static const Result* kMatrixSharedSocketAsGice[NUM_CONFIGS][NUM_CONFIGS];
   static const Result* kMatrixSharedSocketAsIce[NUM_CONFIGS][NUM_CONFIGS];
-  void ConfigureEndpoints(Config config1, Config config2,
-                          int allocator_flags1, int allocator_flags2) {
+  void ConfigureEndpoints(Config config1,
+                          Config config2,
+                          int allocator_flags1,
+                          int allocator_flags2) {
     ServerAddresses stun_servers;
     stun_servers.insert(kStunAddr);
     GetEndpoint(0)->allocator_.reset(
@@ -821,8 +820,8 @@
 
     cricket::RelayServerConfig relay_server(cricket::RELAY_TURN);
     relay_server.credentials = kRelayCredentials;
-    relay_server.ports.push_back(cricket::ProtocolAddress(
-        kTurnUdpIntAddr, cricket::PROTO_UDP, false));
+    relay_server.ports.push_back(
+        cricket::ProtocolAddress(kTurnUdpIntAddr, cricket::PROTO_UDP, false));
     GetEndpoint(0)->allocator_->AddRelay(relay_server);
     GetEndpoint(1)->allocator_->AddRelay(relay_server);
 
@@ -1026,15 +1025,14 @@
 
 // The actual tests that exercise all the various configurations.
 // Test names are of the form P2PTransportChannelTest_TestOPENToNAT_FULL_CONE
-#define P2P_TEST_DECLARATION(x, y, z) \
-  TEST_F(P2PTransportChannelTest, z##Test##x##To##y) { \
-    ConfigureEndpoints(x, y, \
-                       PORTALLOCATOR_ENABLE_SHARED_SOCKET, \
-                       PORTALLOCATOR_ENABLE_SHARED_SOCKET);  \
-    if (kMatrixSharedSocketAsIce[x][y] != NULL) \
-      Test(*kMatrixSharedSocketAsIce[x][y]); \
-    else \
-    LOG(LS_WARNING) << "Not yet implemented"; \
+#define P2P_TEST_DECLARATION(x, y, z)                            \
+  TEST_F(P2PTransportChannelTest, z##Test##x##To##y) {           \
+    ConfigureEndpoints(x, y, PORTALLOCATOR_ENABLE_SHARED_SOCKET, \
+                       PORTALLOCATOR_ENABLE_SHARED_SOCKET);      \
+    if (kMatrixSharedSocketAsIce[x][y] != NULL)                  \
+      Test(*kMatrixSharedSocketAsIce[x][y]);                     \
+    else                                                         \
+      LOG(LS_WARNING) << "Not yet implemented";                  \
   }
 
 #define P2P_TEST(x, y) \
@@ -1089,8 +1087,7 @@
 // Test that we restart candidate allocation when local ufrag&pwd changed.
 // Standard Ice protocol is used.
 TEST_F(P2PTransportChannelTest, HandleUfragPwdChange) {
-  ConfigureEndpoints(OPEN, OPEN,
-                     kDefaultPortAllocatorFlags,
+  ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
                      kDefaultPortAllocatorFlags);
   CreateChannels(1);
   TestHandleIceUfragPasswordChanged();
@@ -1099,8 +1096,7 @@
 
 // Test the operation of GetStats.
 TEST_F(P2PTransportChannelTest, GetStats) {
-  ConfigureEndpoints(OPEN, OPEN,
-                     kDefaultPortAllocatorFlags,
+  ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
                      kDefaultPortAllocatorFlags);
   CreateChannels(1);
   EXPECT_TRUE_WAIT_MARGIN(ep1_ch1()->receiving() && ep1_ch1()->writable() &&
@@ -1126,8 +1122,7 @@
 // Test that we properly create a connection on a STUN ping from unknown address
 // when the signaling is slow.
 TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignaling) {
-  ConfigureEndpoints(OPEN, OPEN,
-                     kDefaultPortAllocatorFlags,
+  ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
                      kDefaultPortAllocatorFlags);
   // Emulate no remote credentials coming in.
   set_clear_remote_candidates_ufrag_pwd(false);
@@ -1171,8 +1166,7 @@
 // Test that we properly create a connection on a STUN ping from unknown address
 // when the signaling is slow and the end points are behind NAT.
 TEST_F(P2PTransportChannelTest, PeerReflexiveCandidateBeforeSignalingWithNAT) {
-  ConfigureEndpoints(OPEN, NAT_SYMMETRIC,
-                     kDefaultPortAllocatorFlags,
+  ConfigureEndpoints(OPEN, NAT_SYMMETRIC, kDefaultPortAllocatorFlags,
                      kDefaultPortAllocatorFlags);
   // Emulate no remote credentials coming in.
   set_clear_remote_candidates_ufrag_pwd(false);
@@ -1214,8 +1208,7 @@
 // Test that if remote candidates don't have ufrag and pwd, we still work.
 TEST_F(P2PTransportChannelTest, RemoteCandidatesWithoutUfragPwd) {
   set_clear_remote_candidates_ufrag_pwd(true);
-  ConfigureEndpoints(OPEN, OPEN,
-                     kDefaultPortAllocatorFlags,
+  ConfigureEndpoints(OPEN, OPEN, kDefaultPortAllocatorFlags,
                      kDefaultPortAllocatorFlags);
   CreateChannels(1);
   const cricket::Connection* best_connection = NULL;
@@ -1230,8 +1223,7 @@
 // Test that a host behind NAT cannot be reached when incoming_only
 // is set to true.
 TEST_F(P2PTransportChannelTest, IncomingOnlyBlocked) {
-  ConfigureEndpoints(NAT_FULL_CONE, OPEN,
-                     kDefaultPortAllocatorFlags,
+  ConfigureEndpoints(NAT_FULL_CONE, OPEN, kDefaultPortAllocatorFlags,
                      kDefaultPortAllocatorFlags);
 
   SetAllocatorFlags(0, kOnlyLocalPorts);
@@ -1252,8 +1244,7 @@
 // Test that a peer behind NAT can connect to a peer that has
 // incoming_only flag set.
 TEST_F(P2PTransportChannelTest, IncomingOnlyOpen) {
-  ConfigureEndpoints(OPEN, NAT_FULL_CONE,
-                     kDefaultPortAllocatorFlags,
+  ConfigureEndpoints(OPEN, NAT_FULL_CONE, kDefaultPortAllocatorFlags,
                      kDefaultPortAllocatorFlags);
 
   SetAllocatorFlags(0, kOnlyLocalPorts);
@@ -1414,11 +1405,10 @@
 
 // Testing forceful TURN connections.
 TEST_F(P2PTransportChannelTest, TestForceTurn) {
-  ConfigureEndpoints(NAT_PORT_RESTRICTED, NAT_SYMMETRIC,
-                     kDefaultPortAllocatorFlags |
-                         cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET,
-                     kDefaultPortAllocatorFlags |
-                         cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET);
+  ConfigureEndpoints(
+      NAT_PORT_RESTRICTED, NAT_SYMMETRIC,
+      kDefaultPortAllocatorFlags | cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET,
+      kDefaultPortAllocatorFlags | cricket::PORTALLOCATOR_ENABLE_SHARED_SOCKET);
   set_force_relay(true);
 
   SetAllocationStepDelay(0, kMinimumStepDelay);
@@ -1473,8 +1463,7 @@
 TEST_F(P2PTransportChannelSameNatTest, TestConesBehindSameCone) {
   ConfigureEndpoints(NAT_FULL_CONE, NAT_FULL_CONE, NAT_FULL_CONE);
   Test(P2PTransportChannelTestBase::Result(
-      "prflx", "udp", "stun", "udp",
-      "stun", "udp", "prflx", "udp", 1000));
+      "prflx", "udp", "stun", "udp", "stun", "udp", "prflx", "udp", 1000));
 }
 
 // Test what happens when we have multiple available pathways.
@@ -1560,11 +1549,12 @@
 
 
   // Remove the public interface, add the alternate interface, and allocate
-  // a new generation of candidates for the new interface (via Connect()).
+  // a new generation of candidates for the new interface (via
+  // MaybeStartGathering()).
   LOG(LS_INFO) << "Draining...";
   AddAddress(1, kAlternateAddrs[1]);
   RemoveAddress(1, kPublicAddrs[1]);
-  ep2_ch1()->Connect();
+  ep2_ch1()->MaybeStartGathering();
 
   // We should switch over to use the alternate address after
   // an exchange of pings.
@@ -1591,17 +1581,11 @@
 
  protected:
   void PrepareChannel(cricket::P2PTransportChannel* ch) {
-    ch->SignalRequestSignaling.connect(
-        this, &P2PTransportChannelPingTest::OnChannelRequestSignaling);
     ch->SetIceRole(cricket::ICEROLE_CONTROLLING);
     ch->SetIceCredentials(kIceUfrag[0], kIcePwd[0]);
     ch->SetRemoteIceCredentials(kIceUfrag[1], kIcePwd[1]);
   }
 
-  void OnChannelRequestSignaling(cricket::TransportChannelImpl* channel) {
-    channel->OnSignalingReady();
-  }
-
   cricket::Candidate CreateCandidate(const std::string& ip,
                                      int port,
                                      int priority) {
@@ -1648,8 +1632,9 @@
   cricket::P2PTransportChannel ch("trigger checks", 1, nullptr, &pa);
   PrepareChannel(&ch);
   ch.Connect();
-  ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1));
-  ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 2));
+  ch.MaybeStartGathering();
+  ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1));
+  ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 2));
 
   cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
   cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
@@ -1672,8 +1657,9 @@
   cricket::P2PTransportChannel ch("trigger checks", 1, nullptr, &pa);
   PrepareChannel(&ch);
   ch.Connect();
-  ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1));
-  ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 2));
+  ch.MaybeStartGathering();
+  ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1));
+  ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 2));
 
   cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
   cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
@@ -1696,16 +1682,17 @@
   cricket::P2PTransportChannel ch("connection resurrection", 1, nullptr, &pa);
   PrepareChannel(&ch);
   ch.Connect();
+  ch.MaybeStartGathering();
 
   // Create conn1 and keep track of original candidate priority.
-  ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1));
+  ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1));
   cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
   ASSERT_TRUE(conn1 != nullptr);
   uint32 remote_priority = conn1->remote_candidate().priority();
 
   // Create a higher priority candidate and make the connection
   // receiving/writable. This will prune conn1.
-  ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 2));
+  ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 2));
   cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
   ASSERT_TRUE(conn2 != nullptr);
   conn2->ReceivedPing();
@@ -1752,7 +1739,8 @@
   EXPECT_EQ(500, ch.receiving_timeout());
   EXPECT_EQ(50, ch.check_receiving_delay());
   ch.Connect();
-  ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1));
+  ch.MaybeStartGathering();
+  ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1));
   cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
   ASSERT_TRUE(conn1 != nullptr);
 
@@ -1773,14 +1761,15 @@
   PrepareChannel(&ch);
   ch.SetIceRole(cricket::ICEROLE_CONTROLLED);
   ch.Connect();
-  ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 1));
+  ch.MaybeStartGathering();
+  ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 1));
   cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
   ASSERT_TRUE(conn1 != nullptr);
   EXPECT_EQ(conn1, ch.best_connection());
 
   // When a higher priority candidate comes in, the new connection is chosen
   // as the best connection.
-  ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 10));
+  ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 10));
   cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
   ASSERT_TRUE(conn1 != nullptr);
   EXPECT_EQ(conn2, ch.best_connection());
@@ -1788,7 +1777,7 @@
   // If a stun request with use-candidate attribute arrives, the receiving
   // connection will be set as the best connection, even though
   // its priority is lower.
-  ch.OnCandidate(CreateCandidate("3.3.3.3", 3, 1));
+  ch.AddRemoteCandidate(CreateCandidate("3.3.3.3", 3, 1));
   cricket::Connection* conn3 = WaitForConnectionTo(&ch, "3.3.3.3", 3);
   ASSERT_TRUE(conn3 != nullptr);
   // Because it has a lower priority, the best connection is still conn2.
@@ -1803,7 +1792,7 @@
   // Even if another higher priority candidate arrives,
   // it will not be set as the best connection because the best connection
   // is nominated by the controlling side.
-  ch.OnCandidate(CreateCandidate("4.4.4.4", 4, 100));
+  ch.AddRemoteCandidate(CreateCandidate("4.4.4.4", 4, 100));
   cricket::Connection* conn4 = WaitForConnectionTo(&ch, "4.4.4.4", 4);
   ASSERT_TRUE(conn4 != nullptr);
   EXPECT_EQ(conn3, ch.best_connection());
@@ -1828,6 +1817,7 @@
   PrepareChannel(&ch);
   ch.SetIceRole(cricket::ICEROLE_CONTROLLED);
   ch.Connect();
+  ch.MaybeStartGathering();
   // A minimal STUN message with prflx priority.
   cricket::IceMessage request;
   request.SetType(cricket::STUN_BINDING_REQUEST);
@@ -1846,7 +1836,7 @@
   EXPECT_EQ(conn1, ch.best_connection());
 
   // Another connection is nominated via use_candidate.
-  ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 1));
+  ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 1));
   cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
   ASSERT_TRUE(conn2 != nullptr);
   // Because it has a lower priority, the best connection is still conn1.
@@ -1892,7 +1882,8 @@
   PrepareChannel(&ch);
   ch.SetIceRole(cricket::ICEROLE_CONTROLLED);
   ch.Connect();
-  ch.OnCandidate(CreateCandidate("1.1.1.1", 1, 10));
+  ch.MaybeStartGathering();
+  ch.AddRemoteCandidate(CreateCandidate("1.1.1.1", 1, 10));
   cricket::Connection* conn1 = WaitForConnectionTo(&ch, "1.1.1.1", 1);
   ASSERT_TRUE(conn1 != nullptr);
   EXPECT_EQ(conn1, ch.best_connection());
@@ -1900,7 +1891,7 @@
   // If a data packet is received on conn2, the best connection should
   // switch to conn2 because the controlled side must mirror the media path
   // chosen by the controlling side.
-  ch.OnCandidate(CreateCandidate("2.2.2.2", 2, 1));
+  ch.AddRemoteCandidate(CreateCandidate("2.2.2.2", 2, 1));
   cricket::Connection* conn2 = WaitForConnectionTo(&ch, "2.2.2.2", 2);
   ASSERT_TRUE(conn2 != nullptr);
   conn2->ReceivedPing();  // Start receiving.
diff --git a/webrtc/p2p/base/portallocator.cc b/webrtc/p2p/base/portallocator.cc
index 76455b5..b97ad55 100644
--- a/webrtc/p2p/base/portallocator.cc
+++ b/webrtc/p2p/base/portallocator.cc
@@ -8,6 +8,7 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
+#include "webrtc/base/checks.h"
 #include "webrtc/p2p/base/portallocator.h"
 
 namespace cricket {
@@ -21,8 +22,10 @@
       component_(component),
       flags_(flags),
       generation_(0),
-      username_(ice_ufrag),
-      password_(ice_pwd) {
+      ice_ufrag_(ice_ufrag),
+      ice_pwd_(ice_pwd) {
+  RTC_DCHECK(!ice_ufrag.empty());
+  RTC_DCHECK(!ice_pwd.empty());
 }
 
 PortAllocatorSession* PortAllocator::CreateSession(
diff --git a/webrtc/p2p/base/portallocator.h b/webrtc/p2p/base/portallocator.h
index b754376..6e3efa8 100644
--- a/webrtc/p2p/base/portallocator.h
+++ b/webrtc/p2p/base/portallocator.h
@@ -74,11 +74,10 @@
 class PortAllocatorSession : public sigslot::has_slots<> {
  public:
   // Content name passed in mostly for logging and debugging.
-  // TODO(mallinath) - Change username and password to ice_ufrag and ice_pwd.
   PortAllocatorSession(const std::string& content_name,
                        int component,
-                       const std::string& username,
-                       const std::string& password,
+                       const std::string& ice_ufrag,
+                       const std::string& ice_pwd,
                        uint32 flags);
 
   // Subclasses should clean up any ports created.
@@ -103,9 +102,14 @@
   virtual void set_generation(uint32 generation) { generation_ = generation; }
   sigslot::signal1<PortAllocatorSession*> SignalDestroyed;
 
+  const std::string& ice_ufrag() const { return ice_ufrag_; }
+  const std::string& ice_pwd() const { return ice_pwd_; }
+
  protected:
-  const std::string& username() const { return username_; }
-  const std::string& password() const { return password_; }
+  // TODO(deadbeef): Get rid of these when everyone switches to ice_ufrag and
+  // ice_pwd.
+  const std::string& username() const { return ice_ufrag_; }
+  const std::string& password() const { return ice_pwd_; }
 
   std::string content_name_;
   int component_;
@@ -113,8 +117,8 @@
  private:
   uint32 flags_;
   uint32 generation_;
-  std::string username_;
-  std::string password_;
+  std::string ice_ufrag_;
+  std::string ice_pwd_;
 };
 
 class PortAllocator : public sigslot::has_slots<> {
diff --git a/webrtc/p2p/base/session.cc b/webrtc/p2p/base/session.cc
index 23680b9..83edb63 100644
--- a/webrtc/p2p/base/session.cc
+++ b/webrtc/p2p/base/session.cc
@@ -10,11 +10,6 @@
 
 #include "webrtc/p2p/base/session.h"
 
-#include "webrtc/p2p/base/dtlstransport.h"
-#include "webrtc/p2p/base/p2ptransport.h"
-#include "webrtc/p2p/base/transport.h"
-#include "webrtc/p2p/base/transportchannelproxy.h"
-#include "webrtc/p2p/base/transportinfo.h"
 #include "webrtc/base/bind.h"
 #include "webrtc/base/common.h"
 #include "webrtc/base/helpers.h"
@@ -22,266 +17,15 @@
 #include "webrtc/base/scoped_ptr.h"
 #include "webrtc/base/stringencode.h"
 #include "webrtc/base/sslstreamadapter.h"
-
+#include "webrtc/p2p/base/transport.h"
+#include "webrtc/p2p/base/transportinfo.h"
+#include "webrtc/p2p/base/transportcontroller.h"
 #include "webrtc/p2p/base/constants.h"
 
 namespace cricket {
 
 using rtc::Bind;
 
-TransportProxy::~TransportProxy() {
-  for (ChannelMap::iterator iter = channels_.begin();
-       iter != channels_.end(); ++iter) {
-    iter->second->SignalDestroyed(iter->second);
-    delete iter->second;
-  }
-}
-
-TransportChannel* TransportProxy::GetChannel(int component) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  return GetChannelProxy(component);
-}
-
-TransportChannel* TransportProxy::CreateChannel(int component) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  ASSERT(GetChannel(component) == NULL);
-  ASSERT(!transport_->get()->HasChannel(component));
-
-  // We always create a proxy in case we need to change out the transport later.
-  TransportChannelProxy* channel_proxy =
-      new TransportChannelProxy(content_name(), component);
-  channels_[component] = channel_proxy;
-
-  // If we're already negotiated, create an impl and hook it up to the proxy
-  // channel. If we're connecting, create an impl but don't hook it up yet.
-  if (negotiated_) {
-    CreateChannelImpl_w(component);
-    SetChannelImplFromTransport_w(channel_proxy, component);
-  } else if (connecting_) {
-    CreateChannelImpl_w(component);
-  }
-  return channel_proxy;
-}
-
-bool TransportProxy::HasChannel(int component) {
-  return transport_->get()->HasChannel(component);
-}
-
-void TransportProxy::DestroyChannel(int component) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  TransportChannelProxy* channel_proxy = GetChannelProxy(component);
-  if (channel_proxy) {
-    // If the state of TransportProxy is not NEGOTIATED then
-    // TransportChannelProxy and its impl are not connected. Both must
-    // be connected before deletion.
-    //
-    // However, if we haven't entered the connecting state then there
-    // is no implementation to hook up.
-    if (connecting_ && !negotiated_) {
-      SetChannelImplFromTransport_w(channel_proxy, component);
-    }
-
-    channels_.erase(component);
-    channel_proxy->SignalDestroyed(channel_proxy);
-    delete channel_proxy;
-  }
-}
-
-void TransportProxy::ConnectChannels() {
-  if (!connecting_) {
-    if (!negotiated_) {
-      for (auto& iter : channels_) {
-        CreateChannelImpl(iter.first);
-      }
-    }
-    connecting_ = true;
-  }
-  // TODO(juberti): Right now Transport::ConnectChannels doesn't work if we
-  // don't have any channels yet, so we need to allow this method to be called
-  // multiple times. Once we fix Transport, we can move this call inside the
-  // if (!connecting_) block.
-  transport_->get()->ConnectChannels();
-}
-
-void TransportProxy::CompleteNegotiation() {
-  if (!negotiated_) {
-    // Negotiating assumes connecting_ has happened and
-    // implementations exist. If not we need to create the
-    // implementations.
-    for (auto& iter : channels_) {
-      if (!connecting_) {
-        CreateChannelImpl(iter.first);
-      }
-      SetChannelImplFromTransport(iter.second, iter.first);
-    }
-    negotiated_ = true;
-  }
-}
-
-void TransportProxy::AddSentCandidates(const Candidates& candidates) {
-  for (Candidates::const_iterator cand = candidates.begin();
-       cand != candidates.end(); ++cand) {
-    sent_candidates_.push_back(*cand);
-  }
-}
-
-void TransportProxy::AddUnsentCandidates(const Candidates& candidates) {
-  for (Candidates::const_iterator cand = candidates.begin();
-       cand != candidates.end(); ++cand) {
-    unsent_candidates_.push_back(*cand);
-  }
-}
-
-TransportChannelProxy* TransportProxy::GetChannelProxy(int component) const {
-  ChannelMap::const_iterator iter = channels_.find(component);
-  return (iter != channels_.end()) ? iter->second : NULL;
-}
-
-void TransportProxy::CreateChannelImpl(int component) {
-  worker_thread_->Invoke<void>(Bind(
-      &TransportProxy::CreateChannelImpl_w, this, component));
-}
-
-void TransportProxy::CreateChannelImpl_w(int component) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  transport_->get()->CreateChannel(component);
-}
-
-void TransportProxy::SetChannelImplFromTransport(TransportChannelProxy* proxy,
-                                                 int component) {
-  worker_thread_->Invoke<void>(Bind(
-      &TransportProxy::SetChannelImplFromTransport_w, this, proxy, component));
-}
-
-void TransportProxy::SetChannelImplFromTransport_w(TransportChannelProxy* proxy,
-                                                   int component) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  TransportChannelImpl* impl = transport_->get()->GetChannel(component);
-  ASSERT(impl != NULL);
-  ReplaceChannelImpl_w(proxy, impl);
-}
-
-void TransportProxy::ReplaceChannelImpl(TransportChannelProxy* proxy,
-                                        TransportChannelImpl* impl) {
-  worker_thread_->Invoke<void>(Bind(
-      &TransportProxy::ReplaceChannelImpl_w, this, proxy, impl));
-}
-
-void TransportProxy::ReplaceChannelImpl_w(TransportChannelProxy* proxy,
-                                          TransportChannelImpl* impl) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  ASSERT(proxy != NULL);
-  proxy->SetImplementation(impl);
-}
-
-// This function muxes |this| onto |target| by repointing |this| at
-// |target|'s transport and setting our TransportChannelProxies
-// to point to |target|'s underlying implementations.
-bool TransportProxy::SetupMux(TransportProxy* target) {
-  // Bail out if there's nothing to do.
-  if (transport_ == target->transport_) {
-    return true;
-  }
-
-  // Run through all channels and remove any non-rtp transport channels before
-  // setting target transport channels.
-  for (ChannelMap::const_iterator iter = channels_.begin();
-       iter != channels_.end(); ++iter) {
-    if (!target->transport_->get()->HasChannel(iter->first)) {
-      // Remove if channel doesn't exist in |transport_|.
-      ReplaceChannelImpl(iter->second, NULL);
-    } else {
-      // Replace the impl for all the TransportProxyChannels with the channels
-      // from |target|'s transport. Fail if there's not an exact match.
-      ReplaceChannelImpl(
-          iter->second, target->transport_->get()->CreateChannel(iter->first));
-    }
-  }
-
-  // Now replace our transport. Must happen afterwards because
-  // it deletes all impls as a side effect.
-  transport_ = target->transport_;
-  transport_->get()->SignalCandidatesReady.connect(
-      this, &TransportProxy::OnTransportCandidatesReady);
-  set_candidates_allocated(target->candidates_allocated());
-  return true;
-}
-
-void TransportProxy::SetIceRole(IceRole role) {
-  transport_->get()->SetIceRole(role);
-}
-
-bool TransportProxy::SetLocalTransportDescription(
-    const TransportDescription& description,
-    ContentAction action,
-    std::string* error_desc) {
-  // If this is an answer, finalize the negotiation.
-  if (action == CA_ANSWER) {
-    CompleteNegotiation();
-  }
-  bool result = transport_->get()->SetLocalTransportDescription(description,
-                                                                action,
-                                                                error_desc);
-  if (result)
-    local_description_set_ = true;
-  return result;
-}
-
-bool TransportProxy::SetRemoteTransportDescription(
-    const TransportDescription& description,
-    ContentAction action,
-    std::string* error_desc) {
-  // If this is an answer, finalize the negotiation.
-  if (action == CA_ANSWER) {
-    CompleteNegotiation();
-  }
-  bool result = transport_->get()->SetRemoteTransportDescription(description,
-                                                                 action,
-                                                                 error_desc);
-  if (result)
-    remote_description_set_ = true;
-  return result;
-}
-
-void TransportProxy::OnSignalingReady() {
-  // If we're starting a new allocation sequence, reset our state.
-  set_candidates_allocated(false);
-  transport_->get()->OnSignalingReady();
-}
-
-bool TransportProxy::OnRemoteCandidates(const Candidates& candidates,
-                                        std::string* error) {
-  // Ensure the transport is negotiated before handling candidates.
-  // TODO(juberti): Remove this once everybody calls SetLocalTD.
-  CompleteNegotiation();
-
-  // Ignore candidates for if the proxy content_name doesn't match the content
-  // name of the actual transport. This stops video candidates from being sent
-  // down to the audio transport when BUNDLE is enabled.
-  if (content_name_ != transport_->get()->content_name()) {
-    return true;
-  }
-
-  // Verify each candidate before passing down to transport layer.
-  for (Candidates::const_iterator cand = candidates.begin();
-       cand != candidates.end(); ++cand) {
-    if (!transport_->get()->VerifyCandidate(*cand, error))
-      return false;
-    if (!HasChannel(cand->component())) {
-      *error = "Candidate has unknown component: " + cand->ToString() +
-               " for content: " + content_name_;
-      return false;
-    }
-  }
-  transport_->get()->OnRemoteCandidates(candidates);
-  return true;
-}
-
-void TransportProxy::SetCertificate(
-    const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
-  transport_->get()->SetCertificate(certificate);
-}
-
 std::string BaseSession::StateToString(State state) {
   switch (state) {
     case STATE_INIT:
@@ -326,7 +70,6 @@
                          rtc::Thread* worker_thread,
                          PortAllocator* port_allocator,
                          const std::string& sid,
-                         const std::string& content_type,
                          bool initiator)
     : state_(STATE_INIT),
       error_(ERROR_NONE),
@@ -334,13 +77,11 @@
       worker_thread_(worker_thread),
       port_allocator_(port_allocator),
       sid_(sid),
-      content_type_(content_type),
-      initiator_(initiator),
-      ssl_max_version_(rtc::SSL_PROTOCOL_DTLS_10),
-      ice_tiebreaker_(rtc::CreateRandomId64()),
-      role_switch_(false),
-      ice_receiving_timeout_(-1) {
+      transport_controller_(new TransportController(signaling_thread,
+                                                    worker_thread,
+                                                    port_allocator)) {
   ASSERT(signaling_thread->IsCurrent());
+  set_initiator(initiator);
 }
 
 BaseSession::~BaseSession() {
@@ -350,11 +91,6 @@
   LogState(state_, STATE_DEINIT);
   state_ = STATE_DEINIT;
   SignalState(this, state_);
-
-  for (TransportMap::iterator iter = transports_.begin();
-       iter != transports_.end(); ++iter) {
-    delete iter->second;
-  }
 }
 
 const SessionDescription* BaseSession::local_description() const {
@@ -384,37 +120,23 @@
     remote_description_.reset(sdesc);
 }
 
+void BaseSession::set_initiator(bool initiator) {
+  initiator_ = initiator;
+
+  IceRole ice_role = initiator ? ICEROLE_CONTROLLING : ICEROLE_CONTROLLED;
+  transport_controller_->SetIceRole(ice_role);
+}
+
 const SessionDescription* BaseSession::initiator_description() const {
   // TODO(tommi): Assert on thread correctness.
   return initiator_ ? local_description_.get() : remote_description_.get();
 }
 
-bool BaseSession::SetCertificate(
-    const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
-  if (certificate_)
-    return false;
-  if (!certificate)
-    return false;
-  certificate_ = certificate;
-  for (TransportMap::iterator iter = transports_.begin();
-       iter != transports_.end(); ++iter) {
-    iter->second->SetCertificate(certificate_);
-  }
-  return true;
-}
-
-bool BaseSession::SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) {
-  if (state_ != STATE_INIT) {
-    return false;
-  }
-
-  ssl_max_version_ = version;
-  return true;
-}
-
 bool BaseSession::PushdownTransportDescription(ContentSource source,
                                                ContentAction action,
                                                std::string* error_desc) {
+  ASSERT(signaling_thread()->IsCurrent());
+
   if (source == CS_LOCAL) {
     return PushdownLocalTransportDescription(local_description(),
                                              action,
@@ -428,23 +150,17 @@
 bool BaseSession::PushdownLocalTransportDescription(
     const SessionDescription* sdesc,
     ContentAction action,
-    std::string* error_desc) {
-  // Update the Transports with the right information, and trigger them to
-  // start connecting.
-  for (TransportMap::iterator iter = transports_.begin();
-       iter != transports_.end(); ++iter) {
-    // If no transport info was in this session description, ret == false
-    // and we just skip this one.
-    TransportDescription tdesc;
-    bool ret = GetTransportDescription(
-        sdesc, iter->second->content_name(), &tdesc);
-    if (ret) {
-      if (!iter->second->SetLocalTransportDescription(tdesc, action,
-                                                      error_desc)) {
-        return false;
-      }
+    std::string* err) {
+  ASSERT(signaling_thread()->IsCurrent());
 
-      iter->second->ConnectChannels();
+  if (!sdesc) {
+    return false;
+  }
+
+  for (const TransportInfo& tinfo : sdesc->transport_infos()) {
+    if (!transport_controller_->SetLocalTransportDescription(
+            tinfo.content_name, tinfo.description, action, err)) {
+      return false;
     }
   }
 
@@ -454,134 +170,23 @@
 bool BaseSession::PushdownRemoteTransportDescription(
     const SessionDescription* sdesc,
     ContentAction action,
-    std::string* error_desc) {
-  // Update the Transports with the right information.
-  for (TransportMap::iterator iter = transports_.begin();
-       iter != transports_.end(); ++iter) {
-    TransportDescription tdesc;
+    std::string* err) {
+  ASSERT(signaling_thread()->IsCurrent());
 
-    // If no transport info was in this session description, ret == false
-    // and we just skip this one.
-    bool ret = GetTransportDescription(
-        sdesc, iter->second->content_name(), &tdesc);
-    if (ret) {
-      if (!iter->second->SetRemoteTransportDescription(tdesc, action,
-                                                       error_desc)) {
-        return false;
-      }
+  if (!sdesc) {
+    return false;
+  }
+
+  for (const TransportInfo& tinfo : sdesc->transport_infos()) {
+    if (!transport_controller_->SetRemoteTransportDescription(
+            tinfo.content_name, tinfo.description, action, err)) {
+      return false;
     }
   }
 
   return true;
 }
 
-void BaseSession::SetIceConnectionReceivingTimeout(int timeout_ms) {
-  ice_receiving_timeout_ = timeout_ms;
-  for (const auto& kv : transport_proxies()) {
-    Transport* transport = kv.second->impl();
-    if (transport) {
-      transport->SetChannelReceivingTimeout(timeout_ms);
-    }
-  }
-}
-
-TransportChannel* BaseSession::CreateChannel(const std::string& content_name,
-                                             int component) {
-  // We create the proxy "on demand" here because we need to support
-  // creating channels at any time, even before we send or receive
-  // initiate messages, which is before we create the transports.
-  TransportProxy* transproxy = GetOrCreateTransportProxy(content_name);
-  return transproxy->CreateChannel(component);
-}
-
-TransportChannel* BaseSession::GetChannel(const std::string& content_name,
-                                          int component) {
-  TransportProxy* transproxy = GetTransportProxy(content_name);
-  if (transproxy == NULL)
-    return NULL;
-
-  return transproxy->GetChannel(component);
-}
-
-void BaseSession::DestroyChannel(const std::string& content_name,
-                                 int component) {
-  TransportProxy* transproxy = GetTransportProxy(content_name);
-  ASSERT(transproxy != NULL);
-  transproxy->DestroyChannel(component);
-}
-
-TransportProxy* BaseSession::GetOrCreateTransportProxy(
-    const std::string& content_name) {
-  TransportProxy* transproxy = GetTransportProxy(content_name);
-  if (transproxy)
-    return transproxy;
-
-  Transport* transport = CreateTransport(content_name);
-  transport->SetIceRole(initiator_ ? ICEROLE_CONTROLLING : ICEROLE_CONTROLLED);
-  transport->SetIceTiebreaker(ice_tiebreaker_);
-  transport->SetSslMaxProtocolVersion(ssl_max_version_);
-  // TODO: Connect all the Transport signals to TransportProxy
-  // then to the BaseSession.
-  transport->SignalConnecting.connect(
-      this, &BaseSession::OnTransportConnecting);
-  transport->SignalWritableState.connect(
-      this, &BaseSession::OnTransportWritable);
-  transport->SignalReceivingState.connect(
-      this, &BaseSession::OnTransportReceiving);
-  transport->SignalRequestSignaling.connect(
-      this, &BaseSession::OnTransportRequestSignaling);
-  transport->SignalRouteChange.connect(
-      this, &BaseSession::OnTransportRouteChange);
-  transport->SignalCandidatesAllocationDone.connect(
-      this, &BaseSession::OnTransportCandidatesAllocationDone);
-  transport->SignalRoleConflict.connect(
-      this, &BaseSession::OnRoleConflict);
-  transport->SignalCompleted.connect(
-      this, &BaseSession::OnTransportCompleted);
-  transport->SignalFailed.connect(
-      this, &BaseSession::OnTransportFailed);
-
-  transproxy = new TransportProxy(worker_thread_, sid_, content_name,
-                                  new TransportWrapper(transport));
-  transproxy->SignalCandidatesReady.connect(
-      this, &BaseSession::OnTransportProxyCandidatesReady);
-  if (certificate_)
-    transproxy->SetCertificate(certificate_);
-  transports_[content_name] = transproxy;
-
-  return transproxy;
-}
-
-Transport* BaseSession::GetTransport(const std::string& content_name) {
-  TransportProxy* transproxy = GetTransportProxy(content_name);
-  if (transproxy == NULL)
-    return NULL;
-  return transproxy->impl();
-}
-
-TransportProxy* BaseSession::GetTransportProxy(
-    const std::string& content_name) {
-  TransportMap::iterator iter = transports_.find(content_name);
-  return (iter != transports_.end()) ? iter->second : NULL;
-}
-
-void BaseSession::DestroyTransportProxy(
-    const std::string& content_name) {
-  TransportMap::iterator iter = transports_.find(content_name);
-  if (iter != transports_.end()) {
-    delete iter->second;
-    transports_.erase(content_name);
-  }
-}
-
-Transport* BaseSession::CreateTransport(const std::string& content_name) {
-  Transport* transport = new DtlsTransport<P2PTransport>(
-      signaling_thread(), worker_thread(), content_name, port_allocator(),
-      certificate_);
-  transport->SetChannelReceivingTimeout(ice_receiving_timeout_);
-  return transport;
-}
-
 void BaseSession::SetState(State state) {
   ASSERT(signaling_thread_->IsCurrent());
   if (state != state_) {
@@ -601,180 +206,18 @@
   }
 }
 
-void BaseSession::OnSignalingReady() {
-  ASSERT(signaling_thread()->IsCurrent());
-  for (TransportMap::iterator iter = transports_.begin();
-       iter != transports_.end(); ++iter) {
-    iter->second->OnSignalingReady();
-  }
+void BaseSession::SetIceConnectionReceivingTimeout(int timeout_ms) {
+  transport_controller_->SetIceConnectionReceivingTimeout(timeout_ms);
 }
 
-// TODO(juberti): Since PushdownLocalTD now triggers the connection process to
-// start, remove this method once everyone calls PushdownLocalTD.
-void BaseSession::SpeculativelyConnectAllTransportChannels() {
-  // Put all transports into the connecting state.
-  for (TransportMap::iterator iter = transports_.begin();
-       iter != transports_.end(); ++iter) {
-    iter->second->ConnectChannels();
-  }
-}
-
-bool BaseSession::OnRemoteCandidates(const std::string& content_name,
-                                     const Candidates& candidates,
-                                     std::string* error) {
-  // Give candidates to the appropriate transport, and tell that transport
-  // to start connecting, if it's not already doing so.
-  TransportProxy* transproxy = GetTransportProxy(content_name);
-  if (!transproxy) {
-    *error = "Unknown content name " + content_name;
-    return false;
-  }
-  if (!transproxy->OnRemoteCandidates(candidates, error)) {
-    return false;
-  }
-  // TODO(juberti): Remove this call once we can be sure that we always have
-  // a local transport description (which will trigger the connection).
-  transproxy->ConnectChannels();
-  return true;
-}
-
-bool BaseSession::MaybeEnableMuxingSupport() {
-  // We need both a local and remote description to decide if we should mux.
-  if ((state_ == STATE_SENTINITIATE ||
-      state_ == STATE_RECEIVEDINITIATE) &&
-      ((local_description_ == NULL) ||
-      (remote_description_ == NULL))) {
-    return false;
-  }
-
-  // In order to perform the multiplexing, we need all proxies to be in the
-  // negotiated state, i.e. to have implementations underneath.
-  // Ensure that this is the case, regardless of whether we are going to mux.
-  for (TransportMap::iterator iter = transports_.begin();
-       iter != transports_.end(); ++iter) {
-    ASSERT(iter->second->negotiated());
-    if (!iter->second->negotiated()) {
-      return false;
-    }
-  }
-
-  // If both sides agree to BUNDLE, mux all the specified contents onto the
-  // transport belonging to the first content name in the BUNDLE group.
-  // If the contents are already muxed, this will be a no-op.
-  // TODO(juberti): Should this check that local and remote have configured
-  // BUNDLE the same way?
-  bool candidates_allocated = IsCandidateAllocationDone();
-  const ContentGroup* local_bundle_group =
-      local_description_->GetGroupByName(GROUP_TYPE_BUNDLE);
-  const ContentGroup* remote_bundle_group =
-      remote_description_->GetGroupByName(GROUP_TYPE_BUNDLE);
-  if (local_bundle_group && remote_bundle_group) {
-    if (!BundleContentGroup(local_bundle_group)) {
-      LOG(LS_WARNING) << "Failed to set up BUNDLE";
-      return false;
-    }
-
-    // If we weren't done gathering before, we might be done now, as a result
-    // of enabling mux.
-    if (!candidates_allocated) {
-      MaybeCandidateAllocationDone();
-    }
-  } else {
-    LOG(LS_INFO) << "BUNDLE group missing from remote or local description.";
-  }
-  return true;
-}
-
-bool BaseSession::BundleContentGroup(const ContentGroup* bundle_group) {
-  const std::string* content_name = bundle_group->FirstContentName();
-  if (!content_name) {
-    LOG(LS_INFO) << "No content names specified in BUNDLE group.";
-    return true;
-  }
-
-  TransportProxy* selected_proxy = GetTransportProxy(*content_name);
-  if (!selected_proxy) {
-    LOG(LS_WARNING) << "No transport found for content \""
-                    << *content_name << "\".";
-    return false;
-  }
-
-  for (TransportMap::iterator iter = transports_.begin();
-       iter != transports_.end(); ++iter) {
-    // If content is part of the mux group, then repoint its proxy at the
-    // transport object that we have chosen to mux onto. If the proxy
-    // is already pointing at the right object, it will be a no-op.
-    if (bundle_group->HasContentName(iter->first) &&
-        !iter->second->SetupMux(selected_proxy)) {
-      LOG(LS_WARNING) << "Failed to bundle " << iter->first << " to "
-                      << *content_name;
-      return false;
-    }
-    LOG(LS_INFO) << "Bundling " << iter->first << " to " << *content_name;
-  }
-
-  return true;
-}
-
-void BaseSession::OnTransportCandidatesAllocationDone(Transport* transport) {
-  // TODO(juberti): This is a clunky way of processing the done signal. Instead,
-  // TransportProxy should receive the done signal directly, set its allocated
-  // flag internally, and then reissue the done signal to Session.
-  // Overall we should make TransportProxy receive *all* the signals from
-  // Transport, since this removes the need to manually iterate over all
-  // the transports, as is needed to make sure signals are handled properly
-  // when BUNDLEing.
-  // TODO(juberti): Per b/7998978, devs and QA are hitting this assert in ways
-  // that make it prohibitively difficult to run dbg builds. Disabled for now.
-  //ASSERT(!IsCandidateAllocationDone());
-  for (TransportMap::iterator iter = transports_.begin();
-       iter != transports_.end(); ++iter) {
-    if (iter->second->impl() == transport) {
-      iter->second->set_candidates_allocated(true);
-    }
-  }
-  MaybeCandidateAllocationDone();
-}
-
-bool BaseSession::IsCandidateAllocationDone() const {
-  for (TransportMap::const_iterator iter = transports_.begin();
-       iter != transports_.end(); ++iter) {
-    if (!iter->second->candidates_allocated()) {
-      LOG(LS_INFO) << "Candidate allocation not done for "
-                   << iter->second->content_name();
-      return false;
-    }
-  }
-  return true;
-}
-
-void BaseSession::MaybeCandidateAllocationDone() {
-  if (IsCandidateAllocationDone()) {
-    LOG(LS_INFO) << "Candidate gathering is complete.";
-    OnCandidatesAllocationDone();
-  }
-}
-
-void BaseSession::OnRoleConflict() {
-  if (role_switch_) {
-    LOG(LS_WARNING) << "Repeat of role conflict signal from Transport.";
-    return;
-  }
-
-  role_switch_ = true;
-  for (TransportMap::iterator iter = transports_.begin();
-       iter != transports_.end(); ++iter) {
-    // Role will be reverse of initial role setting.
-    IceRole role = initiator_ ? ICEROLE_CONTROLLED : ICEROLE_CONTROLLING;
-    iter->second->SetIceRole(role);
-  }
+void BaseSession::MaybeStartGathering() {
+  transport_controller_->MaybeStartGathering();
 }
 
 void BaseSession::LogState(State old_state, State new_state) {
   LOG(LS_INFO) << "Session:" << id()
                << " Old state:" << StateToString(old_state)
-               << " New state:" << StateToString(new_state)
-               << " Type:" << content_type();
+               << " New state:" << StateToString(new_state);
 }
 
 // static
diff --git a/webrtc/p2p/base/session.h b/webrtc/p2p/base/session.h
index 8d7aa21..d8721fd 100644
--- a/webrtc/p2p/base/session.h
+++ b/webrtc/p2p/base/session.h
@@ -16,14 +16,14 @@
 #include <string>
 #include <vector>
 
-#include "webrtc/p2p/base/candidate.h"
-#include "webrtc/p2p/base/port.h"
-#include "webrtc/p2p/base/transport.h"
 #include "webrtc/base/refcount.h"
 #include "webrtc/base/rtccertificate.h"
 #include "webrtc/base/scoped_ptr.h"
 #include "webrtc/base/scoped_ref_ptr.h"
 #include "webrtc/base/socketaddress.h"
+#include "webrtc/p2p/base/candidate.h"
+#include "webrtc/p2p/base/port.h"
+#include "webrtc/p2p/base/transport.h"
 
 namespace cricket {
 
@@ -31,136 +31,8 @@
 class P2PTransportChannel;
 class Transport;
 class TransportChannel;
-class TransportChannelProxy;
 class TransportChannelImpl;
-
-typedef rtc::RefCountedObject<rtc::scoped_ptr<Transport> >
-TransportWrapper;
-
-// Bundles a Transport and ChannelMap together. ChannelMap is used to
-// create transport channels before receiving or sending a session
-// initiate, and for speculatively connecting channels.  Previously, a
-// session had one ChannelMap and transport.  Now, with multiple
-// transports per session, we need multiple ChannelMaps as well.
-
-typedef std::map<int, TransportChannelProxy*> ChannelMap;
-
-class TransportProxy : public sigslot::has_slots<> {
- public:
-  TransportProxy(
-      rtc::Thread* worker_thread,
-      const std::string& sid,
-      const std::string& content_name,
-      TransportWrapper* transport)
-      : worker_thread_(worker_thread),
-        sid_(sid),
-        content_name_(content_name),
-        transport_(transport),
-        connecting_(false),
-        negotiated_(false),
-        sent_candidates_(false),
-        candidates_allocated_(false),
-        local_description_set_(false),
-        remote_description_set_(false) {
-    transport_->get()->SignalCandidatesReady.connect(
-        this, &TransportProxy::OnTransportCandidatesReady);
-  }
-  ~TransportProxy();
-
-  const std::string& content_name() const { return content_name_; }
-  // TODO(juberti): It's not good form to expose the object you're wrapping,
-  // since callers can mutate it. Can we make this return a const Transport*?
-  Transport* impl() const { return transport_->get(); }
-
-  const std::string& type() const;
-  bool negotiated() const { return negotiated_; }
-  const Candidates& sent_candidates() const { return sent_candidates_; }
-  const Candidates& unsent_candidates() const { return unsent_candidates_; }
-  bool candidates_allocated() const { return candidates_allocated_; }
-  void set_candidates_allocated(bool allocated) {
-    candidates_allocated_ = allocated;
-  }
-
-  TransportChannel* GetChannel(int component);
-  TransportChannel* CreateChannel(int component);
-  bool HasChannel(int component);
-  void DestroyChannel(int component);
-
-  void AddSentCandidates(const Candidates& candidates);
-  void AddUnsentCandidates(const Candidates& candidates);
-  void ClearSentCandidates() { sent_candidates_.clear(); }
-  void ClearUnsentCandidates() { unsent_candidates_.clear(); }
-
-  // Start the connection process for any channels, creating impls if needed.
-  void ConnectChannels();
-  // Hook up impls to the proxy channels. Doesn't change connect state.
-  void CompleteNegotiation();
-
-  // Mux this proxy onto the specified proxy's transport.
-  bool SetupMux(TransportProxy* proxy);
-
-  // Simple functions that thunk down to the same functions on Transport.
-  void SetIceRole(IceRole role);
-  void SetCertificate(
-      const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
-  bool SetLocalTransportDescription(const TransportDescription& description,
-                                    ContentAction action,
-                                    std::string* error_desc);
-  bool SetRemoteTransportDescription(const TransportDescription& description,
-                                     ContentAction action,
-                                     std::string* error_desc);
-  void OnSignalingReady();
-  bool OnRemoteCandidates(const Candidates& candidates, std::string* error);
-
-  // Called when a transport signals that it has new candidates.
-  void OnTransportCandidatesReady(cricket::Transport* transport,
-                                  const Candidates& candidates) {
-    SignalCandidatesReady(this, candidates);
-  }
-
-  bool local_description_set() const {
-    return local_description_set_;
-  }
-  bool remote_description_set() const {
-    return remote_description_set_;
-  }
-
-  // Handles sending of ready candidates and receiving of remote candidates.
-  sigslot::signal2<TransportProxy*,
-                         const std::vector<Candidate>&> SignalCandidatesReady;
-
- private:
-  TransportChannelProxy* GetChannelProxy(int component) const;
-
-  // Creates a new channel on the Transport which causes the reference
-  // count to increment.
-  void CreateChannelImpl(int component);
-  void CreateChannelImpl_w(int component);
-
-  // Manipulators of transportchannelimpl in channel proxy.
-  void SetChannelImplFromTransport(TransportChannelProxy* proxy, int component);
-  void SetChannelImplFromTransport_w(TransportChannelProxy* proxy,
-                                     int component);
-  void ReplaceChannelImpl(TransportChannelProxy* proxy,
-                          TransportChannelImpl* impl);
-  void ReplaceChannelImpl_w(TransportChannelProxy* proxy,
-                            TransportChannelImpl* impl);
-
-  rtc::Thread* const worker_thread_;
-  const std::string sid_;
-  const std::string content_name_;
-  rtc::scoped_refptr<TransportWrapper> transport_;
-  bool connecting_;
-  bool negotiated_;
-  ChannelMap channels_;
-  Candidates sent_candidates_;
-  Candidates unsent_candidates_;
-  bool candidates_allocated_;
-  bool local_description_set_;
-  bool remote_description_set_;
-};
-
-typedef std::map<std::string, TransportProxy*> TransportMap;
+class TransportController;
 
 // Statistics for all the transports of this session.
 typedef std::map<std::string, TransportStats> TransportStatsMap;
@@ -224,7 +96,6 @@
               rtc::Thread* worker_thread,
               PortAllocator* port_allocator,
               const std::string& sid,
-              const std::string& content_type,
               bool initiator);
   virtual ~BaseSession();
 
@@ -236,14 +107,6 @@
   // The ID of this session.
   const std::string& id() const { return sid_; }
 
-  // TODO(juberti): This data is largely redundant, as it can now be obtained
-  // from local/remote_description(). Remove these functions and members.
-  // Returns the XML namespace identifying the type of this session.
-  const std::string& content_type() const { return content_type_; }
-
-  // Indicates whether we initiated this session.
-  bool initiator() const { return initiator_; }
-
   // Returns the application-level description given by our client.
   // If we are the recipient, this will be NULL until we send an accept.
   const SessionDescription* local_description() const;
@@ -260,6 +123,9 @@
   // Takes ownership of SessionDescription*
   void set_remote_description(SessionDescription* sdesc);
 
+  void set_initiator(bool initiator);
+  bool initiator() const { return initiator_; }
+
   const SessionDescription* initiator_description() const;
 
   // Returns the current state of the session.  See the enum above for details.
@@ -280,151 +146,29 @@
   // TODO(ronghuawu): remove the SetError method that doesn't take |error_desc|.
   virtual void SetError(Error error, const std::string& error_desc);
 
-  // Fired when the remote description is updated, with the updated
-  // contents.
-  sigslot::signal2<BaseSession* , const ContentInfos&>
-      SignalRemoteDescriptionUpdate;
-
-  // Fired when SetState is called (regardless if there's a state change), which
-  // indicates the session description might have be updated.
-  sigslot::signal2<BaseSession*, ContentAction> SignalNewLocalDescription;
-
-  // Fired when SetState is called (regardless if there's a state change), which
-  // indicates the session description might have be updated.
-  sigslot::signal2<BaseSession*, ContentAction> SignalNewRemoteDescription;
-
-  // Returns the transport that has been negotiated or NULL if
-  // negotiation is still in progress.
-  virtual Transport* GetTransport(const std::string& content_name);
-
-  // Creates a new channel with the given names.  This method may be called
-  // immediately after creating the session.  However, the actual
-  // implementation may not be fixed until transport negotiation completes.
-  // This will usually be called from the worker thread, but that
-  // shouldn't be an issue since the main thread will be blocked in
-  // Send when doing so.
-  virtual TransportChannel* CreateChannel(const std::string& content_name,
-                                          int component);
-
-  // Returns the channel with the given names.
-  virtual TransportChannel* GetChannel(const std::string& content_name,
-                                       int component);
-
-  // Destroys the channel with the given names.
-  // This will usually be called from the worker thread, but that
-  // shouldn't be an issue since the main thread will be blocked in
-  // Send when doing so.
-  virtual void DestroyChannel(const std::string& content_name,
-                              int component);
-
-  // Set the ice connection receiving timeout.
   void SetIceConnectionReceivingTimeout(int timeout_ms);
 
-  // For testing.
-  const rtc::scoped_refptr<rtc::RTCCertificate>&
-  certificate_for_testing() const {
-    return certificate_;
-  }
+  // Start gathering candidates for any new transports, or transports doing an
+  // ICE restart.
+  void MaybeStartGathering();
 
  protected:
-  // Specifies the identity to use in this session.
-  bool SetCertificate(
-      const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
-
-  bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version);
-
   bool PushdownTransportDescription(ContentSource source,
                                     ContentAction action,
                                     std::string* error_desc);
-  void set_initiator(bool initiator) { initiator_ = initiator; }
-
-  const TransportMap& transport_proxies() const { return transports_; }
-  // Get a TransportProxy by content_name or transport. NULL if not found.
-  TransportProxy* GetTransportProxy(const std::string& content_name);
-  void DestroyTransportProxy(const std::string& content_name);
-  // TransportProxy is owned by session.  Return proxy just for convenience.
-  TransportProxy* GetOrCreateTransportProxy(const std::string& content_name);
-  // Creates the actual transport object. Overridable for testing.
-  virtual Transport* CreateTransport(const std::string& content_name);
-
-  void OnSignalingReady();
-  void SpeculativelyConnectAllTransportChannels();
-  // Helper method to provide remote candidates to the transport.
-  bool OnRemoteCandidates(const std::string& content_name,
-                          const Candidates& candidates,
-                          std::string* error);
-
-  // This method will mux transport channels by content_name.
-  // First content is used for muxing.
-  bool MaybeEnableMuxingSupport();
-
-  // Called when a transport requests signaling.
-  virtual void OnTransportRequestSignaling(Transport* transport) {
-  }
-
-  // Called when the first channel of a transport begins connecting.  We use
-  // this to start a timer, to make sure that the connection completes in a
-  // reasonable amount of time.
-  virtual void OnTransportConnecting(Transport* transport) {
-  }
-
-  // Called when a transport changes its writable state.  We track this to make
-  // sure that the transport becomes writable within a reasonable amount of
-  // time.  If this does not occur, we signal an error.
-  virtual void OnTransportWritable(Transport* transport) {
-  }
-  virtual void OnTransportReadable(Transport* transport) {
-  }
-
-  virtual void OnTransportReceiving(Transport* transport) {
-  }
-
-  // Called when a transport has found its steady-state connections.
-  virtual void OnTransportCompleted(Transport* transport) {
-  }
-
-  // Called when a transport has failed permanently.
-  virtual void OnTransportFailed(Transport* transport) {
-  }
-
-  // Called when a transport signals that it has new candidates.
-  virtual void OnTransportProxyCandidatesReady(TransportProxy* proxy,
-                                               const Candidates& candidates) {
-  }
-
-  virtual void OnTransportRouteChange(
-      Transport* transport,
-      int component,
-      const cricket::Candidate& remote_candidate) {
-  }
-
-  virtual void OnTransportCandidatesAllocationDone(Transport* transport);
-
-  // Called when all transport channels allocated required candidates.
-  // This method should be used as an indication of candidates gathering process
-  // is completed and application can now send local candidates list to remote.
-  virtual void OnCandidatesAllocationDone() {
-  }
-
-  // Handles the ice role change callback from Transport. This must be
-  // propagated to all the transports.
-  virtual void OnRoleConflict();
 
   // Handles messages posted to us.
   virtual void OnMessage(rtc::Message *pmsg);
 
- protected:
-  bool IsCandidateAllocationDone() const;
+  TransportController* transport_controller() {
+    return transport_controller_.get();
+  }
 
+ protected:
   State state_;
   Error error_;
   std::string error_desc_;
 
-  // This method will delete the Transport and TransportChannelImpls
-  // and replace those with the Transport object of the first
-  // MediaContent in bundle_group.
-  bool BundleContentGroup(const ContentGroup* bundle_group);
-
  private:
   // Helper methods to push local and remote transport descriptions.
   bool PushdownLocalTransportDescription(
@@ -434,8 +178,6 @@
       const SessionDescription* sdesc, ContentAction action,
       std::string* error_desc);
 
-  void MaybeCandidateAllocationDone();
-
   // Log session state.
   void LogState(State old_state, State new_state);
 
@@ -449,21 +191,10 @@
   rtc::Thread* const worker_thread_;
   PortAllocator* const port_allocator_;
   const std::string sid_;
-  const std::string content_type_;
   bool initiator_;
-  rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
-  rtc::SSLProtocolVersion ssl_max_version_;
+  rtc::scoped_ptr<TransportController> transport_controller_;
   rtc::scoped_ptr<const SessionDescription> local_description_;
   rtc::scoped_ptr<SessionDescription> remote_description_;
-  uint64 ice_tiebreaker_;
-  // This flag will be set to true after the first role switch. This flag
-  // will enable us to stop any role switch during the call.
-  bool role_switch_;
-  TransportMap transports_;
-
-  // Timeout value in milliseconds for which no ICE connection receives
-  // any packets.
-  int ice_receiving_timeout_;
 };
 
 }  // namespace cricket
diff --git a/webrtc/p2p/base/session_unittest.cc b/webrtc/p2p/base/session_unittest.cc
deleted file mode 100644
index 3419cc3..0000000
--- a/webrtc/p2p/base/session_unittest.cc
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- *  Copyright 2015 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/base/gunit.h"
-#include "webrtc/base/helpers.h"
-#include "webrtc/base/scoped_ptr.h"
-#include "webrtc/base/thread.h"
-#include "webrtc/p2p/base/dtlstransportchannel.h"
-#include "webrtc/p2p/base/p2ptransportchannel.h"
-#include "webrtc/p2p/base/portallocator.h"
-#include "webrtc/p2p/base/session.h"
-#include "webrtc/p2p/base/transportchannelproxy.h"
-#include "webrtc/p2p/client/fakeportallocator.h"
-
-using cricket::BaseSession;
-using cricket::DtlsTransportChannelWrapper;
-using cricket::FakePortAllocator;
-using cricket::P2PTransportChannel;
-using cricket::PortAllocator;
-using cricket::TransportChannelProxy;
-using cricket::TransportProxy;
-
-class BaseSessionForTest : public BaseSession {
- public:
-  BaseSessionForTest(rtc::Thread* signaling_thread,
-                     rtc::Thread* worker_thread,
-                     PortAllocator* port_allocator,
-                     const std::string& sid,
-                     const std::string& content_type,
-                     bool initiator)
-      : BaseSession(signaling_thread,
-                    worker_thread,
-                    port_allocator,
-                    sid,
-                    content_type,
-                    initiator) {}
-  using BaseSession::GetOrCreateTransportProxy;
-};
-
-class BaseSessionTest : public testing::Test {
- public:
-  BaseSessionTest()
-      : port_allocator_(new FakePortAllocator(rtc::Thread::Current(), nullptr)),
-        session_(new BaseSessionForTest(rtc::Thread::Current(),
-                                        rtc::Thread::Current(),
-                                        port_allocator_.get(),
-                                        "123",
-                                        cricket::NS_JINGLE_RTP,
-                                        false)) {}
-  P2PTransportChannel* CreateChannel(const std::string& content,
-                                     int component) {
-    TransportProxy* transport_proxy =
-        session_->GetOrCreateTransportProxy(content);
-    // This hacking is needed in order that the p2p transport channel
-    // will be created in the following.
-    transport_proxy->CompleteNegotiation();
-
-    TransportChannelProxy* channel_proxy = static_cast<TransportChannelProxy*>(
-        session_->CreateChannel(content, component));
-    DtlsTransportChannelWrapper* dtls_channel =
-        static_cast<DtlsTransportChannelWrapper*>(channel_proxy->impl());
-    return static_cast<P2PTransportChannel*>(dtls_channel->channel());
-  }
-
-  rtc::scoped_ptr<PortAllocator> port_allocator_;
-  rtc::scoped_ptr<BaseSessionForTest> session_;
-};
-
-TEST_F(BaseSessionTest, TestSetIceReceivingTimeout) {
-  P2PTransportChannel* channel1 = CreateChannel("audio", 1);
-  ASSERT_NE(channel1, nullptr);
-  // These are the default values.
-  EXPECT_EQ(2500, channel1->receiving_timeout());
-  EXPECT_EQ(250, channel1->check_receiving_delay());
-  // Set the timeout to a different value.
-  session_->SetIceConnectionReceivingTimeout(1000);
-  EXPECT_EQ(1000, channel1->receiving_timeout());
-  EXPECT_EQ(100, channel1->check_receiving_delay());
-
-  // Even if a channel is created after setting the receiving timeout,
-  // the set timeout value is applied to the new channel.
-  P2PTransportChannel* channel2 = CreateChannel("video", 2);
-  ASSERT_NE(channel2, nullptr);
-  EXPECT_EQ(1000, channel2->receiving_timeout());
-  EXPECT_EQ(100, channel2->check_receiving_delay());
-
-  // Test minimum checking delay.
-  session_->SetIceConnectionReceivingTimeout(200);
-  EXPECT_EQ(200, channel1->receiving_timeout());
-  EXPECT_EQ(50, channel1->check_receiving_delay());
-  EXPECT_EQ(200, channel2->receiving_timeout());
-  EXPECT_EQ(50, channel2->check_receiving_delay());
-}
diff --git a/webrtc/p2p/base/transport.cc b/webrtc/p2p/base/transport.cc
index d626ad3..b7aba75 100644
--- a/webrtc/p2p/base/transport.cc
+++ b/webrtc/p2p/base/transport.cc
@@ -8,6 +8,8 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
+#include <utility>  // for std::pair
+
 #include "webrtc/p2p/base/transport.h"
 
 #include "webrtc/p2p/base/candidate.h"
@@ -22,39 +24,6 @@
 
 using rtc::Bind;
 
-enum {
-  MSG_ONSIGNALINGREADY = 1,
-  MSG_ONREMOTECANDIDATE,
-  MSG_WRITESTATE,
-  MSG_REQUESTSIGNALING,
-  MSG_CANDIDATEREADY,
-  MSG_ROUTECHANGE,
-  MSG_CONNECTING,
-  MSG_CANDIDATEALLOCATIONCOMPLETE,
-  MSG_ROLECONFLICT,
-  MSG_COMPLETED,
-  MSG_FAILED,
-  MSG_RECEIVINGSTATE,
-};
-
-struct ChannelParams : public rtc::MessageData {
-  ChannelParams() : channel(NULL), candidate(NULL) {}
-  explicit ChannelParams(int component)
-      : component(component), channel(NULL), candidate(NULL) {}
-  explicit ChannelParams(Candidate* candidate)
-      : channel(NULL), candidate(candidate) {
-  }
-
-  ~ChannelParams() {
-    delete candidate;
-  }
-
-  std::string name;
-  int component;
-  TransportChannelImpl* channel;
-  Candidate* candidate;
-};
-
 static bool VerifyIceParams(const TransportDescription& desc) {
   // For legacy protocols.
   if (desc.ice_ufrag.empty() && desc.ice_pwd.empty())
@@ -96,58 +65,59 @@
                                new_desc.ice_ufrag, new_desc.ice_pwd);
 }
 
-Transport::Transport(rtc::Thread* signaling_thread,
-                     rtc::Thread* worker_thread,
-                     const std::string& content_name,
-                     PortAllocator* allocator)
-    : signaling_thread_(signaling_thread),
-      worker_thread_(worker_thread),
-      content_name_(content_name),
-      allocator_(allocator),
-      destroyed_(false),
-      readable_(TRANSPORT_STATE_NONE),
-      writable_(TRANSPORT_STATE_NONE),
-      receiving_(TRANSPORT_STATE_NONE),
-      was_writable_(false),
-      connect_requested_(false),
-      ice_role_(ICEROLE_UNKNOWN),
-      tiebreaker_(0),
-      remote_ice_mode_(ICEMODE_FULL),
-      channel_receiving_timeout_(-1) {
-}
+Transport::Transport(const std::string& name, PortAllocator* allocator)
+    : name_(name), allocator_(allocator) {}
 
 Transport::~Transport() {
-  ASSERT(signaling_thread_->IsCurrent());
-  ASSERT(destroyed_);
+  ASSERT(channels_destroyed_);
+}
+
+bool Transport::AllChannelsCompleted() const {
+  // We aren't completed until at least one channel is complete, so if there
+  // are no channels, we aren't complete yet.
+  if (channels_.empty()) {
+    LOG(LS_INFO) << name() << " transport is not complete"
+                 << " because it has no TransportChannels";
+    return false;
+  }
+
+  // A Transport's ICE process is completed if all of its channels are writable,
+  // have finished allocating candidates, and have pruned all but one of their
+  // connections.
+  for (const auto& iter : channels_) {
+    const TransportChannelImpl* channel = iter.second.get();
+    bool complete =
+        channel->writable() &&
+        channel->GetState() == TransportChannelState::STATE_COMPLETED &&
+        channel->GetIceRole() == ICEROLE_CONTROLLING &&
+        channel->gathering_state() == kIceGatheringComplete;
+    if (!complete) {
+      LOG(LS_INFO) << name() << " transport is not complete"
+                   << " because a channel is still incomplete.";
+      return false;
+    }
+  }
+
+  return true;
+}
+
+bool Transport::AnyChannelFailed() const {
+  for (const auto& iter : channels_) {
+    if (iter.second->GetState() == TransportChannelState::STATE_FAILED) {
+      return true;
+    }
+  }
+  return false;
 }
 
 void Transport::SetIceRole(IceRole role) {
-  worker_thread_->Invoke<void>(Bind(&Transport::SetIceRole_w, this, role));
-}
-
-void Transport::SetCertificate(
-    const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
-  worker_thread_->Invoke<void>(Bind(&Transport::SetCertificate_w, this,
-                                    certificate));
-}
-
-bool Transport::GetCertificate(
-    rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
-  // The identity is set on the worker thread, so for safety it must also be
-  // acquired on the worker thread.
-  return worker_thread_->Invoke<bool>(
-      Bind(&Transport::GetCertificate_w, this, certificate));
+  ice_role_ = role;
+  for (auto& iter : channels_) {
+    iter.second->SetIceRole(ice_role_);
+  }
 }
 
 bool Transport::GetRemoteSSLCertificate(rtc::SSLCertificate** cert) {
-  // Channels can be deleted on the worker thread, so for safety the remote
-  // certificate is acquired on the worker thread.
-  return worker_thread_->Invoke<bool>(
-      Bind(&Transport::GetRemoteSSLCertificate_w, this, cert));
-}
-
-bool Transport::GetRemoteSSLCertificate_w(rtc::SSLCertificate** cert) {
-  ASSERT(worker_thread()->IsCurrent());
   if (channels_.empty())
     return false;
 
@@ -156,12 +126,6 @@
 }
 
 void Transport::SetChannelReceivingTimeout(int timeout_ms) {
-  worker_thread_->Invoke<void>(
-      Bind(&Transport::SetChannelReceivingTimeout_w, this, timeout_ms));
-}
-
-void Transport::SetChannelReceivingTimeout_w(int timeout_ms) {
-  ASSERT(worker_thread()->IsCurrent());
   channel_receiving_timeout_ = timeout_ms;
   for (const auto& kv : channels_) {
     kv.second->SetReceivingTimeout(timeout_ms);
@@ -172,35 +136,73 @@
     const TransportDescription& description,
     ContentAction action,
     std::string* error_desc) {
-  return worker_thread_->Invoke<bool>(Bind(
-      &Transport::SetLocalTransportDescription_w, this,
-      description, action, error_desc));
+  bool ret = true;
+
+  if (!VerifyIceParams(description)) {
+    return BadTransportDescription("Invalid ice-ufrag or ice-pwd length",
+                                   error_desc);
+  }
+
+  if (local_description_ &&
+      IceCredentialsChanged(*local_description_, description)) {
+    IceRole new_ice_role =
+        (action == CA_OFFER) ? ICEROLE_CONTROLLING : ICEROLE_CONTROLLED;
+
+    // It must be called before ApplyLocalTransportDescription, which may
+    // trigger an ICE restart and depends on the new ICE role.
+    SetIceRole(new_ice_role);
+  }
+
+  local_description_.reset(new TransportDescription(description));
+
+  for (auto& iter : channels_) {
+    ret &= ApplyLocalTransportDescription(iter.second.get(), error_desc);
+  }
+  if (!ret) {
+    return false;
+  }
+
+  // If PRANSWER/ANSWER is set, we should decide transport protocol type.
+  if (action == CA_PRANSWER || action == CA_ANSWER) {
+    ret &= NegotiateTransportDescription(action, error_desc);
+  }
+  if (ret) {
+    local_description_set_ = true;
+    ConnectChannels();
+  }
+
+  return ret;
 }
 
 bool Transport::SetRemoteTransportDescription(
     const TransportDescription& description,
     ContentAction action,
     std::string* error_desc) {
-  return worker_thread_->Invoke<bool>(Bind(
-      &Transport::SetRemoteTransportDescription_w, this,
-      description, action, error_desc));
+  bool ret = true;
+
+  if (!VerifyIceParams(description)) {
+    return BadTransportDescription("Invalid ice-ufrag or ice-pwd length",
+                                   error_desc);
+  }
+
+  remote_description_.reset(new TransportDescription(description));
+  for (auto& iter : channels_) {
+    ret &= ApplyRemoteTransportDescription(iter.second.get(), error_desc);
+  }
+
+  // If PRANSWER/ANSWER is set, we should decide transport protocol type.
+  if (action == CA_PRANSWER || action == CA_ANSWER) {
+    ret = NegotiateTransportDescription(CA_OFFER, error_desc);
+  }
+  if (ret) {
+    remote_description_set_ = true;
+  }
+
+  return ret;
 }
 
 TransportChannelImpl* Transport::CreateChannel(int component) {
-  return worker_thread_->Invoke<TransportChannelImpl*>(Bind(
-      &Transport::CreateChannel_w, this, component));
-}
-
-TransportChannelImpl* Transport::CreateChannel_w(int component) {
-  ASSERT(worker_thread()->IsCurrent());
   TransportChannelImpl* impl;
-  // TODO(tommi): We don't really need to grab the lock until the actual call
-  // to insert() below and presumably hold it throughout initialization of
-  // |impl| after the impl_exists check.  Maybe we can factor that out to
-  // a separate function and not grab the lock in this function.
-  // Actually, we probably don't need to hold the lock while initializing
-  // |impl| since we can just do the insert when that's done.
-  rtc::CritScope cs(&crit_);
 
   // Create the entry if it does not exist.
   bool impl_exists = false;
@@ -216,7 +218,7 @@
 
   // Increase the ref count.
   iterator->second.AddRef();
-  destroyed_ = false;
+  channels_destroyed_ = false;
 
   if (impl_exists) {
     // If this is an existing channel, we should just return it without
@@ -228,23 +230,21 @@
   impl->SetIceRole(ice_role_);
   impl->SetIceTiebreaker(tiebreaker_);
   impl->SetReceivingTimeout(channel_receiving_timeout_);
-  // TODO(ronghuawu): Change CreateChannel_w to be able to return error since
-  // below Apply**Description_w calls can fail.
+  // TODO(ronghuawu): Change CreateChannel to be able to return error since
+  // below Apply**Description calls can fail.
   if (local_description_)
-    ApplyLocalTransportDescription_w(impl, NULL);
+    ApplyLocalTransportDescription(impl, NULL);
   if (remote_description_)
-    ApplyRemoteTransportDescription_w(impl, NULL);
+    ApplyRemoteTransportDescription(impl, NULL);
   if (local_description_ && remote_description_)
-    ApplyNegotiatedTransportDescription_w(impl, NULL);
+    ApplyNegotiatedTransportDescription(impl, NULL);
 
   impl->SignalWritableState.connect(this, &Transport::OnChannelWritableState);
   impl->SignalReceivingState.connect(this, &Transport::OnChannelReceivingState);
-  impl->SignalRequestSignaling.connect(
-      this, &Transport::OnChannelRequestSignaling);
-  impl->SignalCandidateReady.connect(this, &Transport::OnChannelCandidateReady);
+  impl->SignalGatheringState.connect(this, &Transport::OnChannelGatheringState);
+  impl->SignalCandidateGathered.connect(this,
+                                        &Transport::OnChannelCandidateGathered);
   impl->SignalRouteChange.connect(this, &Transport::OnChannelRouteChange);
-  impl->SignalCandidatesAllocationDone.connect(
-      this, &Transport::OnChannelCandidatesAllocationDone);
   impl->SignalRoleConflict.connect(this, &Transport::OnRoleConflict);
   impl->SignalConnectionRemoved.connect(
       this, &Transport::OnChannelConnectionRemoved);
@@ -254,36 +254,22 @@
     if (channels_.size() == 1) {
       // If this is the first channel, then indicate that we have started
       // connecting.
-      signaling_thread()->Post(this, MSG_CONNECTING, NULL);
+      SignalConnecting(this);
     }
   }
   return impl;
 }
 
 TransportChannelImpl* Transport::GetChannel(int component) {
-  // TODO(tommi,pthatcher): Since we're returning a pointer from the channels_
-  // map, shouldn't we assume that we're on the worker thread? (The pointer
-  // will be used outside of the lock).
-  // And if we're on the worker thread, which is the only thread that modifies
-  // channels_, can we skip grabbing the lock?
-  rtc::CritScope cs(&crit_);
   ChannelMap::iterator iter = channels_.find(component);
   return (iter != channels_.end()) ? iter->second.get() : NULL;
 }
 
 bool Transport::HasChannels() {
-  rtc::CritScope cs(&crit_);
   return !channels_.empty();
 }
 
 void Transport::DestroyChannel(int component) {
-  worker_thread_->Invoke<void>(Bind(
-      &Transport::DestroyChannel_w, this, component));
-}
-
-void Transport::DestroyChannel_w(int component) {
-  ASSERT(worker_thread()->IsCurrent());
-
   ChannelMap::iterator iter = channels_.find(component);
   if (iter == channels_.end())
     return;
@@ -293,34 +279,30 @@
   iter->second.DecRef();
   if (!iter->second.ref()) {
     impl = iter->second.get();
-    rtc::CritScope cs(&crit_);
     channels_.erase(iter);
   }
 
   if (connect_requested_ && channels_.empty()) {
     // We're no longer attempting to connect.
-    signaling_thread()->Post(this, MSG_CONNECTING, NULL);
+    SignalConnecting(this);
   }
 
   if (impl) {
-    // Check in case the deleted channel was the only non-writable channel.
-    OnChannelWritableState(impl);
     DestroyTransportChannel(impl);
+    // Need to update aggregate state after destroying a channel,
+    // for example if it was the only one that wasn't yet writable.
+    UpdateWritableState();
+    UpdateReceivingState();
+    UpdateGatheringState();
+    MaybeSignalCompleted();
   }
 }
 
 void Transport::ConnectChannels() {
-  ASSERT(signaling_thread()->IsCurrent());
-  worker_thread_->Invoke<void>(Bind(&Transport::ConnectChannels_w, this));
-}
-
-void Transport::ConnectChannels_w() {
-  ASSERT(worker_thread()->IsCurrent());
   if (connect_requested_ || channels_.empty())
     return;
 
   connect_requested_ = true;
-  signaling_thread()->Post(this, MSG_CANDIDATEREADY, NULL);
 
   if (!local_description_) {
     // TOOD(mallinath) : TransportDescription(TD) shouldn't be generated here.
@@ -329,38 +311,28 @@
     // Session.
     // Session must generate local TD before remote candidates pushed when
     // initiate request initiated by the remote.
-    LOG(LS_INFO) << "Transport::ConnectChannels_w: No local description has "
+    LOG(LS_INFO) << "Transport::ConnectChannels: No local description has "
                  << "been set. Will generate one.";
-    TransportDescription desc(std::vector<std::string>(),
-                              rtc::CreateRandomString(ICE_UFRAG_LENGTH),
-                              rtc::CreateRandomString(ICE_PWD_LENGTH),
-                              ICEMODE_FULL, CONNECTIONROLE_NONE, NULL,
-                              Candidates());
-    SetLocalTransportDescription_w(desc, CA_OFFER, NULL);
+    TransportDescription desc(
+        std::vector<std::string>(), rtc::CreateRandomString(ICE_UFRAG_LENGTH),
+        rtc::CreateRandomString(ICE_PWD_LENGTH), ICEMODE_FULL,
+        CONNECTIONROLE_NONE, NULL, Candidates());
+    SetLocalTransportDescription(desc, CA_OFFER, NULL);
   }
 
-  CallChannels_w(&TransportChannelImpl::Connect);
-  if (!channels_.empty()) {
-    signaling_thread()->Post(this, MSG_CONNECTING, NULL);
+  CallChannels(&TransportChannelImpl::Connect);
+  if (HasChannels()) {
+    SignalConnecting(this);
   }
 }
 
-void Transport::OnConnecting_s() {
-  ASSERT(signaling_thread()->IsCurrent());
-  SignalConnecting(this);
+void Transport::MaybeStartGathering() {
+  if (connect_requested_) {
+    CallChannels(&TransportChannelImpl::MaybeStartGathering);
+  }
 }
 
 void Transport::DestroyAllChannels() {
-  ASSERT(signaling_thread()->IsCurrent());
-  worker_thread_->Invoke<void>(Bind(&Transport::DestroyAllChannels_w, this));
-  worker_thread()->Clear(this);
-  signaling_thread()->Clear(this);
-  destroyed_ = true;
-}
-
-void Transport::DestroyAllChannels_w() {
-  ASSERT(worker_thread()->IsCurrent());
-
   std::vector<TransportChannelImpl*> impls;
   for (auto& iter : channels_) {
     iter.second.DecRef();
@@ -368,27 +340,15 @@
       impls.push_back(iter.second.get());
   }
 
-  {
-    rtc::CritScope cs(&crit_);
-    channels_.clear();
+  channels_.clear();
+
+  for (TransportChannelImpl* impl : impls) {
+    DestroyTransportChannel(impl);
   }
-
-  for (size_t i = 0; i < impls.size(); ++i)
-    DestroyTransportChannel(impls[i]);
+  channels_destroyed_ = true;
 }
 
-void Transport::OnSignalingReady() {
-  ASSERT(signaling_thread()->IsCurrent());
-  if (destroyed_) return;
-
-  worker_thread()->Post(this, MSG_ONSIGNALINGREADY, NULL);
-
-  // Notify the subclass.
-  OnTransportSignalingReady();
-}
-
-void Transport::CallChannels_w(TransportChannelFunc func) {
-  ASSERT(worker_thread()->IsCurrent());
+void Transport::CallChannels(TransportChannelFunc func) {
   for (const auto& iter : channels_) {
     ((iter.second.get())->*func)();
   }
@@ -427,14 +387,7 @@
 
 
 bool Transport::GetStats(TransportStats* stats) {
-  ASSERT(signaling_thread()->IsCurrent());
-  return worker_thread_->Invoke<bool>(Bind(
-      &Transport::GetStats_w, this, stats));
-}
-
-bool Transport::GetStats_w(TransportStats* stats) {
-  ASSERT(worker_thread()->IsCurrent());
-  stats->content_name = content_name();
+  stats->transport_name = name();
   stats->channel_stats.clear();
   for (auto iter : channels_) {
     ChannelMapEntry& entry = iter.second;
@@ -450,82 +403,45 @@
   return true;
 }
 
-bool Transport::GetSslRole(rtc::SSLRole* ssl_role) const {
-  return worker_thread_->Invoke<bool>(Bind(
-      &Transport::GetSslRole_w, this, ssl_role));
-}
+bool Transport::AddRemoteCandidates(const std::vector<Candidate>& candidates,
+                                    std::string* error) {
+  ASSERT(!channels_destroyed_);
+  // Verify each candidate before passing down to transport layer.
+  for (const Candidate& cand : candidates) {
+    if (!VerifyCandidate(cand, error)) {
+      return false;
+    }
+    if (!HasChannel(cand.component())) {
+      *error = "Candidate has unknown component: " + cand.ToString() +
+               " for content: " + name();
+      return false;
+    }
+  }
 
-bool Transport::SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) {
-  return worker_thread_->Invoke<bool>(Bind(
-      &Transport::SetSslMaxProtocolVersion_w, this, version));
-}
-
-void Transport::OnRemoteCandidates(const std::vector<Candidate>& candidates) {
   for (std::vector<Candidate>::const_iterator iter = candidates.begin();
        iter != candidates.end();
        ++iter) {
-    OnRemoteCandidate(*iter);
+    TransportChannelImpl* channel = GetChannel(iter->component());
+    if (channel != NULL) {
+      channel->AddRemoteCandidate(*iter);
+    }
   }
-}
-
-void Transport::OnRemoteCandidate(const Candidate& candidate) {
-  ASSERT(signaling_thread()->IsCurrent());
-  if (destroyed_) return;
-
-  if (!HasChannel(candidate.component())) {
-    LOG(LS_WARNING) << "Ignoring candidate for unknown component "
-                    << candidate.component();
-    return;
-  }
-
-  ChannelParams* params = new ChannelParams(new Candidate(candidate));
-  worker_thread()->Post(this, MSG_ONREMOTECANDIDATE, params);
-}
-
-void Transport::OnRemoteCandidate_w(const Candidate& candidate) {
-  ASSERT(worker_thread()->IsCurrent());
-  ChannelMap::iterator iter = channels_.find(candidate.component());
-  // It's ok for a channel to go away while this message is in transit.
-  if (iter != channels_.end()) {
-    iter->second->OnCandidate(candidate);
-  }
+  return true;
 }
 
 void Transport::OnChannelWritableState(TransportChannel* channel) {
-  ASSERT(worker_thread()->IsCurrent());
-  signaling_thread()->Post(this, MSG_WRITESTATE, NULL);
-
-  MaybeCompleted_w();
-}
-
-void Transport::OnChannelWritableState_s() {
-  ASSERT(signaling_thread()->IsCurrent());
-  TransportState writable = GetTransportState_s(TRANSPORT_WRITABLE_STATE);
-  if (writable_ != writable) {
-    was_writable_ = (writable_ == TRANSPORT_STATE_ALL);
-    writable_ = writable;
-    SignalWritableState(this);
-  }
+  LOG(LS_INFO) << name() << " TransportChannel " << channel->component()
+               << " writability changed to " << channel->writable()
+               << ". Check if transport is complete.";
+  UpdateWritableState();
+  MaybeSignalCompleted();
 }
 
 void Transport::OnChannelReceivingState(TransportChannel* channel) {
-  ASSERT(worker_thread()->IsCurrent());
-  signaling_thread()->Post(this, MSG_RECEIVINGSTATE);
+  UpdateReceivingState();
 }
 
-void Transport::OnChannelReceivingState_s() {
-  ASSERT(signaling_thread()->IsCurrent());
-  TransportState receiving = GetTransportState_s(TRANSPORT_RECEIVING_STATE);
-  if (receiving_ != receiving) {
-    receiving_ = receiving;
-    SignalReceivingState(this);
-  }
-}
-
-TransportState Transport::GetTransportState_s(TransportStateType state_type) {
-  ASSERT(signaling_thread()->IsCurrent());
-
-  rtc::CritScope cs(&crit_);
+TransportState Transport::GetTransportState(TransportStateType state_type) {
   bool any = false;
   bool all = !channels_.empty();
   for (const auto iter : channels_) {
@@ -553,106 +469,44 @@
   return TRANSPORT_STATE_NONE;
 }
 
-void Transport::OnChannelRequestSignaling(TransportChannelImpl* channel) {
-  ASSERT(worker_thread()->IsCurrent());
-  // Resetting ICE state for the channel.
-  ChannelMap::iterator iter = channels_.find(channel->component());
-  if (iter != channels_.end())
-    iter->second.set_candidates_allocated(false);
-  signaling_thread()->Post(this, MSG_REQUESTSIGNALING, nullptr);
+void Transport::OnChannelGatheringState(TransportChannelImpl* channel) {
+  ASSERT(channels_.find(channel->component()) != channels_.end());
+  UpdateGatheringState();
+  if (gathering_state_ == kIceGatheringComplete) {
+    // If UpdateGatheringState brought us to kIceGatheringComplete, check if
+    // our connection state is also "Completed". Otherwise, there's no point in
+    // checking (since it would only produce log messages).
+    MaybeSignalCompleted();
+  }
 }
 
-void Transport::OnChannelRequestSignaling_s() {
-  ASSERT(signaling_thread()->IsCurrent());
-  LOG(LS_INFO) << "Transport: " << content_name_ << ", allocating candidates";
-  SignalRequestSignaling(this);
-}
-
-void Transport::OnChannelCandidateReady(TransportChannelImpl* channel,
-                                        const Candidate& candidate) {
+void Transport::OnChannelCandidateGathered(TransportChannelImpl* channel,
+                                           const Candidate& candidate) {
   // We should never signal peer-reflexive candidates.
   if (candidate.type() == PRFLX_PORT_TYPE) {
     ASSERT(false);
     return;
   }
 
-  ASSERT(worker_thread()->IsCurrent());
-  rtc::CritScope cs(&crit_);
-  ready_candidates_.push_back(candidate);
-
-  // We hold any messages until the client lets us connect.
-  if (connect_requested_) {
-    signaling_thread()->Post(
-        this, MSG_CANDIDATEREADY, NULL);
-  }
-}
-
-void Transport::OnChannelCandidateReady_s() {
-  ASSERT(signaling_thread()->IsCurrent());
   ASSERT(connect_requested_);
-
   std::vector<Candidate> candidates;
-  {
-    rtc::CritScope cs(&crit_);
-    candidates.swap(ready_candidates_);
-  }
-
-  // we do the deleting of Candidate* here to keep the new above and
-  // delete below close to each other
-  if (!candidates.empty()) {
-    SignalCandidatesReady(this, candidates);
-  }
+  candidates.push_back(candidate);
+  SignalCandidatesGathered(this, candidates);
 }
 
 void Transport::OnChannelRouteChange(TransportChannel* channel,
                                      const Candidate& remote_candidate) {
-  ASSERT(worker_thread()->IsCurrent());
-  ChannelParams* params = new ChannelParams(new Candidate(remote_candidate));
-  params->channel = static_cast<cricket::TransportChannelImpl*>(channel);
-  signaling_thread()->Post(this, MSG_ROUTECHANGE, params);
-}
-
-void Transport::OnChannelRouteChange_s(const TransportChannel* channel,
-                                       const Candidate& remote_candidate) {
-  ASSERT(signaling_thread()->IsCurrent());
   SignalRouteChange(this, remote_candidate.component(), remote_candidate);
 }
 
-void Transport::OnChannelCandidatesAllocationDone(
-    TransportChannelImpl* channel) {
-  ASSERT(worker_thread()->IsCurrent());
-  ChannelMap::iterator iter = channels_.find(channel->component());
-  ASSERT(iter != channels_.end());
-  LOG(LS_INFO) << "Transport: " << content_name_ << ", component "
-               << channel->component() << " allocation complete";
-
-  iter->second.set_candidates_allocated(true);
-
-  // If all channels belonging to this Transport got signal, then
-  // forward this signal to upper layer.
-  // Can this signal arrive before all transport channels are created?
-  for (auto& iter : channels_) {
-    if (!iter.second.candidates_allocated())
-      return;
-  }
-  signaling_thread_->Post(this, MSG_CANDIDATEALLOCATIONCOMPLETE);
-
-  MaybeCompleted_w();
-}
-
-void Transport::OnChannelCandidatesAllocationDone_s() {
-  ASSERT(signaling_thread()->IsCurrent());
-  LOG(LS_INFO) << "Transport: " << content_name_ << " allocation complete";
-  SignalCandidatesAllocationDone(this);
-}
-
 void Transport::OnRoleConflict(TransportChannelImpl* channel) {
-  signaling_thread_->Post(this, MSG_ROLECONFLICT);
+  SignalRoleConflict();
 }
 
 void Transport::OnChannelConnectionRemoved(TransportChannelImpl* channel) {
-  ASSERT(worker_thread()->IsCurrent());
-  MaybeCompleted_w();
+  LOG(LS_INFO) << name() << " TransportChannel " << channel->component()
+               << " connection removed. Check if transport is complete.";
+  MaybeSignalCompleted();
 
   // Check if the state is now Failed.
   // Failed is only available in the Controlling ICE role.
@@ -660,158 +514,97 @@
     return;
   }
 
-  ChannelMap::iterator iter = channels_.find(channel->component());
-  ASSERT(iter != channels_.end());
-  // Failed can only occur after candidate allocation has stopped.
-  if (!iter->second.candidates_allocated()) {
+  // Failed can only occur after candidate gathering has stopped.
+  if (channel->gathering_state() != kIceGatheringComplete) {
     return;
   }
 
   if (channel->GetState() == TransportChannelState::STATE_FAILED) {
     // A Transport has failed if any of its channels have no remaining
     // connections.
-    signaling_thread_->Post(this, MSG_FAILED);
+    SignalFailed(this);
   }
 }
 
-void Transport::MaybeCompleted_w() {
-  ASSERT(worker_thread()->IsCurrent());
+void Transport::MaybeSignalCompleted() {
+  if (AllChannelsCompleted()) {
+    LOG(LS_INFO) << name() << " transport is complete"
+                 << " because all the channels are complete.";
+    SignalCompleted(this);
+  }
+  // TODO(deadbeef): Should we do anything if we previously were completed,
+  // but now are not (if, for example, a new remote candidate is added)?
+}
 
-  // When there is no channel created yet, calling this function could fire an
-  // IceConnectionCompleted event prematurely.
-  if (channels_.empty()) {
-    return;
+void Transport::UpdateGatheringState() {
+  IceGatheringState new_state = kIceGatheringNew;
+  bool any_gathering = false;
+  bool all_complete = !channels_.empty();
+  for (const auto& kv : channels_) {
+    any_gathering =
+        any_gathering || kv.second->gathering_state() != kIceGatheringNew;
+    all_complete =
+        all_complete && kv.second->gathering_state() == kIceGatheringComplete;
+  }
+  if (all_complete) {
+    new_state = kIceGatheringComplete;
+  } else if (any_gathering) {
+    new_state = kIceGatheringGathering;
   }
 
-  // A Transport's ICE process is completed if all of its channels are writable,
-  // have finished allocating candidates, and have pruned all but one of their
-  // connections.
-  for (const auto& iter : channels_) {
-    const TransportChannelImpl* channel = iter.second.get();
-    if (!(channel->writable() &&
-          channel->GetState() == TransportChannelState::STATE_COMPLETED &&
-          channel->GetIceRole() == ICEROLE_CONTROLLING &&
-          iter.second.candidates_allocated())) {
-      return;
+  if (gathering_state_ != new_state) {
+    gathering_state_ = new_state;
+    if (gathering_state_ == kIceGatheringGathering) {
+      LOG(LS_INFO) << "Transport: " << name_ << ", gathering candidates";
+    } else if (gathering_state_ == kIceGatheringComplete) {
+      LOG(LS_INFO) << "Transport " << name() << " gathering complete.";
     }
-  }
-
-  signaling_thread_->Post(this, MSG_COMPLETED);
-}
-
-void Transport::SetIceRole_w(IceRole role) {
-  ASSERT(worker_thread()->IsCurrent());
-  rtc::CritScope cs(&crit_);
-  ice_role_ = role;
-  for (auto& iter : channels_) {
-    iter.second->SetIceRole(ice_role_);
+    SignalGatheringState(this);
   }
 }
 
-void Transport::SetRemoteIceMode_w(IceMode mode) {
-  ASSERT(worker_thread()->IsCurrent());
-  remote_ice_mode_ = mode;
-  // Shouldn't channels be created after this method executed?
-  for (auto& iter : channels_) {
-    iter.second->SetRemoteIceMode(remote_ice_mode_);
+void Transport::UpdateReceivingState() {
+  TransportState receiving = GetTransportState(TRANSPORT_RECEIVING_STATE);
+  if (receiving_ != receiving) {
+    receiving_ = receiving;
+    SignalReceivingState(this);
   }
 }
 
-bool Transport::SetLocalTransportDescription_w(
-    const TransportDescription& desc,
-    ContentAction action,
-    std::string* error_desc) {
-  ASSERT(worker_thread()->IsCurrent());
-  bool ret = true;
-
-  if (!VerifyIceParams(desc)) {
-    return BadTransportDescription("Invalid ice-ufrag or ice-pwd length",
-                                   error_desc);
+void Transport::UpdateWritableState() {
+  TransportState writable = GetTransportState(TRANSPORT_WRITABLE_STATE);
+  LOG(LS_INFO) << name() << " transport writable state changed? " << writable_
+               << " => " << writable;
+  if (writable_ != writable) {
+    was_writable_ = (writable_ == TRANSPORT_STATE_ALL);
+    writable_ = writable;
+    SignalWritableState(this);
   }
-
-  // TODO(tommi,pthatcher): I'm not sure why we need to grab this lock at this
-  // point. |local_description_| seems to always be modified on the worker
-  // thread, so we should be able to use it here without grabbing the lock.
-  // However, we _might_ need it before the call to reset() below?
-  // Raw access to |local_description_| is granted to derived transports outside
-  // of locking (see local_description() in the header file).
-  // The contract is that the derived implementations must be aware of when the
-  // description might change and do appropriate synchronization.
-  rtc::CritScope cs(&crit_);
-  if (local_description_ && IceCredentialsChanged(*local_description_, desc)) {
-    IceRole new_ice_role = (action == CA_OFFER) ? ICEROLE_CONTROLLING
-                                                : ICEROLE_CONTROLLED;
-
-    // It must be called before ApplyLocalTransportDescription_w, which may
-    // trigger an ICE restart and depends on the new ICE role.
-    SetIceRole_w(new_ice_role);
-  }
-
-  local_description_.reset(new TransportDescription(desc));
-
-  for (auto& iter : channels_) {
-    ret &= ApplyLocalTransportDescription_w(iter.second.get(), error_desc);
-  }
-  if (!ret)
-    return false;
-
-  // If PRANSWER/ANSWER is set, we should decide transport protocol type.
-  if (action == CA_PRANSWER || action == CA_ANSWER) {
-    ret &= NegotiateTransportDescription_w(action, error_desc);
-  }
-  return ret;
 }
 
-bool Transport::SetRemoteTransportDescription_w(
-    const TransportDescription& desc,
-    ContentAction action,
-    std::string* error_desc) {
-  bool ret = true;
-
-  if (!VerifyIceParams(desc)) {
-    return BadTransportDescription("Invalid ice-ufrag or ice-pwd length",
-                                   error_desc);
-  }
-
-  // TODO(tommi,pthatcher): See todo for local_description_ above.
-  rtc::CritScope cs(&crit_);
-  remote_description_.reset(new TransportDescription(desc));
-  for (auto& iter : channels_) {
-    ret &= ApplyRemoteTransportDescription_w(iter.second.get(), error_desc);
-  }
-
-  // If PRANSWER/ANSWER is set, we should decide transport protocol type.
-  if (action == CA_PRANSWER || action == CA_ANSWER) {
-    ret = NegotiateTransportDescription_w(CA_OFFER, error_desc);
-  }
-  return ret;
-}
-
-bool Transport::ApplyLocalTransportDescription_w(TransportChannelImpl* ch,
-                                                 std::string* error_desc) {
-  ASSERT(worker_thread()->IsCurrent());
+bool Transport::ApplyLocalTransportDescription(TransportChannelImpl* ch,
+                                               std::string* error_desc) {
   ch->SetIceCredentials(local_description_->ice_ufrag,
                         local_description_->ice_pwd);
   return true;
 }
 
-bool Transport::ApplyRemoteTransportDescription_w(TransportChannelImpl* ch,
-                                                  std::string* error_desc) {
+bool Transport::ApplyRemoteTransportDescription(TransportChannelImpl* ch,
+                                                std::string* error_desc) {
   ch->SetRemoteIceCredentials(remote_description_->ice_ufrag,
                               remote_description_->ice_pwd);
   return true;
 }
 
-bool Transport::ApplyNegotiatedTransportDescription_w(
-    TransportChannelImpl* channel, std::string* error_desc) {
-  ASSERT(worker_thread()->IsCurrent());
+bool Transport::ApplyNegotiatedTransportDescription(
+    TransportChannelImpl* channel,
+    std::string* error_desc) {
   channel->SetRemoteIceMode(remote_ice_mode_);
   return true;
 }
 
-bool Transport::NegotiateTransportDescription_w(ContentAction local_role,
-                                                std::string* error_desc) {
-  ASSERT(worker_thread()->IsCurrent());
+bool Transport::NegotiateTransportDescription(ContentAction local_role,
+                                              std::string* error_desc) {
   // TODO(ekr@rtfm.com): This is ICE-specific stuff. Refactor into
   // P2PTransport.
 
@@ -819,7 +612,7 @@
   // ice_lite, this local end point should take CONTROLLING role.
   if (ice_role_ == ICEROLE_CONTROLLED &&
       remote_description_->ice_mode == ICEMODE_LITE) {
-    SetIceRole_w(ICEROLE_CONTROLLING);
+    SetIceRole(ICEROLE_CONTROLLING);
   }
 
   // Update remote ice_mode to all existing channels.
@@ -831,57 +624,10 @@
   // creation, we have the negotiation state saved until a new
   // negotiation happens.
   for (auto& iter : channels_) {
-    if (!ApplyNegotiatedTransportDescription_w(iter.second.get(), error_desc))
+    if (!ApplyNegotiatedTransportDescription(iter.second.get(), error_desc))
       return false;
   }
   return true;
 }
 
-void Transport::OnMessage(rtc::Message* msg) {
-  switch (msg->message_id) {
-    case MSG_ONSIGNALINGREADY:
-      CallChannels_w(&TransportChannelImpl::OnSignalingReady);
-      break;
-    case MSG_ONREMOTECANDIDATE: {
-        ChannelParams* params = static_cast<ChannelParams*>(msg->pdata);
-        OnRemoteCandidate_w(*params->candidate);
-        delete params;
-      }
-      break;
-    case MSG_CONNECTING:
-      OnConnecting_s();
-      break;
-    case MSG_WRITESTATE:
-      OnChannelWritableState_s();
-      break;
-    case MSG_RECEIVINGSTATE:
-      OnChannelReceivingState_s();
-      break;
-    case MSG_REQUESTSIGNALING:
-      OnChannelRequestSignaling_s();
-      break;
-    case MSG_CANDIDATEREADY:
-      OnChannelCandidateReady_s();
-      break;
-    case MSG_ROUTECHANGE: {
-        ChannelParams* params = static_cast<ChannelParams*>(msg->pdata);
-        OnChannelRouteChange_s(params->channel, *params->candidate);
-        delete params;
-      }
-      break;
-    case MSG_CANDIDATEALLOCATIONCOMPLETE:
-      OnChannelCandidatesAllocationDone_s();
-      break;
-    case MSG_ROLECONFLICT:
-      SignalRoleConflict();
-      break;
-    case MSG_COMPLETED:
-      SignalCompleted(this);
-      break;
-    case MSG_FAILED:
-      SignalFailed(this);
-      break;
-  }
-}
-
 }  // namespace cricket
diff --git a/webrtc/p2p/base/transport.h b/webrtc/p2p/base/transport.h
index 72a0895..cd763ab 100644
--- a/webrtc/p2p/base/transport.h
+++ b/webrtc/p2p/base/transport.h
@@ -15,15 +15,11 @@
 // state changes (in order to update the manager's state), and forwards
 // requests to begin connecting or to reset to each of the channels.
 //
-// On Threading:  Transport performs work on both the signaling and worker
-// threads.  For subclasses, the rule is that all signaling related calls will
-// be made on the signaling thread and all channel related calls (including
-// signaling for a channel) will be made on the worker thread.  When
-// information needs to be sent between the two threads, this class should do
-// the work (e.g., OnRemoteCandidate).
+// On Threading:  Transport performs work solely on the worker thread, and so
+// its methods should only be called on the worker thread.
 //
-// Note: Subclasses must call DestroyChannels() in their own constructors.
-// It is not possible to do so here because the subclass constructor will
+// Note: Subclasses must call DestroyChannels() in their own destructors.
+// It is not possible to do so here because the subclass destructor will
 // already have run.
 
 #ifndef WEBRTC_P2P_BASE_TRANSPORT_H_
@@ -36,16 +32,11 @@
 #include "webrtc/p2p/base/constants.h"
 #include "webrtc/p2p/base/sessiondescription.h"
 #include "webrtc/p2p/base/transportinfo.h"
-#include "webrtc/base/criticalsection.h"
 #include "webrtc/base/messagequeue.h"
 #include "webrtc/base/rtccertificate.h"
 #include "webrtc/base/sigslot.h"
 #include "webrtc/base/sslstreamadapter.h"
 
-namespace rtc {
-class Thread;
-}
-
 namespace cricket {
 
 class PortAllocator;
@@ -54,6 +45,26 @@
 
 typedef std::vector<Candidate> Candidates;
 
+// TODO(deadbeef): Unify with PeerConnectionInterface::IceConnectionState
+// once /talk/ and /webrtc/ are combined, and also switch to ENUM_NAME naming
+// style.
+enum IceConnectionState {
+  kIceConnectionConnecting = 0,
+  kIceConnectionFailed,
+  kIceConnectionConnected,  // Writable, but still checking one or more
+                            // connections
+  kIceConnectionCompleted,
+};
+
+// TODO(deadbeef): Unify with PeerConnectionInterface::IceConnectionState
+// once /talk/ and /webrtc/ are combined, and also switch to ENUM_NAME naming
+// style.
+enum IceGatheringState {
+  kIceGatheringNew = 0,
+  kIceGatheringGathering,
+  kIceGatheringComplete,
+};
+
 // For "writable" and "receiving", we need to differentiate between
 // none, all, and some.
 enum TransportState {
@@ -124,7 +135,7 @@
 
 // Information about the stats of a transport.
 struct TransportStats {
-  std::string content_name;
+  std::string transport_name;
   TransportChannelStatsList channel_stats;
 };
 
@@ -135,22 +146,13 @@
                            const std::string& new_ufrag,
                            const std::string& new_pwd);
 
-class Transport : public rtc::MessageHandler,
-                  public sigslot::has_slots<> {
+class Transport : public sigslot::has_slots<> {
  public:
-  Transport(rtc::Thread* signaling_thread,
-            rtc::Thread* worker_thread,
-            const std::string& content_name,
-            PortAllocator* allocator);
+  Transport(const std::string& name, PortAllocator* allocator);
   virtual ~Transport();
 
-  // Returns the signaling thread. The app talks to Transport on this thread.
-  rtc::Thread* signaling_thread() const { return signaling_thread_; }
-  // Returns the worker thread. The actual networking is done on this thread.
-  rtc::Thread* worker_thread() const { return worker_thread_; }
-
-  // Returns the content_name of this transport.
-  const std::string& content_name() const { return content_name_; }
+  // Returns the name of this transport.
+  const std::string& name() const { return name_; }
 
   // Returns the port allocator object for this transport.
   PortAllocator* port_allocator() { return allocator_; }
@@ -172,6 +174,14 @@
     return (receiving_ == TRANSPORT_STATE_SOME ||
             receiving_ == TRANSPORT_STATE_ALL);
   }
+  bool ready_for_remote_candidates() const {
+    return local_description_set_ && remote_description_set_;
+  }
+
+  bool AllChannelsCompleted() const;
+  bool AnyChannelFailed() const;
+
+  IceGatheringState gathering_state() const { return gathering_state_; }
 
   sigslot::signal1<Transport*> SignalWritableState;
   sigslot::signal1<Transport*> SignalReceivingState;
@@ -190,21 +200,23 @@
   void SetChannelReceivingTimeout(int timeout_ms);
 
   // Must be called before applying local session description.
-  void SetCertificate(
-      const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
+  virtual void SetLocalCertificate(
+      const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {}
 
-  // Get a copy of the local identity provided by SetIdentity.
-  bool GetCertificate(rtc::scoped_refptr<rtc::RTCCertificate>* certificate);
+  // Get a copy of the local certificate provided by SetLocalCertificate.
+  virtual bool GetLocalCertificate(
+      rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
+    return false;
+  }
 
   // Get a copy of the remote certificate in use by the specified channel.
   bool GetRemoteSSLCertificate(rtc::SSLCertificate** cert);
 
   // Create, destroy, and lookup the channels of this type by their components.
   TransportChannelImpl* CreateChannel(int component);
-  // Note: GetChannel may lead to race conditions, since the mutex is not held
-  // after the pointer is returned.
+
   TransportChannelImpl* GetChannel(int component);
-  // Note: HasChannel does not lead to race conditions, unlike GetChannel.
+
   bool HasChannel(int component) {
     return (NULL != GetChannel(component));
   }
@@ -212,7 +224,6 @@
   void DestroyChannel(int component);
 
   // Set the local TransportDescription to be used by TransportChannels.
-  // This should be called before ConnectChannels().
   bool SetLocalTransportDescription(const TransportDescription& description,
                                     ContentAction action,
                                     std::string* error_desc);
@@ -227,6 +238,11 @@
   void ConnectChannels();
   sigslot::signal1<Transport*> SignalConnecting;
 
+  // Tells channels to start gathering candidates if necessary.
+  // Should be called after ConnectChannels() has been called at least once,
+  // which will happen in SetLocalTransportDescription.
+  void MaybeStartGathering();
+
   // Resets all of the channels back to their initial state.  They are no
   // longer connecting.
   void ResetChannels();
@@ -236,20 +252,15 @@
 
   bool GetStats(TransportStats* stats);
 
-  // Before any stanza is sent, the manager will request signaling.  Once
-  // signaling is available, the client should call OnSignalingReady.  Once
-  // this occurs, the transport (or its channels) can send any waiting stanzas.
-  // OnSignalingReady invokes OnTransportSignalingReady and then forwards this
-  // signal to each channel.
-  sigslot::signal1<Transport*> SignalRequestSignaling;
-  void OnSignalingReady();
+  sigslot::signal1<Transport*> SignalGatheringState;
 
   // Handles sending of ready candidates and receiving of remote candidates.
-  sigslot::signal2<Transport*,
-                   const std::vector<Candidate>&> SignalCandidatesReady;
+  sigslot::signal2<Transport*, const std::vector<Candidate>&>
+      SignalCandidatesGathered;
 
-  sigslot::signal1<Transport*> SignalCandidatesAllocationDone;
-  void OnRemoteCandidates(const std::vector<Candidate>& candidates);
+  // Called when one or more candidates are ready from the remote peer.
+  bool AddRemoteCandidates(const std::vector<Candidate>& candidates,
+                           std::string* error);
 
   // If candidate is not acceptable, returns false and sets error.
   // Call this before calling OnRemoteCandidates.
@@ -264,10 +275,12 @@
   // Forwards the signal from TransportChannel to BaseSession.
   sigslot::signal0<> SignalRoleConflict;
 
-  virtual bool GetSslRole(rtc::SSLRole* ssl_role) const;
+  virtual bool GetSslRole(rtc::SSLRole* ssl_role) const { return false; }
 
   // Must be called before channel is starting to connect.
-  virtual bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version);
+  virtual bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version) {
+    return false;
+  }
 
  protected:
   // These are called by Create/DestroyChannel above in order to create or
@@ -275,9 +288,6 @@
   virtual TransportChannelImpl* CreateTransportChannel(int component) = 0;
   virtual void DestroyTransportChannel(TransportChannelImpl* channel) = 0;
 
-  // Informs the subclass that we received the signaling ready message.
-  virtual void OnTransportSignalingReady() {}
-
   // The current local transport description, for use by derived classes
   // when performing transport description negotiation.
   const TransportDescription* local_description() const {
@@ -290,53 +300,37 @@
     return remote_description_.get();
   }
 
-  virtual void SetCertificate_w(
-      const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {}
-
-  virtual bool GetCertificate_w(
-      rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
-    return false;
-  }
-
   // Pushes down the transport parameters from the local description, such
   // as the ICE ufrag and pwd.
   // Derived classes can override, but must call the base as well.
-  virtual bool ApplyLocalTransportDescription_w(TransportChannelImpl* channel,
-                                                std::string* error_desc);
+  virtual bool ApplyLocalTransportDescription(TransportChannelImpl* channel,
+                                              std::string* error_desc);
 
   // Pushes down remote ice credentials from the remote description to the
   // transport channel.
-  virtual bool ApplyRemoteTransportDescription_w(TransportChannelImpl* ch,
-                                                 std::string* error_desc);
+  virtual bool ApplyRemoteTransportDescription(TransportChannelImpl* ch,
+                                               std::string* error_desc);
 
   // Negotiates the transport parameters based on the current local and remote
   // transport description, such as the ICE role to use, and whether DTLS
   // should be activated.
   // Derived classes can negotiate their specific parameters here, but must call
   // the base as well.
-  virtual bool NegotiateTransportDescription_w(ContentAction local_role,
-                                               std::string* error_desc);
+  virtual bool NegotiateTransportDescription(ContentAction local_role,
+                                             std::string* error_desc);
 
   // Pushes down the transport parameters obtained via negotiation.
   // Derived classes can set their specific parameters here, but must call the
   // base as well.
-  virtual bool ApplyNegotiatedTransportDescription_w(
-      TransportChannelImpl* channel, std::string* error_desc);
-
-  virtual bool GetSslRole_w(rtc::SSLRole* ssl_role) const {
-    return false;
-  }
-
-  virtual bool SetSslMaxProtocolVersion_w(rtc::SSLProtocolVersion version) {
-    return false;
-  }
+  virtual bool ApplyNegotiatedTransportDescription(
+      TransportChannelImpl* channel,
+      std::string* error_desc);
 
  private:
   struct ChannelMapEntry {
-    ChannelMapEntry() : impl_(NULL), candidates_allocated_(false), ref_(0) {}
+    ChannelMapEntry() : impl_(NULL), ref_(0) {}
     explicit ChannelMapEntry(TransportChannelImpl *impl)
         : impl_(impl),
-          candidates_allocated_(false),
           ref_(0) {
     }
 
@@ -349,14 +343,9 @@
 
     TransportChannelImpl* get() const { return impl_; }
     TransportChannelImpl* operator->() const  { return impl_; }
-    void set_candidates_allocated(bool status) {
-      candidates_allocated_ = status;
-    }
-    bool candidates_allocated() const { return candidates_allocated_; }
 
-  private:
-    TransportChannelImpl *impl_;
-    bool candidates_allocated_;
+   private:
+    TransportChannelImpl* impl_;
     int ref_;
   };
 
@@ -369,92 +358,55 @@
   // Called when the receiving state of a channel changes.
   void OnChannelReceivingState(TransportChannel* channel);
 
-  // Called when a channel requests signaling.
-  void OnChannelRequestSignaling(TransportChannelImpl* channel);
+  // Called when a channel starts finishes gathering candidates
+  void OnChannelGatheringState(TransportChannelImpl* channel);
 
-  // Called when a candidate is ready from remote peer.
-  void OnRemoteCandidate(const Candidate& candidate);
   // Called when a candidate is ready from channel.
-  void OnChannelCandidateReady(TransportChannelImpl* channel,
-                               const Candidate& candidate);
+  void OnChannelCandidateGathered(TransportChannelImpl* channel,
+                                  const Candidate& candidate);
   void OnChannelRouteChange(TransportChannel* channel,
                             const Candidate& remote_candidate);
-  void OnChannelCandidatesAllocationDone(TransportChannelImpl* channel);
   // Called when there is ICE role change.
   void OnRoleConflict(TransportChannelImpl* channel);
   // Called when the channel removes a connection.
   void OnChannelConnectionRemoved(TransportChannelImpl* channel);
 
-  // Dispatches messages to the appropriate handler (below).
-  void OnMessage(rtc::Message* msg);
-
-  // These are versions of the above methods that are called only on a
-  // particular thread (s = signaling, w = worker).  The above methods post or
-  // send a message to invoke this version.
-  TransportChannelImpl* CreateChannel_w(int component);
-  void DestroyChannel_w(int component);
-  void ConnectChannels_w();
-  void ResetChannels_w();
-  void DestroyAllChannels_w();
-  void OnRemoteCandidate_w(const Candidate& candidate);
-  void OnChannelWritableState_s();
-  void OnChannelReceivingState_s();
-  void OnChannelRequestSignaling_s();
-  void OnConnecting_s();
-  void OnChannelRouteChange_s(const TransportChannel* channel,
-                              const Candidate& remote_candidate);
-  void OnChannelCandidatesAllocationDone_s();
-
   // Helper function that invokes the given function on every channel.
   typedef void (TransportChannelImpl::* TransportChannelFunc)();
-  void CallChannels_w(TransportChannelFunc func);
+  void CallChannels(TransportChannelFunc func);
 
   // Computes the AND and OR of the channel's read/write/receiving state
   // (argument picks the operation).
-  TransportState GetTransportState_s(TransportStateType type);
-
-  void OnChannelCandidateReady_s();
-
-  void SetIceRole_w(IceRole role);
-  void SetRemoteIceMode_w(IceMode mode);
-  bool SetLocalTransportDescription_w(const TransportDescription& desc,
-                                      ContentAction action,
-                                      std::string* error_desc);
-  bool SetRemoteTransportDescription_w(const TransportDescription& desc,
-                                       ContentAction action,
-                                       std::string* error_desc);
-  bool GetStats_w(TransportStats* infos);
-  bool GetRemoteSSLCertificate_w(rtc::SSLCertificate** cert);
-
-  void SetChannelReceivingTimeout_w(int timeout_ms);
+  TransportState GetTransportState(TransportStateType type);
 
   // Sends SignalCompleted if we are now in that state.
-  void MaybeCompleted_w();
+  void MaybeSignalCompleted();
 
-  rtc::Thread* const signaling_thread_;
-  rtc::Thread* const worker_thread_;
-  const std::string content_name_;
+  // Sends SignalGatheringState if gathering state changed
+  void UpdateGatheringState();
+
+  void UpdateWritableState();
+  void UpdateReceivingState();
+
+  const std::string name_;
   PortAllocator* const allocator_;
-  bool destroyed_;
-  TransportState readable_;
-  TransportState writable_;
-  TransportState receiving_;
-  bool was_writable_;
-  bool connect_requested_;
-  IceRole ice_role_;
-  uint64 tiebreaker_;
-  IceMode remote_ice_mode_;
-  int channel_receiving_timeout_;
+  bool channels_destroyed_ = false;
+  TransportState readable_ = TRANSPORT_STATE_NONE;
+  TransportState writable_ = TRANSPORT_STATE_NONE;
+  TransportState receiving_ = TRANSPORT_STATE_NONE;
+  bool was_writable_ = false;
+  bool connect_requested_ = false;
+  IceRole ice_role_ = ICEROLE_UNKNOWN;
+  uint64 tiebreaker_ = 0;
+  IceMode remote_ice_mode_ = ICEMODE_FULL;
+  int channel_receiving_timeout_ = -1;
   rtc::scoped_ptr<TransportDescription> local_description_;
   rtc::scoped_ptr<TransportDescription> remote_description_;
+  bool local_description_set_ = false;
+  bool remote_description_set_ = false;
+  IceGatheringState gathering_state_ = kIceGatheringNew;
 
-  // TODO(tommi): Make sure we only use this on the worker thread.
   ChannelMap channels_;
-  // Buffers the ready_candidates so that SignalCanidatesReady can
-  // provide them in multiples.
-  std::vector<Candidate> ready_candidates_;
-  // Protects changes to channels and messages
-  rtc::CriticalSection crit_;
 
   RTC_DISALLOW_COPY_AND_ASSIGN(Transport);
 };
diff --git a/webrtc/p2p/base/transport_unittest.cc b/webrtc/p2p/base/transport_unittest.cc
index 43b761a..9febfe3 100644
--- a/webrtc/p2p/base/transport_unittest.cc
+++ b/webrtc/p2p/base/transport_unittest.cc
@@ -11,8 +11,7 @@
 #include "webrtc/base/fakesslidentity.h"
 #include "webrtc/base/gunit.h"
 #include "webrtc/base/network.h"
-#include "webrtc/base/thread.h"
-#include "webrtc/p2p/base/fakesession.h"
+#include "webrtc/p2p/base/faketransportcontroller.h"
 #include "webrtc/p2p/base/p2ptransport.h"
 
 using cricket::Candidate;
@@ -35,9 +34,7 @@
                       public sigslot::has_slots<> {
  public:
   TransportTest()
-      : thread_(rtc::Thread::Current()),
-        transport_(new FakeTransport(
-            thread_, thread_, "test content name", NULL)),
+      : transport_(new FakeTransport("test content name")),
         channel_(NULL),
         connecting_signalled_(false),
         completed_(false),
@@ -73,7 +70,6 @@
     failed_ = true;
   }
 
-  rtc::Thread* thread_;
   rtc::scoped_ptr<FakeTransport> transport_;
   FakeTransportChannel* channel_;
   bool connecting_signalled_;
@@ -85,20 +81,7 @@
 TEST_F(TransportTest, TestConnectChannelsDoesSignal) {
   EXPECT_TRUE(SetupChannel());
   transport_->ConnectChannels();
-  EXPECT_FALSE(connecting_signalled_);
-
-  EXPECT_TRUE_WAIT(connecting_signalled_, 100);
-}
-
-// Test that DestroyAllChannels kills any pending OnConnecting signals.
-TEST_F(TransportTest, TestDestroyAllClearsPosts) {
-  EXPECT_TRUE(transport_->CreateChannel(1) != NULL);
-
-  transport_->ConnectChannels();
-  transport_->DestroyAllChannels();
-
-  thread_->ProcessMessages(0);
-  EXPECT_FALSE(connecting_signalled_);
+  EXPECT_TRUE(connecting_signalled_);
 }
 
 // This test verifies channels are created with proper ICE
@@ -232,7 +215,7 @@
                                                         NULL));
 
   channel_->SetConnectionCount(2);
-  channel_->SignalCandidatesAllocationDone(channel_);
+  channel_->SetCandidatesGatheringComplete();
   channel_->SetWritable(true);
   EXPECT_TRUE_WAIT(transport_->all_channels_writable(), 100);
   // ICE is not yet completed because there is still more than one connection.
diff --git a/webrtc/p2p/base/transportchannel.cc b/webrtc/p2p/base/transportchannel.cc
index 5d5a7c9..97a4113 100644
--- a/webrtc/p2p/base/transportchannel.cc
+++ b/webrtc/p2p/base/transportchannel.cc
@@ -18,7 +18,7 @@
   const char RECEIVING_ABBREV[2] = { '_', 'R' };
   const char WRITABLE_ABBREV[2] = { '_', 'W' };
   std::stringstream ss;
-  ss << "Channel[" << content_name_ << "|" << component_ << "|"
+  ss << "Channel[" << transport_name_ << "|" << component_ << "|"
      << RECEIVING_ABBREV[receiving_] << WRITABLE_ABBREV[writable_] << "]";
   return ss.str();
 }
diff --git a/webrtc/p2p/base/transportchannel.h b/webrtc/p2p/base/transportchannel.h
index f195a0e..5e7adfa 100644
--- a/webrtc/p2p/base/transportchannel.h
+++ b/webrtc/p2p/base/transportchannel.h
@@ -37,14 +37,19 @@
 };
 
 // Used to indicate channel's connection state.
-enum TransportChannelState { STATE_CONNECTING, STATE_COMPLETED, STATE_FAILED };
+enum TransportChannelState {
+  STATE_INIT,
+  STATE_CONNECTING,  // Will enter this state once a connection is created
+  STATE_COMPLETED,
+  STATE_FAILED
+};
 
 // A TransportChannel represents one logical stream of packets that are sent
 // between the two sides of a session.
 class TransportChannel : public sigslot::has_slots<> {
  public:
-  explicit TransportChannel(const std::string& content_name, int component)
-      : content_name_(content_name),
+  explicit TransportChannel(const std::string& transport_name, int component)
+      : transport_name_(transport_name),
         component_(component),
         writable_(false),
         receiving_(false) {}
@@ -60,7 +65,7 @@
   // Returns the session id of this channel.
   virtual const std::string SessionId() const { return std::string(); }
 
-  const std::string& content_name() const { return content_name_; }
+  const std::string& transport_name() const { return transport_name_; }
   int component() const { return component_; }
 
   // Returns the states of this channel.  Each time one of these states changes,
@@ -146,10 +151,9 @@
   // Sets the receiving state, signaling if necessary.
   void set_receiving(bool receiving);
 
-
  private:
   // Used mostly for debugging.
-  std::string content_name_;
+  std::string transport_name_;
   int component_;
   bool writable_;
   bool receiving_;
diff --git a/webrtc/p2p/base/transportchannelimpl.h b/webrtc/p2p/base/transportchannelimpl.h
index 3aca951..ddddf15 100644
--- a/webrtc/p2p/base/transportchannelimpl.h
+++ b/webrtc/p2p/base/transportchannelimpl.h
@@ -32,8 +32,9 @@
 // client.
 class TransportChannelImpl : public TransportChannel {
  public:
-  explicit TransportChannelImpl(const std::string& content_name, int component)
-      : TransportChannel(content_name, component) {}
+  explicit TransportChannelImpl(const std::string& transport_name,
+                                int component)
+      : TransportChannel(transport_name, component) {}
 
   // Returns the transport that created this channel.
   virtual Transport* GetTransport() = 0;
@@ -63,11 +64,11 @@
   // Begins the process of attempting to make a connection to the other client.
   virtual void Connect() = 0;
 
-  // Allows an individual channel to request signaling and be notified when it
-  // is ready.  This is useful if the individual named channels have need to
-  // send their own transport-info stanzas.
-  sigslot::signal1<TransportChannelImpl*> SignalRequestSignaling;
-  virtual void OnSignalingReady() = 0;
+  // Start gathering candidates if not already started, or if an ICE restart
+  // occurred.
+  virtual void MaybeStartGathering() = 0;
+
+  sigslot::signal1<TransportChannelImpl*> SignalGatheringState;
 
   // Handles sending and receiving of candidates.  The Transport
   // receives the candidates and may forward them to the relevant
@@ -77,9 +78,11 @@
   // channel, they cannot return an error if the message is invalid.
   // It is assumed that the Transport will have checked validity
   // before forwarding.
-  sigslot::signal2<TransportChannelImpl*,
-                   const Candidate&> SignalCandidateReady;
-  virtual void OnCandidate(const Candidate& candidate) = 0;
+  sigslot::signal2<TransportChannelImpl*, const Candidate&>
+      SignalCandidateGathered;
+  virtual void AddRemoteCandidate(const Candidate& candidate) = 0;
+
+  virtual IceGatheringState gathering_state() const = 0;
 
   // DTLS methods
   virtual bool SetLocalCertificate(
@@ -92,9 +95,6 @@
 
   virtual bool SetSslRole(rtc::SSLRole role) = 0;
 
-  // TransportChannel is forwarding this signal from PortAllocatorSession.
-  sigslot::signal1<TransportChannelImpl*> SignalCandidatesAllocationDone;
-
   // Invoked when there is conflict in the ICE role between local and remote
   // agents.
   sigslot::signal1<TransportChannelImpl*> SignalRoleConflict;
diff --git a/webrtc/p2p/base/transportchannelproxy.cc b/webrtc/p2p/base/transportchannelproxy.cc
deleted file mode 100644
index 74d1e1d..0000000
--- a/webrtc/p2p/base/transportchannelproxy.cc
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- *  Copyright 2004 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/base/transport.h"
-#include "webrtc/p2p/base/transportchannelimpl.h"
-#include "webrtc/p2p/base/transportchannelproxy.h"
-#include "webrtc/base/common.h"
-#include "webrtc/base/logging.h"
-#include "webrtc/base/thread.h"
-
-namespace cricket {
-
-enum {
-  MSG_UPDATESTATE,
-};
-
-TransportChannelProxy::TransportChannelProxy(const std::string& content_name,
-                                             int component)
-    : TransportChannel(content_name, component),
-      impl_(NULL) {
-  worker_thread_ = rtc::Thread::Current();
-}
-
-TransportChannelProxy::~TransportChannelProxy() {
-  // Clearing any pending signal.
-  worker_thread_->Clear(this);
-  if (impl_) {
-    impl_->GetTransport()->DestroyChannel(impl_->component());
-  }
-}
-
-void TransportChannelProxy::SetImplementation(TransportChannelImpl* impl) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-
-  if (impl == impl_) {
-    // Ignore if the |impl| has already been set.
-    LOG(LS_WARNING) << "Ignored TransportChannelProxy::SetImplementation call "
-                    << "with a same impl as the existing one.";
-    return;
-  }
-
-  // Destroy any existing impl_.
-  if (impl_) {
-    impl_->GetTransport()->DestroyChannel(impl_->component());
-  }
-
-  // Adopt the supplied impl, and connect to its signals.
-  impl_ = impl;
-
-  if (impl_) {
-    impl_->SignalWritableState.connect(
-        this, &TransportChannelProxy::OnWritableState);
-    impl_->SignalReceivingState.connect(
-        this, &TransportChannelProxy::OnReceivingState);
-    impl_->SignalReadPacket.connect(
-        this, &TransportChannelProxy::OnReadPacket);
-    impl_->SignalReadyToSend.connect(
-        this, &TransportChannelProxy::OnReadyToSend);
-    impl_->SignalRouteChange.connect(
-        this, &TransportChannelProxy::OnRouteChange);
-    for (const auto& pair : options_) {
-      impl_->SetOption(pair.first, pair.second);
-    }
-
-    // Push down the SRTP ciphers, if any were set.
-    if (!pending_srtp_ciphers_.empty()) {
-      impl_->SetSrtpCiphers(pending_srtp_ciphers_);
-    }
-  }
-
-  // Post ourselves a message to see if we need to fire state callbacks.
-  worker_thread_->Post(this, MSG_UPDATESTATE);
-}
-
-int TransportChannelProxy::SendPacket(const char* data, size_t len,
-                                      const rtc::PacketOptions& options,
-                                      int flags) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  // Fail if we don't have an impl yet.
-  if (!impl_) {
-    return -1;
-  }
-  return impl_->SendPacket(data, len, options, flags);
-}
-
-int TransportChannelProxy::SetOption(rtc::Socket::Option opt, int value) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  options_.push_back(OptionPair(opt, value));
-  if (!impl_) {
-    return 0;
-  }
-  return impl_->SetOption(opt, value);
-}
-
-bool TransportChannelProxy::GetOption(rtc::Socket::Option opt, int* value) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  if (impl_) {
-    return impl_->GetOption(opt, value);
-  }
-
-  for (const auto& pair : options_) {
-    if (pair.first == opt) {
-      *value = pair.second;
-      return true;
-    }
-  }
-  return false;
-}
-
-int TransportChannelProxy::GetError() {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  if (!impl_) {
-    return 0;
-  }
-  return impl_->GetError();
-}
-
-TransportChannelState TransportChannelProxy::GetState() const {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  if (!impl_) {
-    return TransportChannelState::STATE_CONNECTING;
-  }
-  return impl_->GetState();
-}
-
-bool TransportChannelProxy::GetStats(ConnectionInfos* infos) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  if (!impl_) {
-    return false;
-  }
-  return impl_->GetStats(infos);
-}
-
-bool TransportChannelProxy::IsDtlsActive() const {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  if (!impl_) {
-    return false;
-  }
-  return impl_->IsDtlsActive();
-}
-
-bool TransportChannelProxy::GetSslRole(rtc::SSLRole* role) const {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  if (!impl_) {
-    return false;
-  }
-  return impl_->GetSslRole(role);
-}
-
-bool TransportChannelProxy::SetSslRole(rtc::SSLRole role) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  if (!impl_) {
-    return false;
-  }
-  return impl_->SetSslRole(role);
-}
-
-bool TransportChannelProxy::SetSrtpCiphers(const std::vector<std::string>&
-                                           ciphers) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  pending_srtp_ciphers_ = ciphers;  // Cache so we can send later, but always
-                                    // set so it stays consistent.
-  if (impl_) {
-    return impl_->SetSrtpCiphers(ciphers);
-  }
-  return true;
-}
-
-bool TransportChannelProxy::GetSrtpCipher(std::string* cipher) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  if (!impl_) {
-    return false;
-  }
-  return impl_->GetSrtpCipher(cipher);
-}
-
-bool TransportChannelProxy::GetSslCipher(std::string* cipher) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  if (!impl_) {
-    return false;
-  }
-  return impl_->GetSslCipher(cipher);
-}
-
-rtc::scoped_refptr<rtc::RTCCertificate>
-TransportChannelProxy::GetLocalCertificate() const {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  if (!impl_) {
-    return nullptr;
-  }
-  return impl_->GetLocalCertificate();
-}
-
-bool TransportChannelProxy::GetRemoteSSLCertificate(
-    rtc::SSLCertificate** cert) const {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  if (!impl_) {
-    return false;
-  }
-  return impl_->GetRemoteSSLCertificate(cert);
-}
-
-bool TransportChannelProxy::ExportKeyingMaterial(const std::string& label,
-                                                 const uint8* context,
-                                                 size_t context_len,
-                                                 bool use_context,
-                                                 uint8* result,
-                                                 size_t result_len) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  if (!impl_) {
-    return false;
-  }
-  return impl_->ExportKeyingMaterial(label, context, context_len, use_context,
-                                     result, result_len);
-}
-
-IceRole TransportChannelProxy::GetIceRole() const {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  if (!impl_) {
-    return ICEROLE_UNKNOWN;
-  }
-  return impl_->GetIceRole();
-}
-
-void TransportChannelProxy::OnWritableState(TransportChannel* channel) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  ASSERT(channel == impl_);
-  set_writable(impl_->writable());
-  // Note: SignalWritableState fired by set_writable.
-}
-
-void TransportChannelProxy::OnReceivingState(TransportChannel* channel) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  ASSERT(channel == impl_);
-  set_receiving(impl_->receiving());
-  // Note: SignalReceivingState fired by set_receiving.
-}
-
-void TransportChannelProxy::OnReadPacket(
-    TransportChannel* channel, const char* data, size_t size,
-    const rtc::PacketTime& packet_time, int flags) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  ASSERT(channel == impl_);
-  SignalReadPacket(this, data, size, packet_time, flags);
-}
-
-void TransportChannelProxy::OnReadyToSend(TransportChannel* channel) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  ASSERT(channel == impl_);
-  SignalReadyToSend(this);
-}
-
-void TransportChannelProxy::OnRouteChange(TransportChannel* channel,
-                                          const Candidate& candidate) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  ASSERT(channel == impl_);
-  SignalRouteChange(this, candidate);
-}
-
-void TransportChannelProxy::OnMessage(rtc::Message* msg) {
-  ASSERT(rtc::Thread::Current() == worker_thread_);
-  if (msg->message_id == MSG_UPDATESTATE) {
-    // If impl_ is already receiving or writable, push up those signals.
-    set_writable(impl_ ? impl_->writable() : false);
-    set_receiving(impl_ ? impl_->receiving() : false);
-  }
-}
-
-}  // namespace cricket
diff --git a/webrtc/p2p/base/transportchannelproxy.h b/webrtc/p2p/base/transportchannelproxy.h
deleted file mode 100644
index 80ee20a..0000000
--- a/webrtc/p2p/base/transportchannelproxy.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- *  Copyright 2004 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_BASE_TRANSPORTCHANNELPROXY_H_
-#define WEBRTC_P2P_BASE_TRANSPORTCHANNELPROXY_H_
-
-#include <string>
-#include <utility>
-#include <vector>
-
-#include "webrtc/p2p/base/transportchannel.h"
-#include "webrtc/base/messagehandler.h"
-
-namespace rtc {
-class Thread;
-}
-
-namespace cricket {
-
-class TransportChannelImpl;
-
-// Proxies calls between the client and the transport channel implementation.
-// This is needed because clients are allowed to create channels before the
-// network negotiation is complete.  Hence, we create a proxy up front, and
-// when negotiation completes, connect the proxy to the implementaiton.
-class TransportChannelProxy : public TransportChannel,
-                              public rtc::MessageHandler {
- public:
-  TransportChannelProxy(const std::string& content_name,
-                        int component);
-  ~TransportChannelProxy() override;
-
-  TransportChannelImpl* impl() { return impl_; }
-
-  TransportChannelState GetState() const override;
-
-  // Sets the implementation to which we will proxy.
-  void SetImplementation(TransportChannelImpl* impl);
-
-  // Implementation of the TransportChannel interface.  These simply forward to
-  // the implementation.
-  int SendPacket(const char* data, size_t len,
-                 const rtc::PacketOptions& options,
-                 int flags) override;
-  int SetOption(rtc::Socket::Option opt, int value) override;
-  bool GetOption(rtc::Socket::Option opt, int* value) override;
-  int GetError() override;
-  virtual IceRole GetIceRole() const;
-  bool GetStats(ConnectionInfos* infos) override;
-  bool IsDtlsActive() const override;
-  bool GetSslRole(rtc::SSLRole* role) const override;
-  virtual bool SetSslRole(rtc::SSLRole role);
-  bool SetSrtpCiphers(const std::vector<std::string>& ciphers) override;
-  bool GetSrtpCipher(std::string* cipher) override;
-  bool GetSslCipher(std::string* cipher) override;
-  rtc::scoped_refptr<rtc::RTCCertificate> GetLocalCertificate() const override;
-  bool GetRemoteSSLCertificate(rtc::SSLCertificate** cert) const override;
-  bool ExportKeyingMaterial(const std::string& label,
-                            const uint8* context,
-                            size_t context_len,
-                            bool use_context,
-                            uint8* result,
-                            size_t result_len) override;
-
- private:
-  // Catch signals from the implementation channel.  These just forward to the
-  // client (after updating our state to match).
-  void OnReceivingState(TransportChannel* channel);
-  void OnWritableState(TransportChannel* channel);
-  void OnReadPacket(TransportChannel* channel, const char* data, size_t size,
-                    const rtc::PacketTime& packet_time, int flags);
-  void OnReadyToSend(TransportChannel* channel);
-  void OnRouteChange(TransportChannel* channel, const Candidate& candidate);
-
-  void OnMessage(rtc::Message* message) override;
-
-  typedef std::pair<rtc::Socket::Option, int> OptionPair;
-  typedef std::vector<OptionPair> OptionList;
-  rtc::Thread* worker_thread_;
-  TransportChannelImpl* impl_;
-  OptionList options_;
-  std::vector<std::string> pending_srtp_ciphers_;
-
-  RTC_DISALLOW_COPY_AND_ASSIGN(TransportChannelProxy);
-};
-
-}  // namespace cricket
-
-#endif  // WEBRTC_P2P_BASE_TRANSPORTCHANNELPROXY_H_
diff --git a/webrtc/p2p/base/transportcontroller.cc b/webrtc/p2p/base/transportcontroller.cc
new file mode 100644
index 0000000..4d9e403
--- /dev/null
+++ b/webrtc/p2p/base/transportcontroller.cc
@@ -0,0 +1,575 @@
+/*
+ *  Copyright 2015 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/base/transportcontroller.h"
+
+#include "webrtc/base/bind.h"
+#include "webrtc/base/checks.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/p2p/base/dtlstransport.h"
+#include "webrtc/p2p/base/p2ptransport.h"
+
+namespace cricket {
+
+enum {
+  MSG_ICECONNECTIONSTATE,
+  MSG_RECEIVING,
+  MSG_ICEGATHERINGSTATE,
+  MSG_CANDIDATESGATHERED,
+};
+
+struct CandidatesData : public rtc::MessageData {
+  CandidatesData(const std::string& transport_name,
+                 const Candidates& candidates)
+      : transport_name(transport_name), candidates(candidates) {}
+
+  std::string transport_name;
+  Candidates candidates;
+};
+
+TransportController::TransportController(rtc::Thread* signaling_thread,
+                                         rtc::Thread* worker_thread,
+                                         PortAllocator* port_allocator)
+    : signaling_thread_(signaling_thread),
+      worker_thread_(worker_thread),
+      port_allocator_(port_allocator) {}
+
+TransportController::~TransportController() {
+  worker_thread_->Invoke<void>(
+      rtc::Bind(&TransportController::DestroyAllTransports_w, this));
+  signaling_thread_->Clear(this);
+}
+
+bool TransportController::SetSslMaxProtocolVersion(
+    rtc::SSLProtocolVersion version) {
+  return worker_thread_->Invoke<bool>(rtc::Bind(
+      &TransportController::SetSslMaxProtocolVersion_w, this, version));
+}
+
+void TransportController::SetIceConnectionReceivingTimeout(int timeout_ms) {
+  worker_thread_->Invoke<void>(
+      rtc::Bind(&TransportController::SetIceConnectionReceivingTimeout_w, this,
+                timeout_ms));
+}
+
+void TransportController::SetIceRole(IceRole ice_role) {
+  worker_thread_->Invoke<void>(
+      rtc::Bind(&TransportController::SetIceRole_w, this, ice_role));
+}
+
+bool TransportController::GetSslRole(rtc::SSLRole* role) {
+  return worker_thread_->Invoke<bool>(
+      rtc::Bind(&TransportController::GetSslRole_w, this, role));
+}
+
+bool TransportController::SetLocalCertificate(
+    const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
+  return worker_thread_->Invoke<bool>(rtc::Bind(
+      &TransportController::SetLocalCertificate_w, this, certificate));
+}
+
+bool TransportController::GetLocalCertificate(
+    const std::string& transport_name,
+    rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
+  return worker_thread_->Invoke<bool>(
+      rtc::Bind(&TransportController::GetLocalCertificate_w, this,
+                transport_name, certificate));
+}
+
+bool TransportController::GetRemoteSSLCertificate(
+    const std::string& transport_name,
+    rtc::SSLCertificate** cert) {
+  return worker_thread_->Invoke<bool>(
+      rtc::Bind(&TransportController::GetRemoteSSLCertificate_w, this,
+                transport_name, cert));
+}
+
+bool TransportController::SetLocalTransportDescription(
+    const std::string& transport_name,
+    const TransportDescription& tdesc,
+    ContentAction action,
+    std::string* err) {
+  return worker_thread_->Invoke<bool>(
+      rtc::Bind(&TransportController::SetLocalTransportDescription_w, this,
+                transport_name, tdesc, action, err));
+}
+
+bool TransportController::SetRemoteTransportDescription(
+    const std::string& transport_name,
+    const TransportDescription& tdesc,
+    ContentAction action,
+    std::string* err) {
+  return worker_thread_->Invoke<bool>(
+      rtc::Bind(&TransportController::SetRemoteTransportDescription_w, this,
+                transport_name, tdesc, action, err));
+}
+
+void TransportController::MaybeStartGathering() {
+  worker_thread_->Invoke<void>(
+      rtc::Bind(&TransportController::MaybeStartGathering_w, this));
+}
+
+bool TransportController::AddRemoteCandidates(const std::string& transport_name,
+                                              const Candidates& candidates,
+                                              std::string* err) {
+  return worker_thread_->Invoke<bool>(
+      rtc::Bind(&TransportController::AddRemoteCandidates_w, this,
+                transport_name, candidates, err));
+}
+
+bool TransportController::ReadyForRemoteCandidates(
+    const std::string& transport_name) {
+  return worker_thread_->Invoke<bool>(rtc::Bind(
+      &TransportController::ReadyForRemoteCandidates_w, this, transport_name));
+}
+
+bool TransportController::GetStats(const std::string& transport_name,
+                                   TransportStats* stats) {
+  return worker_thread_->Invoke<bool>(
+      rtc::Bind(&TransportController::GetStats_w, this, transport_name, stats));
+}
+
+TransportChannel* TransportController::CreateTransportChannel_w(
+    const std::string& transport_name,
+    int component) {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+
+  Transport* transport = GetOrCreateTransport_w(transport_name);
+  return transport->CreateChannel(component);
+}
+
+void TransportController::DestroyTransportChannel_w(
+    const std::string& transport_name,
+    int component) {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+
+  Transport* transport = GetTransport_w(transport_name);
+  if (!transport) {
+    ASSERT(false);
+    return;
+  }
+  transport->DestroyChannel(component);
+
+  // Just as we create a Transport when its first channel is created,
+  // we delete it when its last channel is deleted.
+  if (!transport->HasChannels()) {
+    DestroyTransport_w(transport_name);
+  }
+}
+
+const rtc::scoped_refptr<rtc::RTCCertificate>&
+TransportController::certificate_for_testing() {
+  return certificate_;
+}
+
+Transport* TransportController::CreateTransport_w(
+    const std::string& transport_name) {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+
+  Transport* transport = new DtlsTransport<P2PTransport>(
+      transport_name, port_allocator(), certificate_);
+  return transport;
+}
+
+Transport* TransportController::GetTransport_w(
+    const std::string& transport_name) {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+
+  auto iter = transports_.find(transport_name);
+  return (iter != transports_.end()) ? iter->second : nullptr;
+}
+
+void TransportController::OnMessage(rtc::Message* pmsg) {
+  RTC_DCHECK(signaling_thread_->IsCurrent());
+
+  switch (pmsg->message_id) {
+    case MSG_ICECONNECTIONSTATE: {
+      rtc::TypedMessageData<IceConnectionState>* data =
+          static_cast<rtc::TypedMessageData<IceConnectionState>*>(pmsg->pdata);
+      SignalConnectionState(data->data());
+      delete data;
+      break;
+    }
+    case MSG_RECEIVING: {
+      rtc::TypedMessageData<bool>* data =
+          static_cast<rtc::TypedMessageData<bool>*>(pmsg->pdata);
+      SignalReceiving(data->data());
+      delete data;
+      break;
+    }
+    case MSG_ICEGATHERINGSTATE: {
+      rtc::TypedMessageData<IceGatheringState>* data =
+          static_cast<rtc::TypedMessageData<IceGatheringState>*>(pmsg->pdata);
+      SignalGatheringState(data->data());
+      delete data;
+      break;
+    }
+    case MSG_CANDIDATESGATHERED: {
+      CandidatesData* data = static_cast<CandidatesData*>(pmsg->pdata);
+      SignalCandidatesGathered(data->transport_name, data->candidates);
+      delete data;
+      break;
+    }
+    default:
+      ASSERT(false);
+  }
+}
+
+Transport* TransportController::GetOrCreateTransport_w(
+    const std::string& transport_name) {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+
+  Transport* transport = GetTransport_w(transport_name);
+  if (transport) {
+    return transport;
+  }
+
+  transport = CreateTransport_w(transport_name);
+  // The stuff below happens outside of CreateTransport_w so that unit tests
+  // can override CreateTransport_w to return a different type of transport.
+  transport->SetSslMaxProtocolVersion(ssl_max_version_);
+  transport->SetChannelReceivingTimeout(ice_receiving_timeout_ms_);
+  transport->SetIceRole(ice_role_);
+  transport->SetIceTiebreaker(ice_tiebreaker_);
+  if (certificate_) {
+    transport->SetLocalCertificate(certificate_);
+  }
+  transport->SignalConnecting.connect(
+      this, &TransportController::OnTransportConnecting_w);
+  transport->SignalWritableState.connect(
+      this, &TransportController::OnTransportWritableState_w);
+  transport->SignalReceivingState.connect(
+      this, &TransportController::OnTransportReceivingState_w);
+  transport->SignalCompleted.connect(
+      this, &TransportController::OnTransportCompleted_w);
+  transport->SignalFailed.connect(this,
+                                  &TransportController::OnTransportFailed_w);
+  transport->SignalGatheringState.connect(
+      this, &TransportController::OnTransportGatheringState_w);
+  transport->SignalCandidatesGathered.connect(
+      this, &TransportController::OnTransportCandidatesGathered_w);
+  transport->SignalRoleConflict.connect(
+      this, &TransportController::OnTransportRoleConflict_w);
+  transports_[transport_name] = transport;
+
+  return transport;
+}
+
+void TransportController::DestroyTransport_w(
+    const std::string& transport_name) {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+
+  auto iter = transports_.find(transport_name);
+  if (iter != transports_.end()) {
+    delete iter->second;
+    transports_.erase(transport_name);
+  }
+  // Destroying a transport may cause aggregate state to change.
+  UpdateAggregateStates_w();
+}
+
+void TransportController::DestroyAllTransports_w() {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+
+  for (const auto& kv : transports_) {
+    delete kv.second;
+  }
+  transports_.clear();
+}
+
+bool TransportController::SetSslMaxProtocolVersion_w(
+    rtc::SSLProtocolVersion version) {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+
+  // Max SSL version can only be set before transports are created.
+  if (!transports_.empty()) {
+    return false;
+  }
+
+  ssl_max_version_ = version;
+  return true;
+}
+
+void TransportController::SetIceConnectionReceivingTimeout_w(int timeout_ms) {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+  ice_receiving_timeout_ms_ = timeout_ms;
+  for (const auto& kv : transports_) {
+    kv.second->SetChannelReceivingTimeout(timeout_ms);
+  }
+}
+
+void TransportController::SetIceRole_w(IceRole ice_role) {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+  ice_role_ = ice_role;
+  for (const auto& kv : transports_) {
+    kv.second->SetIceRole(ice_role_);
+  }
+}
+
+bool TransportController::GetSslRole_w(rtc::SSLRole* role) {
+  RTC_DCHECK(worker_thread()->IsCurrent());
+
+  if (transports_.empty()) {
+    return false;
+  }
+  return transports_.begin()->second->GetSslRole(role);
+}
+
+bool TransportController::SetLocalCertificate_w(
+    const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+
+  if (certificate_) {
+    return false;
+  }
+  if (!certificate) {
+    return false;
+  }
+  certificate_ = certificate;
+
+  for (const auto& kv : transports_) {
+    kv.second->SetLocalCertificate(certificate_);
+  }
+  return true;
+}
+
+bool TransportController::GetLocalCertificate_w(
+    const std::string& transport_name,
+    rtc::scoped_refptr<rtc::RTCCertificate>* certificate) {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+
+  Transport* t = GetTransport_w(transport_name);
+  if (!t) {
+    return false;
+  }
+
+  return t->GetLocalCertificate(certificate);
+}
+
+bool TransportController::GetRemoteSSLCertificate_w(
+    const std::string& transport_name,
+    rtc::SSLCertificate** cert) {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+
+  Transport* t = GetTransport_w(transport_name);
+  if (!t) {
+    return false;
+  }
+
+  return t->GetRemoteSSLCertificate(cert);
+}
+
+bool TransportController::SetLocalTransportDescription_w(
+    const std::string& transport_name,
+    const TransportDescription& tdesc,
+    ContentAction action,
+    std::string* err) {
+  RTC_DCHECK(worker_thread()->IsCurrent());
+
+  Transport* transport = GetTransport_w(transport_name);
+  if (!transport) {
+    // If we didn't find a transport, that's not an error;
+    // it could have been deleted as a result of bundling.
+    // TODO(deadbeef): Make callers smarter so they won't attempt to set a
+    // description on a deleted transport.
+    return true;
+  }
+
+  return transport->SetLocalTransportDescription(tdesc, action, err);
+}
+
+bool TransportController::SetRemoteTransportDescription_w(
+    const std::string& transport_name,
+    const TransportDescription& tdesc,
+    ContentAction action,
+    std::string* err) {
+  RTC_DCHECK(worker_thread()->IsCurrent());
+
+  Transport* transport = GetTransport_w(transport_name);
+  if (!transport) {
+    // If we didn't find a transport, that's not an error;
+    // it could have been deleted as a result of bundling.
+    // TODO(deadbeef): Make callers smarter so they won't attempt to set a
+    // description on a deleted transport.
+    return true;
+  }
+
+  return transport->SetRemoteTransportDescription(tdesc, action, err);
+}
+
+void TransportController::MaybeStartGathering_w() {
+  for (const auto& kv : transports_) {
+    kv.second->MaybeStartGathering();
+  }
+}
+
+bool TransportController::AddRemoteCandidates_w(
+    const std::string& transport_name,
+    const Candidates& candidates,
+    std::string* err) {
+  RTC_DCHECK(worker_thread()->IsCurrent());
+
+  Transport* transport = GetTransport_w(transport_name);
+  if (!transport) {
+    // If we didn't find a transport, that's not an error;
+    // it could have been deleted as a result of bundling.
+    return true;
+  }
+
+  return transport->AddRemoteCandidates(candidates, err);
+}
+
+bool TransportController::ReadyForRemoteCandidates_w(
+    const std::string& transport_name) {
+  RTC_DCHECK(worker_thread()->IsCurrent());
+
+  Transport* transport = GetTransport_w(transport_name);
+  if (!transport) {
+    return false;
+  }
+  return transport->ready_for_remote_candidates();
+}
+
+bool TransportController::GetStats_w(const std::string& transport_name,
+                                     TransportStats* stats) {
+  RTC_DCHECK(worker_thread()->IsCurrent());
+
+  Transport* transport = GetTransport_w(transport_name);
+  if (!transport) {
+    return false;
+  }
+  return transport->GetStats(stats);
+}
+
+void TransportController::OnTransportConnecting_w(Transport* transport) {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+  UpdateAggregateStates_w();
+}
+
+void TransportController::OnTransportWritableState_w(Transport* transport) {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+  UpdateAggregateStates_w();
+}
+
+void TransportController::OnTransportReceivingState_w(Transport* transport) {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+  UpdateAggregateStates_w();
+}
+
+void TransportController::OnTransportCompleted_w(Transport* transport) {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+  UpdateAggregateStates_w();
+}
+
+void TransportController::OnTransportFailed_w(Transport* transport) {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+  UpdateAggregateStates_w();
+}
+
+void TransportController::OnTransportGatheringState_w(Transport* transport) {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+  UpdateAggregateStates_w();
+}
+
+void TransportController::OnTransportCandidatesGathered_w(
+    Transport* transport,
+    const std::vector<Candidate>& candidates) {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+  CandidatesData* data = new CandidatesData(transport->name(), candidates);
+  signaling_thread_->Post(this, MSG_CANDIDATESGATHERED, data);
+}
+
+void TransportController::OnTransportRoleConflict_w() {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+
+  if (ice_role_switch_) {
+    LOG(LS_WARNING) << "Repeat of role conflict signal from Transport.";
+    return;
+  }
+
+  ice_role_switch_ = true;
+  IceRole reversed_role = (ice_role_ == ICEROLE_CONTROLLING)
+                              ? ICEROLE_CONTROLLED
+                              : ICEROLE_CONTROLLING;
+  for (const auto& kv : transports_) {
+    kv.second->SetIceRole(reversed_role);
+  }
+}
+
+void TransportController::UpdateAggregateStates_w() {
+  RTC_DCHECK(worker_thread_->IsCurrent());
+
+  IceConnectionState new_connection_state = kIceConnectionConnecting;
+  IceGatheringState new_gathering_state = kIceGatheringNew;
+  bool any_receiving = false;
+  bool any_failed = false;
+  bool all_connected = HasChannels_w();
+  bool all_completed = HasChannels_w();
+  bool any_gathering = false;
+  bool all_done_gathering = HasChannels_w();
+  for (const auto& kv : transports_) {
+    // Ignore transports without channels since they're about to be deleted,
+    // and their state is meaningless.
+    if (!kv.second->HasChannels()) {
+      continue;
+    }
+    any_receiving = any_receiving || kv.second->any_channel_receiving();
+    any_failed = any_failed || kv.second->AnyChannelFailed();
+    all_connected = all_connected && kv.second->all_channels_writable();
+    all_completed = all_completed && kv.second->AllChannelsCompleted();
+    any_gathering =
+        any_gathering || kv.second->gathering_state() != kIceGatheringNew;
+    all_done_gathering = all_done_gathering &&
+                         kv.second->gathering_state() == kIceGatheringComplete;
+  }
+
+  if (any_failed) {
+    new_connection_state = kIceConnectionFailed;
+  } else if (all_completed) {
+    new_connection_state = kIceConnectionCompleted;
+  } else if (all_connected) {
+    new_connection_state = kIceConnectionConnected;
+  }
+  if (connection_state_ != new_connection_state) {
+    connection_state_ = new_connection_state;
+    signaling_thread_->Post(
+        this, MSG_ICECONNECTIONSTATE,
+        new rtc::TypedMessageData<IceConnectionState>(new_connection_state));
+  }
+
+  if (receiving_ != any_receiving) {
+    receiving_ = any_receiving;
+    signaling_thread_->Post(this, MSG_RECEIVING,
+                            new rtc::TypedMessageData<bool>(any_receiving));
+  }
+
+  if (all_done_gathering) {
+    new_gathering_state = kIceGatheringComplete;
+  } else if (any_gathering) {
+    new_gathering_state = kIceGatheringGathering;
+  }
+  if (gathering_state_ != new_gathering_state) {
+    gathering_state_ = new_gathering_state;
+    signaling_thread_->Post(
+        this, MSG_ICEGATHERINGSTATE,
+        new rtc::TypedMessageData<IceGatheringState>(new_gathering_state));
+  }
+}
+
+bool TransportController::HasChannels_w() {
+  for (const auto& kv : transports_) {
+    if (kv.second->HasChannels()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+}  // namespace cricket
diff --git a/webrtc/p2p/base/transportcontroller.h b/webrtc/p2p/base/transportcontroller.h
new file mode 100644
index 0000000..e31eb83
--- /dev/null
+++ b/webrtc/p2p/base/transportcontroller.h
@@ -0,0 +1,196 @@
+/*
+ *  Copyright 2015 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_BASE_TRANSPORTCONTROLLER_H_
+#define WEBRTC_P2P_BASE_TRANSPORTCONTROLLER_H_
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include "webrtc/base/sigslot.h"
+#include "webrtc/base/sslstreamadapter.h"
+#include "webrtc/p2p/base/candidate.h"
+#include "webrtc/p2p/base/transport.h"
+
+namespace rtc {
+class Thread;
+}
+
+namespace cricket {
+
+class TransportController : public sigslot::has_slots<>,
+                            public rtc::MessageHandler {
+ public:
+  TransportController(rtc::Thread* signaling_thread,
+                      rtc::Thread* worker_thread,
+                      PortAllocator* port_allocator);
+
+  virtual ~TransportController();
+
+  rtc::Thread* signaling_thread() const { return signaling_thread_; }
+  rtc::Thread* worker_thread() const { return worker_thread_; }
+
+  PortAllocator* port_allocator() const { return port_allocator_; }
+
+  // Can only be set before transports are created.
+  // TODO(deadbeef): Make this an argument to the constructor once BaseSession
+  // and WebRtcSession are combined
+  bool SetSslMaxProtocolVersion(rtc::SSLProtocolVersion version);
+
+  void SetIceConnectionReceivingTimeout(int timeout_ms);
+  void SetIceRole(IceRole ice_role);
+
+  // TODO(deadbeef) - Return role of each transport, as role may differ from
+  // one another.
+  // In current implementaion we just return the role of the first transport
+  // alphabetically.
+  bool GetSslRole(rtc::SSLRole* role);
+
+  // Specifies the identity to use in this session.
+  // Can only be called once.
+  bool SetLocalCertificate(
+      const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
+  bool GetLocalCertificate(
+      const std::string& transport_name,
+      rtc::scoped_refptr<rtc::RTCCertificate>* certificate);
+  // Caller owns returned certificate
+  bool GetRemoteSSLCertificate(const std::string& transport_name,
+                               rtc::SSLCertificate** cert);
+  bool SetLocalTransportDescription(const std::string& transport_name,
+                                    const TransportDescription& tdesc,
+                                    ContentAction action,
+                                    std::string* err);
+  bool SetRemoteTransportDescription(const std::string& transport_name,
+                                     const TransportDescription& tdesc,
+                                     ContentAction action,
+                                     std::string* err);
+  // Start gathering candidates for any new transports, or transports doing an
+  // ICE restart.
+  void MaybeStartGathering();
+  bool AddRemoteCandidates(const std::string& transport_name,
+                           const Candidates& candidates,
+                           std::string* err);
+  bool ReadyForRemoteCandidates(const std::string& transport_name);
+  bool GetStats(const std::string& transport_name, TransportStats* stats);
+
+  virtual TransportChannel* CreateTransportChannel_w(
+      const std::string& transport_name,
+      int component);
+  virtual void DestroyTransportChannel_w(const std::string& transport_name,
+                                         int component);
+
+  // All of these signals are fired on the signalling thread.
+
+  // If any transport failed => failed,
+  // Else if all completed => completed,
+  // Else if all connected => connected,
+  // Else => connecting
+  sigslot::signal1<IceConnectionState> SignalConnectionState;
+
+  // Receiving if any transport is receiving
+  sigslot::signal1<bool> SignalReceiving;
+
+  // If all transports done gathering => complete,
+  // Else if any are gathering => gathering,
+  // Else => new
+  sigslot::signal1<IceGatheringState> SignalGatheringState;
+
+  // (transport_name, candidates)
+  sigslot::signal2<const std::string&, const Candidates&>
+      SignalCandidatesGathered;
+
+  // for unit test
+  const rtc::scoped_refptr<rtc::RTCCertificate>& certificate_for_testing();
+
+ protected:
+  // Protected and virtual so we can override it in unit tests.
+  virtual Transport* CreateTransport_w(const std::string& transport_name);
+
+  // For unit tests
+  const std::map<std::string, Transport*>& transports() { return transports_; }
+  Transport* GetTransport_w(const std::string& transport_name);
+
+ private:
+  void OnMessage(rtc::Message* pmsg) override;
+
+  Transport* GetOrCreateTransport_w(const std::string& transport_name);
+  void DestroyTransport_w(const std::string& transport_name);
+  void DestroyAllTransports_w();
+
+  bool SetSslMaxProtocolVersion_w(rtc::SSLProtocolVersion version);
+  void SetIceConnectionReceivingTimeout_w(int timeout_ms);
+  void SetIceRole_w(IceRole ice_role);
+  bool GetSslRole_w(rtc::SSLRole* role);
+  bool SetLocalCertificate_w(
+      const rtc::scoped_refptr<rtc::RTCCertificate>& certificate);
+  bool GetLocalCertificate_w(
+      const std::string& transport_name,
+      rtc::scoped_refptr<rtc::RTCCertificate>* certificate);
+  bool GetRemoteSSLCertificate_w(const std::string& transport_name,
+                                 rtc::SSLCertificate** cert);
+  bool SetLocalTransportDescription_w(const std::string& transport_name,
+                                      const TransportDescription& tdesc,
+                                      ContentAction action,
+                                      std::string* err);
+  bool SetRemoteTransportDescription_w(const std::string& transport_name,
+                                       const TransportDescription& tdesc,
+                                       ContentAction action,
+                                       std::string* err);
+  void MaybeStartGathering_w();
+  bool AddRemoteCandidates_w(const std::string& transport_name,
+                             const Candidates& candidates,
+                             std::string* err);
+  bool ReadyForRemoteCandidates_w(const std::string& transport_name);
+  bool GetStats_w(const std::string& transport_name, TransportStats* stats);
+
+  // Handlers for signals from Transport.
+  void OnTransportConnecting_w(Transport* transport);
+  void OnTransportWritableState_w(Transport* transport);
+  void OnTransportReceivingState_w(Transport* transport);
+  void OnTransportCompleted_w(Transport* transport);
+  void OnTransportFailed_w(Transport* transport);
+  void OnTransportGatheringState_w(Transport* transport);
+  void OnTransportCandidatesGathered_w(
+      Transport* transport,
+      const std::vector<Candidate>& candidates);
+  void OnTransportRoleConflict_w();
+
+  void UpdateAggregateStates_w();
+  bool HasChannels_w();
+
+  rtc::Thread* const signaling_thread_ = nullptr;
+  rtc::Thread* const worker_thread_ = nullptr;
+  typedef std::map<std::string, Transport*> TransportMap;
+  TransportMap transports_;
+
+  PortAllocator* const port_allocator_ = nullptr;
+  rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_10;
+
+  // Aggregate state for Transports
+  IceConnectionState connection_state_ = kIceConnectionConnecting;
+  bool receiving_ = false;
+  IceGatheringState gathering_state_ = kIceGatheringNew;
+
+  // TODO(deadbeef): Move the fields below down to the transports themselves
+
+  // Timeout value in milliseconds for which no ICE connection receives
+  // any packets
+  int ice_receiving_timeout_ms_ = -1;
+  IceRole ice_role_ = ICEROLE_CONTROLLING;
+  // Flag which will be set to true after the first role switch
+  bool ice_role_switch_ = false;
+  uint64 ice_tiebreaker_ = rtc::CreateRandomId64();
+  rtc::scoped_refptr<rtc::RTCCertificate> certificate_;
+};
+
+}  // namespace cricket
+
+#endif  // WEBRTC_P2P_BASE_TRANSPORTCONTROLLER_H_
diff --git a/webrtc/p2p/base/transportcontroller_unittest.cc b/webrtc/p2p/base/transportcontroller_unittest.cc
new file mode 100644
index 0000000..c7835259
--- /dev/null
+++ b/webrtc/p2p/base/transportcontroller_unittest.cc
@@ -0,0 +1,679 @@
+/*
+ *  Copyright 2015 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 <map>
+
+#include "webrtc/base/fakesslidentity.h"
+#include "webrtc/base/gunit.h"
+#include "webrtc/base/helpers.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/base/sslidentity.h"
+#include "webrtc/base/thread.h"
+#include "webrtc/p2p/base/dtlstransportchannel.h"
+#include "webrtc/p2p/base/faketransportcontroller.h"
+#include "webrtc/p2p/base/p2ptransportchannel.h"
+#include "webrtc/p2p/base/portallocator.h"
+#include "webrtc/p2p/base/transportcontroller.h"
+#include "webrtc/p2p/client/fakeportallocator.h"
+
+static const int kTimeout = 100;
+static const char kIceUfrag1[] = "TESTICEUFRAG0001";
+static const char kIcePwd1[] = "TESTICEPWD00000000000001";
+static const char kIceUfrag2[] = "TESTICEUFRAG0002";
+static const char kIcePwd2[] = "TESTICEPWD00000000000002";
+
+using cricket::Candidate;
+using cricket::Candidates;
+using cricket::FakeTransportChannel;
+using cricket::FakeTransportController;
+using cricket::IceConnectionState;
+using cricket::IceGatheringState;
+using cricket::TransportChannel;
+using cricket::TransportController;
+using cricket::TransportDescription;
+using cricket::TransportStats;
+
+// Only subclassing from FakeTransportController because currently that's the
+// only way to have a TransportController with fake TransportChannels.
+//
+// TODO(deadbeef): Change this once the Transport/TransportChannel class
+// heirarchy is cleaned up, and we can pass a "TransportChannelFactory" or
+// something similar into TransportController.
+typedef FakeTransportController TransportControllerForTest;
+
+class TransportControllerTest : public testing::Test,
+                                public sigslot::has_slots<> {
+ public:
+  TransportControllerTest()
+      : transport_controller_(new TransportControllerForTest()),
+        signaling_thread_(rtc::Thread::Current()) {
+    ConnectTransportControllerSignals();
+  }
+
+  void CreateTransportControllerWithWorkerThread() {
+    if (!worker_thread_) {
+      worker_thread_.reset(new rtc::Thread());
+      worker_thread_->Start();
+    }
+    transport_controller_.reset(
+        new TransportControllerForTest(worker_thread_.get()));
+    ConnectTransportControllerSignals();
+  }
+
+  void ConnectTransportControllerSignals() {
+    transport_controller_->SignalConnectionState.connect(
+        this, &TransportControllerTest::OnConnectionState);
+    transport_controller_->SignalReceiving.connect(
+        this, &TransportControllerTest::OnReceiving);
+    transport_controller_->SignalGatheringState.connect(
+        this, &TransportControllerTest::OnGatheringState);
+    transport_controller_->SignalCandidatesGathered.connect(
+        this, &TransportControllerTest::OnCandidatesGathered);
+  }
+
+  FakeTransportChannel* CreateChannel(const std::string& content,
+                                      int component) {
+    TransportChannel* channel =
+        transport_controller_->CreateTransportChannel_w(content, component);
+    return static_cast<FakeTransportChannel*>(channel);
+  }
+
+  void DestroyChannel(const std::string& content, int component) {
+    transport_controller_->DestroyTransportChannel_w(content, component);
+  }
+
+  Candidate CreateCandidate(int component) {
+    Candidate c;
+    c.set_address(rtc::SocketAddress("192.168.1.1", 8000));
+    c.set_component(1);
+    c.set_protocol(cricket::UDP_PROTOCOL_NAME);
+    c.set_priority(1);
+    return c;
+  }
+
+  // Used for thread hopping test.
+  void CreateChannelsAndCompleteConnectionOnWorkerThread() {
+    worker_thread_->Invoke<void>(rtc::Bind(
+        &TransportControllerTest::CreateChannelsAndCompleteConnection_w, this));
+  }
+
+  void CreateChannelsAndCompleteConnection_w() {
+    transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING);
+    FakeTransportChannel* channel1 = CreateChannel("audio", 1);
+    ASSERT_NE(nullptr, channel1);
+    FakeTransportChannel* channel2 = CreateChannel("video", 1);
+    ASSERT_NE(nullptr, channel2);
+
+    TransportDescription local_desc(
+        std::vector<std::string>(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL,
+        cricket::CONNECTIONROLE_ACTPASS, nullptr, Candidates());
+    std::string err;
+    transport_controller_->SetLocalTransportDescription(
+        "audio", local_desc, cricket::CA_OFFER, &err);
+    transport_controller_->SetLocalTransportDescription(
+        "video", local_desc, cricket::CA_OFFER, &err);
+    transport_controller_->MaybeStartGathering();
+    channel1->SignalCandidateGathered(channel1, CreateCandidate(1));
+    channel2->SignalCandidateGathered(channel2, CreateCandidate(1));
+    channel1->SetCandidatesGatheringComplete();
+    channel2->SetCandidatesGatheringComplete();
+    channel1->SetConnectionCount(2);
+    channel2->SetConnectionCount(2);
+    channel1->SetReceiving(true);
+    channel2->SetReceiving(true);
+    channel1->SetWritable(true);
+    channel2->SetWritable(true);
+    channel1->SetConnectionCount(1);
+    channel2->SetConnectionCount(1);
+  }
+
+ protected:
+  void OnConnectionState(IceConnectionState state) {
+    if (!signaling_thread_->IsCurrent()) {
+      signaled_on_non_signaling_thread_ = true;
+    }
+    connection_state_ = state;
+    ++connection_state_signal_count_;
+  }
+
+  void OnReceiving(bool receiving) {
+    if (!signaling_thread_->IsCurrent()) {
+      signaled_on_non_signaling_thread_ = true;
+    }
+    receiving_ = receiving;
+    ++receiving_signal_count_;
+  }
+
+  void OnGatheringState(IceGatheringState state) {
+    if (!signaling_thread_->IsCurrent()) {
+      signaled_on_non_signaling_thread_ = true;
+    }
+    gathering_state_ = state;
+    ++gathering_state_signal_count_;
+  }
+
+  void OnCandidatesGathered(const std::string& transport_name,
+                            const Candidates& candidates) {
+    if (!signaling_thread_->IsCurrent()) {
+      signaled_on_non_signaling_thread_ = true;
+    }
+    candidates_[transport_name].insert(candidates_[transport_name].end(),
+                                       candidates.begin(), candidates.end());
+    ++candidates_signal_count_;
+  }
+
+  rtc::scoped_ptr<rtc::Thread> worker_thread_;  // Not used for most tests.
+  rtc::scoped_ptr<TransportControllerForTest> transport_controller_;
+
+  // Information received from signals from transport controller.
+  IceConnectionState connection_state_ = cricket::kIceConnectionConnecting;
+  bool receiving_ = false;
+  IceGatheringState gathering_state_ = cricket::kIceGatheringNew;
+  // transport_name => candidates
+  std::map<std::string, Candidates> candidates_;
+  // Counts of each signal emitted.
+  int connection_state_signal_count_ = 0;
+  int receiving_signal_count_ = 0;
+  int gathering_state_signal_count_ = 0;
+  int candidates_signal_count_ = 0;
+
+  // Used to make sure signals only come on signaling thread.
+  rtc::Thread* const signaling_thread_ = nullptr;
+  bool signaled_on_non_signaling_thread_ = false;
+};
+
+TEST_F(TransportControllerTest, TestSetIceReceivingTimeout) {
+  FakeTransportChannel* channel1 = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel1);
+
+  transport_controller_->SetIceConnectionReceivingTimeout(1000);
+  EXPECT_EQ(1000, channel1->receiving_timeout());
+
+  // Test that value stored in controller is applied to new channels.
+  FakeTransportChannel* channel2 = CreateChannel("video", 1);
+  ASSERT_NE(nullptr, channel2);
+  EXPECT_EQ(1000, channel2->receiving_timeout());
+}
+
+TEST_F(TransportControllerTest, TestSetSslMaxProtocolVersion) {
+  EXPECT_TRUE(transport_controller_->SetSslMaxProtocolVersion(
+      rtc::SSL_PROTOCOL_DTLS_12));
+  FakeTransportChannel* channel = CreateChannel("audio", 1);
+
+  ASSERT_NE(nullptr, channel);
+  EXPECT_EQ(rtc::SSL_PROTOCOL_DTLS_12, channel->ssl_max_protocol_version());
+
+  // Setting max version after transport is created should fail.
+  EXPECT_FALSE(transport_controller_->SetSslMaxProtocolVersion(
+      rtc::SSL_PROTOCOL_DTLS_10));
+}
+
+TEST_F(TransportControllerTest, TestSetIceRole) {
+  FakeTransportChannel* channel1 = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel1);
+
+  transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING);
+  EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel1->GetIceRole());
+  transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLED);
+  EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel1->GetIceRole());
+
+  // Test that value stored in controller is applied to new channels.
+  FakeTransportChannel* channel2 = CreateChannel("video", 1);
+  ASSERT_NE(nullptr, channel2);
+  EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel2->GetIceRole());
+}
+
+// Test that when one channel encounters a role conflict, the ICE role is
+// swapped on every channel.
+TEST_F(TransportControllerTest, TestIceRoleConflict) {
+  FakeTransportChannel* channel1 = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel1);
+  FakeTransportChannel* channel2 = CreateChannel("video", 1);
+  ASSERT_NE(nullptr, channel2);
+
+  transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING);
+  EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel1->GetIceRole());
+  EXPECT_EQ(cricket::ICEROLE_CONTROLLING, channel2->GetIceRole());
+
+  channel1->SignalRoleConflict(channel1);
+  EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel1->GetIceRole());
+  EXPECT_EQ(cricket::ICEROLE_CONTROLLED, channel2->GetIceRole());
+}
+
+TEST_F(TransportControllerTest, TestGetSslRole) {
+  FakeTransportChannel* channel = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel);
+  ASSERT_TRUE(channel->SetSslRole(rtc::SSL_CLIENT));
+  rtc::SSLRole role;
+  EXPECT_TRUE(transport_controller_->GetSslRole(&role));
+  EXPECT_EQ(rtc::SSL_CLIENT, role);
+}
+
+TEST_F(TransportControllerTest, TestSetAndGetLocalCertificate) {
+  rtc::scoped_refptr<rtc::RTCCertificate> certificate1 =
+      rtc::RTCCertificate::Create(
+          rtc::scoped_ptr<rtc::SSLIdentity>(
+              rtc::SSLIdentity::Generate("session1", rtc::KT_DEFAULT))
+              .Pass());
+  rtc::scoped_refptr<rtc::RTCCertificate> certificate2 =
+      rtc::RTCCertificate::Create(
+          rtc::scoped_ptr<rtc::SSLIdentity>(
+              rtc::SSLIdentity::Generate("session2", rtc::KT_DEFAULT))
+              .Pass());
+  rtc::scoped_refptr<rtc::RTCCertificate> returned_certificate;
+
+  FakeTransportChannel* channel1 = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel1);
+
+  EXPECT_TRUE(transport_controller_->SetLocalCertificate(certificate1));
+  EXPECT_TRUE(transport_controller_->GetLocalCertificate(
+      "audio", &returned_certificate));
+  EXPECT_EQ(certificate1->identity()->certificate().ToPEMString(),
+            returned_certificate->identity()->certificate().ToPEMString());
+
+  // Should fail if called for a nonexistant transport.
+  EXPECT_FALSE(transport_controller_->GetLocalCertificate(
+      "video", &returned_certificate));
+
+  // Test that identity stored in controller is applied to new channels.
+  FakeTransportChannel* channel2 = CreateChannel("video", 1);
+  ASSERT_NE(nullptr, channel2);
+  EXPECT_TRUE(transport_controller_->GetLocalCertificate(
+      "video", &returned_certificate));
+  EXPECT_EQ(certificate1->identity()->certificate().ToPEMString(),
+            returned_certificate->identity()->certificate().ToPEMString());
+
+  // Shouldn't be able to change the identity once set.
+  EXPECT_FALSE(transport_controller_->SetLocalCertificate(certificate2));
+}
+
+TEST_F(TransportControllerTest, TestGetRemoteSSLCertificate) {
+  rtc::FakeSSLCertificate fake_certificate("fake_data");
+  rtc::scoped_ptr<rtc::SSLCertificate> returned_certificate;
+
+  FakeTransportChannel* channel = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel);
+
+  channel->SetRemoteSSLCertificate(&fake_certificate);
+  EXPECT_TRUE(transport_controller_->GetRemoteSSLCertificate(
+      "audio", returned_certificate.accept()));
+  EXPECT_EQ(fake_certificate.ToPEMString(),
+            returned_certificate->ToPEMString());
+
+  // Should fail if called for a nonexistant transport.
+  EXPECT_FALSE(transport_controller_->GetRemoteSSLCertificate(
+      "video", returned_certificate.accept()));
+}
+
+TEST_F(TransportControllerTest, TestSetLocalTransportDescription) {
+  FakeTransportChannel* channel = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel);
+  TransportDescription local_desc(
+      std::vector<std::string>(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL,
+      cricket::CONNECTIONROLE_ACTPASS, nullptr, Candidates());
+  std::string err;
+  EXPECT_TRUE(transport_controller_->SetLocalTransportDescription(
+      "audio", local_desc, cricket::CA_OFFER, &err));
+  // Check that ICE ufrag and pwd were propagated to channel.
+  EXPECT_EQ(kIceUfrag1, channel->ice_ufrag());
+  EXPECT_EQ(kIcePwd1, channel->ice_pwd());
+  // After setting local description, we should be able to start gathering
+  // candidates.
+  transport_controller_->MaybeStartGathering();
+  EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
+  EXPECT_EQ(1, gathering_state_signal_count_);
+}
+
+TEST_F(TransportControllerTest, TestSetRemoteTransportDescription) {
+  FakeTransportChannel* channel = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel);
+  TransportDescription remote_desc(
+      std::vector<std::string>(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL,
+      cricket::CONNECTIONROLE_ACTPASS, nullptr, Candidates());
+  std::string err;
+  EXPECT_TRUE(transport_controller_->SetRemoteTransportDescription(
+      "audio", remote_desc, cricket::CA_OFFER, &err));
+  // Check that ICE ufrag and pwd were propagated to channel.
+  EXPECT_EQ(kIceUfrag1, channel->remote_ice_ufrag());
+  EXPECT_EQ(kIcePwd1, channel->remote_ice_pwd());
+}
+
+TEST_F(TransportControllerTest, TestAddRemoteCandidates) {
+  FakeTransportChannel* channel = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel);
+  Candidates candidates;
+  candidates.push_back(CreateCandidate(1));
+  std::string err;
+  EXPECT_TRUE(
+      transport_controller_->AddRemoteCandidates("audio", candidates, &err));
+  EXPECT_EQ(1U, channel->remote_candidates().size());
+}
+
+TEST_F(TransportControllerTest, TestReadyForRemoteCandidates) {
+  FakeTransportChannel* channel = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel);
+  // We expect to be ready for remote candidates only after local and remote
+  // descriptions are set.
+  EXPECT_FALSE(transport_controller_->ReadyForRemoteCandidates("audio"));
+
+  std::string err;
+  TransportDescription remote_desc(
+      std::vector<std::string>(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL,
+      cricket::CONNECTIONROLE_ACTPASS, nullptr, Candidates());
+  EXPECT_TRUE(transport_controller_->SetRemoteTransportDescription(
+      "audio", remote_desc, cricket::CA_OFFER, &err));
+  EXPECT_FALSE(transport_controller_->ReadyForRemoteCandidates("audio"));
+
+  TransportDescription local_desc(
+      std::vector<std::string>(), kIceUfrag2, kIcePwd2, cricket::ICEMODE_FULL,
+      cricket::CONNECTIONROLE_ACTPASS, nullptr, Candidates());
+  EXPECT_TRUE(transport_controller_->SetLocalTransportDescription(
+      "audio", local_desc, cricket::CA_ANSWER, &err));
+  EXPECT_TRUE(transport_controller_->ReadyForRemoteCandidates("audio"));
+}
+
+TEST_F(TransportControllerTest, TestGetStats) {
+  FakeTransportChannel* channel1 = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel1);
+  FakeTransportChannel* channel2 = CreateChannel("audio", 2);
+  ASSERT_NE(nullptr, channel2);
+  FakeTransportChannel* channel3 = CreateChannel("video", 1);
+  ASSERT_NE(nullptr, channel3);
+
+  TransportStats stats;
+  EXPECT_TRUE(transport_controller_->GetStats("audio", &stats));
+  EXPECT_EQ("audio", stats.transport_name);
+  EXPECT_EQ(2U, stats.channel_stats.size());
+}
+
+// Test that transport gets destroyed when it has no more channels.
+TEST_F(TransportControllerTest, TestCreateAndDestroyChannel) {
+  FakeTransportChannel* channel1 = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel1);
+  FakeTransportChannel* channel2 = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel2);
+  ASSERT_EQ(channel1, channel2);
+  FakeTransportChannel* channel3 = CreateChannel("audio", 2);
+  ASSERT_NE(nullptr, channel3);
+
+  // Using GetStats to check if transport is destroyed from an outside class's
+  // perspective.
+  TransportStats stats;
+  EXPECT_TRUE(transport_controller_->GetStats("audio", &stats));
+  DestroyChannel("audio", 2);
+  DestroyChannel("audio", 1);
+  EXPECT_TRUE(transport_controller_->GetStats("audio", &stats));
+  DestroyChannel("audio", 1);
+  EXPECT_FALSE(transport_controller_->GetStats("audio", &stats));
+}
+
+TEST_F(TransportControllerTest, TestSignalConnectionStateFailed) {
+  // Need controlling ICE role to get in failed state.
+  transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING);
+  FakeTransportChannel* channel1 = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel1);
+  FakeTransportChannel* channel2 = CreateChannel("video", 1);
+  ASSERT_NE(nullptr, channel2);
+
+  // Should signal "failed" if any channel failed; channel is considered failed
+  // if it previously had a connection but now has none, and gathering is
+  // complete.
+  channel1->SetCandidatesGatheringComplete();
+  channel1->SetConnectionCount(1);
+  channel1->SetConnectionCount(0);
+  EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
+  EXPECT_EQ(1, connection_state_signal_count_);
+}
+
+TEST_F(TransportControllerTest, TestSignalConnectionStateConnected) {
+  transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING);
+  FakeTransportChannel* channel1 = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel1);
+  FakeTransportChannel* channel2 = CreateChannel("video", 1);
+  ASSERT_NE(nullptr, channel2);
+  FakeTransportChannel* channel3 = CreateChannel("video", 2);
+  ASSERT_NE(nullptr, channel3);
+
+  // First, have one channel connect, and another fail, to ensure that
+  // the first channel connecting didn't trigger a "connected" state signal.
+  // We should only get a signal when all are connected.
+  channel1->SetConnectionCount(2);
+  channel1->SetWritable(true);
+  channel3->SetCandidatesGatheringComplete();
+  channel3->SetConnectionCount(1);
+  channel3->SetConnectionCount(0);
+  EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
+  // Signal count of 1 means that the only signal emitted was "failed".
+  EXPECT_EQ(1, connection_state_signal_count_);
+
+  // Destroy the failed channel to return to "connecting" state.
+  DestroyChannel("video", 2);
+  EXPECT_EQ_WAIT(cricket::kIceConnectionConnecting, connection_state_,
+                 kTimeout);
+  EXPECT_EQ(2, connection_state_signal_count_);
+
+  // Make the remaining channel reach a connected state.
+  channel2->SetConnectionCount(2);
+  channel2->SetWritable(true);
+  EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
+  EXPECT_EQ(3, connection_state_signal_count_);
+}
+
+TEST_F(TransportControllerTest, TestSignalConnectionStateComplete) {
+  transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING);
+  FakeTransportChannel* channel1 = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel1);
+  FakeTransportChannel* channel2 = CreateChannel("video", 1);
+  ASSERT_NE(nullptr, channel2);
+  FakeTransportChannel* channel3 = CreateChannel("video", 2);
+  ASSERT_NE(nullptr, channel3);
+
+  // Similar to above test, but we're now reaching the completed state, which
+  // means only one connection per FakeTransportChannel.
+  channel1->SetCandidatesGatheringComplete();
+  channel1->SetConnectionCount(1);
+  channel1->SetWritable(true);
+  channel3->SetCandidatesGatheringComplete();
+  channel3->SetConnectionCount(1);
+  channel3->SetConnectionCount(0);
+  EXPECT_EQ_WAIT(cricket::kIceConnectionFailed, connection_state_, kTimeout);
+  // Signal count of 1 means that the only signal emitted was "failed".
+  EXPECT_EQ(1, connection_state_signal_count_);
+
+  // Destroy the failed channel to return to "connecting" state.
+  DestroyChannel("video", 2);
+  EXPECT_EQ_WAIT(cricket::kIceConnectionConnecting, connection_state_,
+                 kTimeout);
+  EXPECT_EQ(2, connection_state_signal_count_);
+
+  // Make the remaining channel reach a connected state.
+  channel2->SetCandidatesGatheringComplete();
+  channel2->SetConnectionCount(2);
+  channel2->SetWritable(true);
+  EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
+  EXPECT_EQ(3, connection_state_signal_count_);
+
+  // Finally, transition to completed state.
+  channel2->SetConnectionCount(1);
+  EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
+  EXPECT_EQ(4, connection_state_signal_count_);
+}
+
+// Make sure that if we're "connected" and remove a transport, we stay in the
+// "connected" state.
+TEST_F(TransportControllerTest, TestDestroyTransportAndStayConnected) {
+  FakeTransportChannel* channel1 = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel1);
+  FakeTransportChannel* channel2 = CreateChannel("video", 1);
+  ASSERT_NE(nullptr, channel2);
+
+  channel1->SetCandidatesGatheringComplete();
+  channel1->SetConnectionCount(2);
+  channel1->SetWritable(true);
+  channel2->SetCandidatesGatheringComplete();
+  channel2->SetConnectionCount(2);
+  channel2->SetWritable(true);
+  EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
+  EXPECT_EQ(1, connection_state_signal_count_);
+
+  // Destroy one channel, then "complete" the other one, so we reach
+  // a known state.
+  DestroyChannel("video", 1);
+  channel1->SetConnectionCount(1);
+  EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
+  // Signal count of 2 means the deletion didn't cause any unexpected signals
+  EXPECT_EQ(2, connection_state_signal_count_);
+}
+
+// If we destroy the last/only transport, we should simply transition to
+// "connecting".
+TEST_F(TransportControllerTest, TestDestroyLastTransportWhileConnected) {
+  FakeTransportChannel* channel = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel);
+
+  channel->SetCandidatesGatheringComplete();
+  channel->SetConnectionCount(2);
+  channel->SetWritable(true);
+  EXPECT_EQ_WAIT(cricket::kIceConnectionConnected, connection_state_, kTimeout);
+  EXPECT_EQ(1, connection_state_signal_count_);
+
+  DestroyChannel("audio", 1);
+  EXPECT_EQ_WAIT(cricket::kIceConnectionConnecting, connection_state_,
+                 kTimeout);
+  // Signal count of 2 means the deletion didn't cause any unexpected signals
+  EXPECT_EQ(2, connection_state_signal_count_);
+}
+
+TEST_F(TransportControllerTest, TestSignalReceiving) {
+  FakeTransportChannel* channel1 = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel1);
+  FakeTransportChannel* channel2 = CreateChannel("video", 1);
+  ASSERT_NE(nullptr, channel2);
+
+  // Should signal receiving as soon as any channel is receiving.
+  channel1->SetReceiving(true);
+  EXPECT_TRUE_WAIT(receiving_, kTimeout);
+  EXPECT_EQ(1, receiving_signal_count_);
+
+  channel2->SetReceiving(true);
+  channel1->SetReceiving(false);
+  channel2->SetReceiving(false);
+  EXPECT_TRUE_WAIT(!receiving_, kTimeout);
+  EXPECT_EQ(2, receiving_signal_count_);
+}
+
+TEST_F(TransportControllerTest, TestSignalGatheringStateGathering) {
+  FakeTransportChannel* channel = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel);
+  channel->Connect();
+  channel->MaybeStartGathering();
+  // Should be in the gathering state as soon as any transport starts gathering.
+  EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
+  EXPECT_EQ(1, gathering_state_signal_count_);
+}
+
+TEST_F(TransportControllerTest, TestSignalGatheringStateComplete) {
+  FakeTransportChannel* channel1 = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel1);
+  FakeTransportChannel* channel2 = CreateChannel("video", 1);
+  ASSERT_NE(nullptr, channel2);
+  FakeTransportChannel* channel3 = CreateChannel("data", 1);
+  ASSERT_NE(nullptr, channel3);
+
+  channel3->Connect();
+  channel3->MaybeStartGathering();
+  EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
+  EXPECT_EQ(1, gathering_state_signal_count_);
+
+  // Have one channel finish gathering, then destroy it, to make sure gathering
+  // completion wasn't signalled if only one transport finished gathering.
+  channel3->SetCandidatesGatheringComplete();
+  DestroyChannel("data", 1);
+  EXPECT_EQ_WAIT(cricket::kIceGatheringNew, gathering_state_, kTimeout);
+  EXPECT_EQ(2, gathering_state_signal_count_);
+
+  // Make remaining channels start and then finish gathering.
+  channel1->Connect();
+  channel1->MaybeStartGathering();
+  channel2->Connect();
+  channel2->MaybeStartGathering();
+  EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
+  EXPECT_EQ(3, gathering_state_signal_count_);
+
+  channel1->SetCandidatesGatheringComplete();
+  channel2->SetCandidatesGatheringComplete();
+  EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
+  EXPECT_EQ(4, gathering_state_signal_count_);
+}
+
+// Test that when the last transport that hasn't finished connecting and/or
+// gathering is destroyed, the aggregate state jumps to "completed". This can
+// happen if, for example, we have an audio and video transport, the audio
+// transport completes, then we start bundling video on the audio transport.
+TEST_F(TransportControllerTest,
+       TestSignalingWhenLastIncompleteTransportDestroyed) {
+  transport_controller_->SetIceRole(cricket::ICEROLE_CONTROLLING);
+  FakeTransportChannel* channel1 = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel1);
+  FakeTransportChannel* channel2 = CreateChannel("video", 1);
+  ASSERT_NE(nullptr, channel2);
+
+  channel1->SetCandidatesGatheringComplete();
+  EXPECT_EQ_WAIT(cricket::kIceGatheringGathering, gathering_state_, kTimeout);
+  EXPECT_EQ(1, gathering_state_signal_count_);
+
+  channel1->SetConnectionCount(1);
+  channel1->SetWritable(true);
+  DestroyChannel("video", 1);
+  EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
+  EXPECT_EQ(1, connection_state_signal_count_);
+  EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
+  EXPECT_EQ(2, gathering_state_signal_count_);
+}
+
+TEST_F(TransportControllerTest, TestSignalCandidatesGathered) {
+  FakeTransportChannel* channel = CreateChannel("audio", 1);
+  ASSERT_NE(nullptr, channel);
+
+  // Transport won't signal candidates until it has a local description.
+  TransportDescription local_desc(
+      std::vector<std::string>(), kIceUfrag1, kIcePwd1, cricket::ICEMODE_FULL,
+      cricket::CONNECTIONROLE_ACTPASS, nullptr, Candidates());
+  std::string err;
+  EXPECT_TRUE(transport_controller_->SetLocalTransportDescription(
+      "audio", local_desc, cricket::CA_OFFER, &err));
+  transport_controller_->MaybeStartGathering();
+
+  channel->SignalCandidateGathered(channel, CreateCandidate(1));
+  EXPECT_EQ_WAIT(1, candidates_signal_count_, kTimeout);
+  EXPECT_EQ(1U, candidates_["audio"].size());
+}
+
+TEST_F(TransportControllerTest, TestSignalingOccursOnSignalingThread) {
+  CreateTransportControllerWithWorkerThread();
+  CreateChannelsAndCompleteConnectionOnWorkerThread();
+
+  // connecting --> connected --> completed
+  EXPECT_EQ_WAIT(cricket::kIceConnectionCompleted, connection_state_, kTimeout);
+  EXPECT_EQ(2, connection_state_signal_count_);
+
+  EXPECT_TRUE_WAIT(receiving_, kTimeout);
+  EXPECT_EQ(1, receiving_signal_count_);
+
+  // new --> gathering --> complete
+  EXPECT_EQ_WAIT(cricket::kIceGatheringComplete, gathering_state_, kTimeout);
+  EXPECT_EQ(2, gathering_state_signal_count_);
+
+  EXPECT_EQ_WAIT(1U, candidates_["audio"].size(), kTimeout);
+  EXPECT_EQ_WAIT(1U, candidates_["video"].size(), kTimeout);
+  EXPECT_EQ(2, candidates_signal_count_);
+
+  EXPECT_TRUE(!signaled_on_non_signaling_thread_);
+}
diff --git a/webrtc/p2p/base/transportdescriptionfactory.cc b/webrtc/p2p/base/transportdescriptionfactory.cc
index 4c701df..1ddf55d 100644
--- a/webrtc/p2p/base/transportdescriptionfactory.cc
+++ b/webrtc/p2p/base/transportdescriptionfactory.cc
@@ -14,7 +14,6 @@
 #include "webrtc/base/helpers.h"
 #include "webrtc/base/logging.h"
 #include "webrtc/base/messagedigest.h"
-#include "webrtc/base/scoped_ptr.h"
 #include "webrtc/base/sslfingerprint.h"
 
 namespace cricket {
diff --git a/webrtc/p2p/p2p.gyp b/webrtc/p2p/p2p.gyp
index 45c9e13..b5409f4 100644
--- a/webrtc/p2p/p2p.gyp
+++ b/webrtc/p2p/p2p.gyp
@@ -66,8 +66,8 @@
         'base/transportchannel.cc',
         'base/transportchannel.h',
         'base/transportchannelimpl.h',
-        'base/transportchannelproxy.cc',
-        'base/transportchannelproxy.h',
+        'base/transportcontroller.cc',
+        'base/transportcontroller.h',
         'base/transportdescription.cc',
         'base/transportdescription.h',
         'base/transportdescriptionfactory.cc',
diff --git a/webrtc/p2p/p2p_tests.gypi b/webrtc/p2p/p2p_tests.gypi
index 8c2c2b3..ba7f553 100644
--- a/webrtc/p2p/p2p_tests.gypi
+++ b/webrtc/p2p/p2p_tests.gypi
@@ -15,13 +15,12 @@
       'direct_dependent_settings': {
         'sources': [
           'base/dtlstransportchannel_unittest.cc',
-          'base/fakesession.h',
+          'base/faketransportcontroller.h',
           'base/p2ptransportchannel_unittest.cc',
           'base/port_unittest.cc',
           'base/pseudotcp_unittest.cc',
           'base/relayport_unittest.cc',
           'base/relayserver_unittest.cc',
-          'base/session_unittest.cc',
           'base/stun_unittest.cc',
           'base/stunport_unittest.cc',
           'base/stunrequest_unittest.cc',
@@ -30,6 +29,7 @@
           'base/teststunserver.h',
           'base/testturnserver.h',
           'base/transport_unittest.cc',
+          'base/transportcontroller_unittest.cc',
           'base/transportdescriptionfactory_unittest.cc',
           'base/turnport_unittest.cc',
           'client/fakeportallocator.h',